From 7cc123e0a442d41a3610a57a2ee4f8008ef5811f Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 29 Nov 2023 09:09:36 +0100 Subject: [PATCH 001/175] chore: from_values takes ref --- evm/src/keccak/keccak_stark.rs | 6 +----- evm/src/prover.rs | 11 ++--------- plonky2/src/fri/oracle.rs | 8 ++++++-- plonky2/src/plonk/circuit_builder.rs | 2 +- plonky2/src/plonk/prover.rs | 4 ++-- starky/src/prover.rs | 6 ++---- 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 9870b90643..019b8083fb 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -722,15 +722,11 @@ mod tests { stark.generate_trace(input, 8, &mut timing) ); - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. - let cloned_trace_poly_values = timed!(timing, "clone", trace_poly_values.clone()); - let trace_commitments = timed!( timing, "compute trace commitment", PolynomialBatch::::from_values( - cloned_trace_poly_values, + &trace_poly_values, config.fri_config.rate_bits, false, config.fri_config.cap_height, diff --git a/evm/src/prover.rs b/evm/src/prover.rs index fe8ad51781..77985a234e 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -102,14 +102,7 @@ where timing, &format!("compute trace commitment for {:?}", table), PolynomialBatch::::from_values( - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. - trace.clone(), - rate_bits, - false, - cap_height, - timing, - None, + trace, rate_bits, false, cap_height, timing, None, ) ) }) @@ -380,7 +373,7 @@ where timing, "compute auxiliary polynomials commitment", PolynomialBatch::from_values( - auxiliary_polys, + &auxiliary_polys, rate_bits, false, config.fri_config.cap_height, diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index 8642a6c566..bb065b7c04 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -55,7 +55,7 @@ impl, C: GenericConfig, const D: usize> { /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. pub fn from_values( - values: Vec>, + values: &[PolynomialValues], rate_bits: usize, blinding: bool, cap_height: usize, @@ -65,7 +65,11 @@ impl, C: GenericConfig, const D: usize> let coeffs = timed!( timing, "IFFT", - values.into_par_iter().map(|v| v.ifft()).collect::>() + values + .into_par_iter() + .cloned() + .map(|v| v.ifft()) + .collect::>() ); Self::from_coeffs( diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 67db68649a..279adac96a 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1029,7 +1029,7 @@ impl, const D: usize> CircuitBuilder { let constants_sigmas_commitment = if commit_to_sigma { let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat(); PolynomialBatch::::from_values( - constants_sigmas_vecs, + &constants_sigmas_vecs, rate_bits, PlonkOracle::CONSTANTS_SIGMAS.blinding, cap_height, diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 41aebdb1e9..208d645046 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -171,7 +171,7 @@ where timing, "compute wires commitment", PolynomialBatch::::from_values( - wires_values, + &wires_values, config.fri_config.rate_bits, config.zero_knowledge && PlonkOracle::WIRES.blinding, config.fri_config.cap_height, @@ -238,7 +238,7 @@ where timing, "commit to partial products, Z's and, if any, lookup polynomials", PolynomialBatch::from_values( - zs_partial_products_lookups, + &zs_partial_products_lookups, config.fri_config.rate_bits, config.zero_knowledge && PlonkOracle::ZS_PARTIAL_PRODUCTS.blinding, config.fri_config.cap_height, diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 23808e0f4e..066319c8c4 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -55,9 +55,7 @@ where timing, "compute trace commitment", PolynomialBatch::::from_values( - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. - trace_poly_values.clone(), + &trace_poly_values, rate_bits, false, cap_height, @@ -88,7 +86,7 @@ where timing, "compute permutation Z commitments", PolynomialBatch::from_values( - permutation_z_polys, + &permutation_z_polys, rate_bits, false, config.fri_config.cap_height, From 37918cccfd148814605d936155ef58714c81dc91 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 30 Nov 2023 15:07:10 +0100 Subject: [PATCH 002/175] Revert "chore: from_values takes ref" This reverts commit 7cc123e0a442d41a3610a57a2ee4f8008ef5811f. --- evm/src/keccak/keccak_stark.rs | 6 +++++- evm/src/prover.rs | 11 +++++++++-- plonky2/src/fri/oracle.rs | 8 ++------ plonky2/src/plonk/circuit_builder.rs | 2 +- plonky2/src/plonk/prover.rs | 4 ++-- starky/src/prover.rs | 6 ++++-- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 019b8083fb..9870b90643 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -722,11 +722,15 @@ mod tests { stark.generate_trace(input, 8, &mut timing) ); + // TODO: Cloning this isn't great; consider having `from_values` accept a reference, + // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. + let cloned_trace_poly_values = timed!(timing, "clone", trace_poly_values.clone()); + let trace_commitments = timed!( timing, "compute trace commitment", PolynomialBatch::::from_values( - &trace_poly_values, + cloned_trace_poly_values, config.fri_config.rate_bits, false, config.fri_config.cap_height, diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 77985a234e..fe8ad51781 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -102,7 +102,14 @@ where timing, &format!("compute trace commitment for {:?}", table), PolynomialBatch::::from_values( - trace, rate_bits, false, cap_height, timing, None, + // TODO: Cloning this isn't great; consider having `from_values` accept a reference, + // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. + trace.clone(), + rate_bits, + false, + cap_height, + timing, + None, ) ) }) @@ -373,7 +380,7 @@ where timing, "compute auxiliary polynomials commitment", PolynomialBatch::from_values( - &auxiliary_polys, + auxiliary_polys, rate_bits, false, config.fri_config.cap_height, diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index bb065b7c04..8642a6c566 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -55,7 +55,7 @@ impl, C: GenericConfig, const D: usize> { /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. pub fn from_values( - values: &[PolynomialValues], + values: Vec>, rate_bits: usize, blinding: bool, cap_height: usize, @@ -65,11 +65,7 @@ impl, C: GenericConfig, const D: usize> let coeffs = timed!( timing, "IFFT", - values - .into_par_iter() - .cloned() - .map(|v| v.ifft()) - .collect::>() + values.into_par_iter().map(|v| v.ifft()).collect::>() ); Self::from_coeffs( diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 279adac96a..67db68649a 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1029,7 +1029,7 @@ impl, const D: usize> CircuitBuilder { let constants_sigmas_commitment = if commit_to_sigma { let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat(); PolynomialBatch::::from_values( - &constants_sigmas_vecs, + constants_sigmas_vecs, rate_bits, PlonkOracle::CONSTANTS_SIGMAS.blinding, cap_height, diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 208d645046..41aebdb1e9 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -171,7 +171,7 @@ where timing, "compute wires commitment", PolynomialBatch::::from_values( - &wires_values, + wires_values, config.fri_config.rate_bits, config.zero_knowledge && PlonkOracle::WIRES.blinding, config.fri_config.cap_height, @@ -238,7 +238,7 @@ where timing, "commit to partial products, Z's and, if any, lookup polynomials", PolynomialBatch::from_values( - &zs_partial_products_lookups, + zs_partial_products_lookups, config.fri_config.rate_bits, config.zero_knowledge && PlonkOracle::ZS_PARTIAL_PRODUCTS.blinding, config.fri_config.cap_height, diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 066319c8c4..23808e0f4e 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -55,7 +55,9 @@ where timing, "compute trace commitment", PolynomialBatch::::from_values( - &trace_poly_values, + // TODO: Cloning this isn't great; consider having `from_values` accept a reference, + // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. + trace_poly_values.clone(), rate_bits, false, cap_height, @@ -86,7 +88,7 @@ where timing, "compute permutation Z commitments", PolynomialBatch::from_values( - &permutation_z_polys, + permutation_z_polys, rate_bits, false, config.fri_config.cap_height, From e68195fc0d4613f32e3874ca198a8be6acbb6ec0 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 30 Nov 2023 15:10:34 +0100 Subject: [PATCH 003/175] chore: Remove TODOs about `from_values` taking a reference --- evm/src/keccak/keccak_stark.rs | 2 -- evm/src/prover.rs | 2 -- starky/src/prover.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 9870b90643..e81d2f8a00 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -722,8 +722,6 @@ mod tests { stark.generate_trace(input, 8, &mut timing) ); - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. let cloned_trace_poly_values = timed!(timing, "clone", trace_poly_values.clone()); let trace_commitments = timed!( diff --git a/evm/src/prover.rs b/evm/src/prover.rs index fe8ad51781..c61361d638 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -102,8 +102,6 @@ where timing, &format!("compute trace commitment for {:?}", table), PolynomialBatch::::from_values( - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. trace.clone(), rate_bits, false, diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 23808e0f4e..866bb6357a 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -55,8 +55,6 @@ where timing, "compute trace commitment", PolynomialBatch::::from_values( - // TODO: Cloning this isn't great; consider having `from_values` accept a reference, - // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. trace_poly_values.clone(), rate_bits, false, From a90aa40b7a01d5141ed41977c91a4f278ede2eb4 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:08:47 -0500 Subject: [PATCH 004/175] Implement MPT preinitialization (#1406) * Implement MPT preinitialization * Apply comments * Replace GlobalMetadata reads with stores in the kernel * Change memory specs * Remove trie data length as a prover input --- evm/spec/tables/memory.tex | 1 + evm/spec/zkevm.pdf | Bin 294878 -> 292595 bytes evm/src/cpu/kernel/aggregator.rs | 2 - evm/src/cpu/kernel/asm/main.asm | 26 +- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 234 +++++++++------- .../asm/mpt/hash/hash_trie_specific.asm | 239 +++++++++------- evm/src/cpu/kernel/asm/mpt/load/load.asm | 173 ------------ .../asm/mpt/load/load_trie_specific.asm | 150 ---------- evm/src/cpu/kernel/interpreter.rs | 6 + evm/src/cpu/kernel/tests/account_code.rs | 99 +++++-- evm/src/cpu/kernel/tests/add11.rs | 12 +- evm/src/cpu/kernel/tests/balance.rs | 20 +- evm/src/cpu/kernel/tests/mpt/delete.rs | 14 +- evm/src/cpu/kernel/tests/mpt/hash.rs | 19 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 20 +- evm/src/cpu/kernel/tests/mpt/load.rs | 71 ++--- evm/src/cpu/kernel/tests/mpt/read.rs | 12 +- evm/src/cpu/kernel/tests/receipt.rs | 16 +- evm/src/generation/mod.rs | 7 +- evm/src/generation/mpt.rs | 256 ++++++++++-------- evm/src/generation/prover_input.rs | 19 +- evm/src/generation/state.rs | 40 ++- evm/src/memory/memory_stark.rs | 15 +- evm/src/proof.rs | 3 +- evm/tests/log_opcode.rs | 2 +- 25 files changed, 648 insertions(+), 808 deletions(-) delete mode 100644 evm/src/cpu/kernel/asm/mpt/load/load.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/load/load_trie_specific.asm diff --git a/evm/spec/tables/memory.tex b/evm/spec/tables/memory.tex index f5cf4b02c1..e3a19cab8e 100644 --- a/evm/spec/tables/memory.tex +++ b/evm/spec/tables/memory.tex @@ -76,4 +76,5 @@ \subsubsection{Memory initialization} \begin{itemize} \item The read-only kernel code (in segment 0, context 0) is initialized with its correct values. It's checked by hashing the segment and verifying that the hash value matches a verifier-provided one. + \item The ``TrieData'' segment is initialized with the input tries. The stored tries are hashed and checked against the provided initial hash. Note that the length of the segment and the pointers -- within the ``TrieData'' segment -- for the three tries are provided as prover inputs. The length is then checked against a value computed when hashing the tries. \end{itemize} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index c6a444447d75c7d1a0bb57711db03d3e3696e72d..0a409abd39bd363d8f4d95789aeaef2808374751 100644 GIT binary patch delta 107877 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 z zJRR&Ucd0`K&uqFHT0)^VQ_M_SYC54-X3r9wg)5Pk?4NF=h!%Wlb8?XeYX1yrD_$sX0JWSx0}^*f$A>$^*z~F;Lda;UzI*7UJu136V)sYH zl#vw5a9uQZE524oh)iTWA(x#^#Get=sLFRF^Sgqg-M-zD05~@@RdwN;>c&lk4IIdt zQbeXr&w#ypY6R=^GrKO22g;dp{Lj7}utdWFABt&EUN9ED4g-A;l>$Xfpk9<4E&h%v zBudMzjqK-XVOaJH>_SJE6a$ejPFsmH%SrLgsE-C8@ob(9>)W%_c`bBHDu2(10J4bfY;vu%rQmOgMMJG*WsCLxd?x7c6?_3hVDftm1?x0E zys#o<|D};>^5r-9^wu=ekvkK=v*kMwC}aS&gn`CWppw*8fo4gm!{iFK-HzV4t!m~< z5*D~SUIC~HK4?PxYyh3ryBaDN_JAFo1B4^J*yR!z0v5u_YS#2Fy^Fl`Q<2i=FLw7>>glzAVNi zviQoO--45acXRL~)4MTjo%oC3pA>Dz5s|Uln(jkoZa^VY#8)`ut5VDfN}?DVT5EHk z;VFl{f03~+b=U=z4yc5yuZ9>Lg~R~wRubM2VQ@b%?9R(!!Pa2mrwSrsHQ!kh+&5!8 zIEm&!+G<5B8kt==U$7(?bWNna9^DEwk)SBmuBrct@%mR3G}={!mz2HPRciWzEot#K z$B3KF2z*>5`OgUjE!=um0lbeHxIGyK-4XNIrPmE<6&p1K0~$4Tq*}_Ts=_aW&+ZSy zNsT?W|4J_1!a>nf%UnUrfFCDQKWLF#H#gRU5Dcs=@=>_LH<(7ZHa}k`Cl{*!6#^uX z>TbWhnw$HO+;?v?O+{^e(FL!Z$gND^C6XY1&&5kNkA-v`Gt{@S)4P4`@AAeut0GX! zsoqNSEpDXEDhQ=v=9GEMO6@$0UC<&b-#J*|xK_xyEXUHNEEQ0D1FS06zF#VcF1Xli z>Fa0m@u;nTc%07asGpwl!ec?L(V-{SGCh&SdDJ0DU4W>e*!EHp6UE4}OUF~~@!!*7 z5SWFMS)^NN8-Cp@u4_|0nXw@)i_z@V)PSs{$loQD!lySVwprm@V_=83=^iTI9&JoM z3F<*+cjzf^b#=Ym02dy0AT3YpS(1SF2Nw3m?`7tSroxYOjuTaGWqu=gNWhc$K%IeR z9h+I}rlR}vnoJRQdk;r9fe*8s5L^KOX9r*u0n+;VYN7eNU!vZA8(RrC;b^R`Q%nmFJg@&$#*}v)2g_Dn4CltY zggKTiD5`ucI$vq@EL}!B2rW#^)9EtFnBk3-&V&4jQ1GnmRP8-*R`Q}ZHQd@z=g0Ga z!(O|6BIk0m3ZAE|cqMFUQtvO^+ePh}plZu6<#Bw{x<$LZ!4%!gJ5sj{9{CZu1aEO_ zGh-j78K(gc>|eE2(($J3NfmP#Jnsq+bHYd_cK=9>_L|Z0v3&kLLnf++gnUrzEsl4h z$D>){@y7IXw|*bW!eUdRfV)#KUtnX&hi`;`+Q09vhOvgOEk$9!VTOHHnen}8184Z} zn;Nu|1W0iJ+O?a#l$UXtBO{AC@3~UU-AS`8*W@o*n0})A4G~ab^X4TFPy=X)3+X?Q z|6TBM|BIr=0AprOs%@Z2jRyV|Jj|xT27t1Frl)=n0A&DV_QfzX<={KMQ4M?=w7CR) z7{*FSC)Lw0C}hv$#>KAGy?ltkYg{tz-6~F$-Pvhb0B#OMf zi{4yk&M6@(;F{L5zE^X2TX4^8^ZOl@zF9k!&bI-tOOKv2Q0_RKi_IEix3rvb==g#d zBcs>ae0c}7B9{*a@Xl1?o2Uloa?J65^I;6{46g#`sc|6&Gl)hPn_J~v+sPSHztVOW z*Iz@T!o>`n&SbtZ@laR2P?|7)(yz_d=aqe#A7BbEtG6*yrv3b>OJpU-`zxAjFpp3I z{=&aDM0=%edbJPI+H{vIb5tM{)Z$5Wfs@^>ILHB*Lt;OUENecK%{rcA#K;+B$a3qh z&bY`L{N8TUE;&yd%+f@sG-v3Yk7cvmeQA4jbm7CO<^Y|+ziqrK+WF@sPuPA9G|tND z1k`qRdoBM4e~KHwgKW|?)3?J-ynYaU8Cl|X_8>676bcDCOY%w#>fPc+8_rOb#LPOtB&sgi3D*xI+9%# zlrT99{Z}oYbpF9l9;*PbaKibr&fIj(lOk3ZXto-|9 z?v;Ph-_=La1@5J8?j(Qr2C+JJ`TiAqz2ja0GlSmW@r6NNCP!$t%cfbswm^D?(09`$ z9_0evmH6*1kD=Ef4S^oA7Jzry_i)|L=IOU zP=*~U;)!tD-?aDWV()(_V)ZCOczK87XEc+Nyo*1d5EY>|+mfQS2&6-_n*(n^$4BU7 zLQ>a9k9CMF%fSeR$om=Ze@F2AtMv@;OYq+JE_{bAl<`Fi*FGwntNM8KwxAOjlr&7k z-!$%RD-4xi``dH!HDw$mz#CId(!9Ld<0X)E!;p=b&;QFlO^^?W)f0uz;V%W7QGTHX zDF_mH69ul`DQn3@n+aqhLG&u$pJv(gAACjpIRw?IC1zVQjP6pVY3vGw@$)7({<`Qis3(RqVotL#Ez8caZ}6eu}K`R&5=Z zL+Q#hIG-Eid!b!QQDg76EOS1P#V`+9naYv|Xdv8gG=2og!!^zFHq7=&LrcWQV_L-A z?>8nylrqH5%q>w-7lw_()+LeL&tQ5~;wD%}D?%~LRBn|IjFwX5%7cI2Wz?4#(K5IpbU|L})a;Bz>P|0)!iZ8lRD-9UBh&w=x| ze)apCfDO5g^%=z8MVf0ID+n)Bm-ZXO!~<5^)%NYiiD3?(H+0quk7wMySr7q|Q8eDT zk3>>@xNfcLRv3W*ZOe6vjN4RJrlyfi;1Qkz!Gk>ma+CuK{0-W5`F)Plif{u^c!6Es(HPF-e z#37b{;Dncbr#bX+=5#dpPIdO&3|Y8y6BDJqU9RE0_y*@SA2sbWxN~Dz&qEJQ#}3hM z@p$ud#|2S)ex?5savx)QFBQX_%FsrtcXn77+7IIaL1+_L9g2ks>#L#`Ehl79En(x$ zz46G?a7-K6_S~W!el!@ zf%_z7|7Qr%e*j)2%oP9ou;fW%!9j;$;bi%5OBq}33>%j^vl6p9vnn$SXdZ@Wl?bCL z{O`0tuy`1gmmoQ+ zB{3ASoWwFz6aG0uO>mDwFgMz{UVMM69)BxXD@Z~}p?2!b3^>L=hl6kvoygLsQnP0^%&qM$B(lS!(@R#90`$Aa*bnIRAnrpRK2^uP>dgX7kGQ zEw9J=rxl-Ei`;Y$xqIGMy}Bl5n|L&Y$nD*|Lrmxqo72qO8!%5V$QCcJ7pJr|gdP}} zV+Z{3QJ8%w=>FfkUxV?02RJCIlpN?!Av6wSrGSj@5Is~N+vr$F=%_~rkk@y&kstCQ ztr6fD0fa`n0hC-qh)|HdPHFqbjGOjGKF|=(x!pvd6`Zyp0xT@3Q(KoH+7|J6%TO?o zmSCtz#1{LpoI)@{MCRa7BpqLvK@Sn&U^}E!{d*@Tqlm6yF8%AkdC@XAXpT_`xDYY~ zbgNf@Eoiq4lqG1kFd(xxWDJJG9k2s@yEk@+QT&Yu697SMgolC=>*^G~8ng~FvH|*9 zh6`ecjdIUaH)IH*(35)z^4#+B3Hvnsrb*0v-^?~M1a){07UCV8_XzTx83JNPV|H7@ zlY|>osN{l)6a@?jC;12@6k>+3j3IwlAb?RyTz~{g!2B#lw%>zt7w&1@F&e4H_3{rD zZqi1zrbBjdhZ070%rbnT3D^+g9O?48d*{)kqMkrMeR~>1LTGG!QxB_bhmRw{I6p;F zQGf0i7UBNtJr{-vK&eivsX-wE*@px1TH(HaU+w8xgS-dUp7A6Wx3A8?TtJse#z9_! zFN8*r@eR3Wz#)WMT7%#HzSgd?mWPKKqovC zZuh{iKfOQhe#EM!F(AO*d?J55q25`Z(2R|qI=<7q5EW$MPrz@F4>Cb*qoE%{Ufcm8C?uxeuz%^^g&Cs|JUAHGV3uMs*!-#`37;5(^@{3An0>bcBe6K%dZQJwGivG%i!qeK0daK! z_ueC(#%)Cdd%h(pp2gaK(47Dww5LW0bOj~0xrbivU3c^+r~nZRfIfk|3kf(!>6QcX zqyUuIa|oQv5I%x%z)`@!7$lBk=As*pkX<%v30zLHP9@I}#$hSIOfUu$1Z{2m)y* z#(Pkd5(YuW`0@275-u8W{Y8t=vlldh37AK9Ovk&CB|pR=*O_=wKd0V)**;Z&uC=tc zlIZ&*!`(W58hmxV2oW>IBTnt&Pe_o?-^g38saMj+P%Zsyacz8vErFQpk|Ke)dzUqY zMx3BOu+rqATcaIIaLo>_yDsGs#C~am96M5Lg z-6?ZKqg5f|cpYoID zK$%a!rvTPdJu)&(@*GO3879oR;V@dZiYf-$gXR>sH<`sUB4W@2or&m+K;TSk^zt{R z56?t6;yiQGzslls`5t;6L|zl49~bIGEq(jStoQHZ?Y&JqIX}^H_D~sCT$O^PeHK68 zItQA|M6ii|MlWNAqPm^l)hNg|J$F2(gLJ<0xn?gSnMD+{n!qO^+(>{yKthS#LE(O< z{-(41XVw<6V_kIxB#Mz_i+Y6KKVZSvV z2fit;gvvNyxCoh(ETrWIUk;hI&j!IuV~M4GVTbRsr?IIrdy|l_ut3B^P2Dqys3mG) zK8-<%_@}9$_OCo`L8XF=cY+k%=@R|97Y@eUm7~Mt(<+pMmd~0sgGwyLVT&sr|W30ZPS6G^!PC zi-t4SXKcv~`@gTb88OKlvAq;vF0uPXSl#8D9K`~5cwC=NMx?7{Tv7dG_ExmIpsSKDY56R z=3&5Y{sEyz#Y{M%dwmTGQakJz{^Uo$6OOM*!Z#Qr6TRr!vQtlEG;`mNRf^%su^))NP=Tz zywj4!wU@HE)R^AVe#f{WIh$2DBrhbswmqr|xQuS5na(+*S;qL4X*F0_MN0p=vVOGU zN9!LvJ!DQP^|`fyy}dPJs;?dXhTi-up?-{N1K>c8h_iM$@@03aTs-}q4xRMKt{5@L z*tSb&37{{_9k8m~%uQOToypJuTAir*!`vqWRuJL4+P9|%`NW)Qy2N_6cKxlN((t5^ z;mLPYx}ja^>3GhiWIisRT>2kTUXK3Xv0m7&7GiY915eYfzq#P?{@v%LZv06n3$yUZ z|1d%nq_CQZ5$=J>>Jk=EIEx8$BuS9A8>Jp;M+M9yr=LHQYE@a9%iiNar*b(AV|PvP z1vf(JPOx>E#lHBuTPu*mNylt?!#kKGAzmCxFE7^^jpuVczYC!J^ayJ+@M_@J?p7e2 zubNgOM*X6A2ycgs^v8&;n6dg(lSTg5IS<;EXU+({oR+Q2K0k)U_Ovc~APd?wnyzPy z%NZyGq$mN(n7zS%dgjm0HCDXs7I;@nuX^Ldxe5oK91U&ev08BS_cq@ra^!$nzV51r z3_i~@m!iM3|9VrTsA}lP-`}I|>8Th`cSDfpEL7gbnC3ffO`96y>N?oLRcnZjO%td-Qrj9>c9s)J`Lmg#*BmkjfhNER&pJ-k zx8u5KB&DeC$%P70(*^yKSP%|bo+#kNCJPuap^~5IgM+NPlhT@;BbvX0=eQQ90@|jq zP3M|qCI}jN{_evZ8%f1+8M>> z`Fd^8FMd5RnBeFXT`XFGh5v-41VjwfR*0IRr}8SPS@s~>Q3Q`rmM6A_(}|NWkFe^> zX27oW(7N}ZDdHpthViG-4UXnI6_srpBHpgeQ5mG`0U)RNG$_TFs1_ycQ55nQW>*Q> zR?S(STSNbE`tvwl-vE!0^jwS05lv#*gc;1FzMW>(@wNq9_BS^Moz@04fE2C5UeZ+V zwEi^3!HfDuKfO1#NY9acT?}1mVpV!2QwQ5$?x4S3Wvk(st@7_1`+PNz%nqS`H!*ha z18?yMQYJJ`9-^F`a02D~iVyz8h-|wf_Po+x8*;=;a?03S zo;B2qtGhG$#JZ1@W4m}vy!kcDeZHFPE`Q4IE2x7>VK2z|~IM`7b5yW$IaMN0X#C1xQ#XkR|fXO!QPa^1;mZuhyIT7vLMA)*e-42gPlc;o6k9%j_9#ac3snG}jC zfu=HHn{Zw%*+sgP0R%bcU)#SQC~OX5ddxHR66NczD%CBv;8~Tgg!MVCO>VS(y*X)R z+XuoB`PqGHC%91-)neF^efqQfq8d` z36J7}WvDS;`M7K2m+^g0xkCKcL7bJ~XfBKP$QB#F$f`eYCywE^wuzza@$P%G09F0o zjPr8)2$b`pv%BmokH$4T>L@Z~)DIwYP@Y$z+)B<6@RY*o=^QUmlM!W`=j+o=3!53& zc8HKTF!wx$?Q{G6=X78R))#g)!^NG4UnMGlV`P5Ot`PF0fo~-#8wrJaWrkwy;#UD1 zFEHgv`bTS7BxjpC;;v5*SLudgY3a^Ch9WS&$1Gts;X}@%)2K@CFtzd=d}S7H_rZOM z3!RD#fQVac(l#+c@__Qxr)B$`D28QN&D?%uvLpnUGj5xHnV98^TG6u&{%K;QW6?^iD_e`r z+1(_2On7v*N5~;ijDAw~&S$7#jf!Hb^S!nOHXFz9ty#)>i;w(#Da=F!kF(%jdFk1Z zNQSvSN15%&t8WizHSdGb2W@vhs5UiubS^!6FeWSs&b=c7#%9?3{Ve|4j|q@bfs`YGsZs`RjSVH3vt6oL|pHapp^CauWE zx@5fAr7H`mor1^YST~*wE)>ye2)V*}INw5SOeAoM#;U%{MqSYxOH)66nhJUCA{;F7 zmF|vX99$RL2dtJLJ2At&3J|dAd^JwM=Xw{Dk1L~k!Vr45X^uSpVy)|5U5*rw@zihU zTZ2?%j`YB!lzBtfo>0t4>$Q`KtFs9^_Ifpms9^oxmp7-xYEcJQcZg?O&>qmI|HJ_Y|XYcxwoWba5%5jMO%*Rfy<{h=$l!4xi5 zV~`wJRxnQ6kVCM8d8=oidSnabv}KMDb;3s#i%YBn z>5;WAo#__A{li%W^>N8l&&+BuG5O_hR_IX0;)R5?^?LRrg0ePF#-i0G?L95ywZ6Z^ z)kZY9##eol^yi<2RO=y&YXvZ-8@Ga6OU<8E{4eSQuw;^kR6RS317q6(2U4DnO0)b& z8LBHtXia-{?ZBO=d)^|4Fil>B%G14_9ehl?xjzKNsAyK3aEtTs^fe5K@+lI*A)V-d zw76KxOKCMl5C+O#*AhY&=vSti&dF0EJw+eJ-BdAkY`XS`Vl)x4z`e2lsA{V{ zXK^!qOO_EVETyK;!Bv(8+Otf&6^po%PAgbxq?@3g5bnptjr8aZFsRF!j=ARrm#mq6 z!mD^~Td}`U$GMpZUDos3g?9#j{Geo@IFy{>rxV_9t|Q&b!j)aSc$Ji(1Ndzbf{Z4L z0xXIG#qf0A72HF6*wWfFehS%x>=^Wy4!;^4c?G=I)jGr;PLu@z_rAWi>4&8+jV&z6 z5HtzH1tH9R(QlM^XiuUCgcQb-$!YNeE@u_xGEzr#cpb42j)I=>RDwe7%W#{~&)<#Yix?D`Lw#h={mPv`H> zB*1Z=1j?zuRGfOS?}ji2Sz z5rCnkr{M>WVf4_$Uk3%28g5L>Xl{I|?ra~Hwu^WwIAe5ueRJ8q@KI*DTx(5SB(SkY z=D~{v8B{>BS|`cdl`{Xi+$vokJ5N6UiLa|}C8-CrK_}G32jn@F@Y~PDU5zkZ*=pRE zis*{bj*LDJm6p_05V#E#hEeQKqS~=|A%SX7qxUCr-RX?y<1*_Gn?3V&ff$Ei*dLnW zo>p6_!eAw->9VpQi6^w7S27?}$gSZhZwcJZ;T#?6<-n9de;C5v=^X8iMB0|}ml&kw zhnWItfArV+Z$nz+v#!i%Eoi%zMlqEW!;}>|JmyJI*0?K8MlPEaQ=+C~zFq0g^F)46 zjjx}HSz?sMYPtN8>O-tVw2NdXZtC0qXEm<-iET>-58Tz_0rW^2%5*71y+$w_9sOrmIl1MykK4y_Vi(F-{J1qx=ha78MngqlZz=9RiszFA;pNjo4-Q~)Y_Yi21v3g_LZL2ep8juB4dLEIMofIi>^%~ z{5}j+1sGlhCkvX8`IM44^a6_dYwepr#LeOg&11bx3$xTF;@ZlDi^X@Eeq@US$;V*M zg4)LF?n_-%WYgppaG=eJ$F&8`MmV+%4IFaASXb z1d$kXB?YcR_h|j{9+-z%0bx(i$;kG#Q_944!G|VtKU1Uec)icD4AWxW@9VyxDtiwR z;>&lgs>}N^e8$B=yxE+T>K-BboaLR-#jX0a%7;>MD+WDxA2N7DN#^YSK>vCR|iThr$K4|o+@0L^`w6vFcJpKQLHlzJ?h;2Acb z!RD;>RGO$~$E?UfGu?o$^~eakkIciXx?b!^Y`W)1evLM5<_A zr6Qv*dBz#5TBk)W&D7Qv;YyUV6P?@afFE5j$sD-Jr8iA{2IvVvtjMvUeYZtEBJYp4 zzP)@d$Hum0^(mpzt>aiADGx>lt!>w{TMM4a%X>^DMqFn}@0MY2F4nE^67TdbNyiVy zsmpW=k~lDO6?!fhF4U33agPU`E23?hjbS$EcqW@DGj%`GCoL)Vu0cVc0Y25p>cw7+$v+U4t;oE&ogzj2}zD#M$~r!UbNh!>RKk%O@@NoFrn2at$dhO8i>eb9C>021jdgev}sUE0opkue?E; z!v0NjucHSSaG)4@k&)R>UP zX%xfC!_M3SL7ZPRidt~4(+pDq0z2K3UVgw^Q$ffV_%%WOTtq1uy)M4H7fMf_pvk6< zCA#I)>WT@2^=*FjV-Hz7EzY5O|CG&b@YiY^2IVxaemCrS|4i(n>(~PQvUf*Yj?Ha+ z8#mw7!+bQX~*-U-#(!pH(b!=2W zF~Ff3xP)an?_-D@{XJ zNBWT`NcWmzikx$!{KB7}CSxN`1U~h~(9~HCIHtr2f?dx^mL4Cq(sFRPcL+GghV@SC z8&aWiYeieE-D*xm39(tQj){(rFxvC??{DIW8hFLX)gD?qO3KgA!A8n& zdw5=-ZZx1nq{@3AnDU@!KS4!xN+RP!7c*^-HmYbDbdPo$Buyl2Dvr{xf4Y(=TG4K8 z0zS`^qMsS+C8Ah-MHPr!EHc#30s*fujec`C;AY5-kJK0MS7$1oHLP9&h;oq&T9#-? z+KlU%s++nFg(O7;Io{vpebUVGqvlHbc$OEwU)yQJG-Brr9eOk!QulXpb&at-lqou2 zenwUd2Vy|{8~r&vX#3xe4&1bDQ5f3M;?3jbp$t z9wn0^+5Lu)b<3YIFikPq?CPZ9g@V5j??0L74%aUm*Av6Iu`2hp4%STBYa{hWhhqz< zO&HhxO%)t%KhE3a6I1?nQvbVG$?kntr&XVVRyq0i56?G($pjd}Bj9ujnW)<7Om z0&4Ut)uY!tZANXvE#Ue7z+A9Qa|&PAC-yc2c&ViaaDq(Ve{Ni! zzwDa17i8~pX^0k5Yj<&bt(acOszCmyi=i=dcZ;9TYNaaJlR1$pR|QLH1dWnAU30Nu z7yIvjT^2ORGQ5S?`vC!h9b=Os(Y8gLmse1EFTb}D0H7Af z<9d~H7$lX7+WL#=oxG2SDR|!3DC(dLhfS1~tImpM5+Pa2@PD4Lw@j|ssE~aEKSU$= zdPixpPdmp>gw)g>PNv)=v+dSJNLwGujK#j+$;Ay?P1!XjoPZphz4pdWS)nll5YPcCgYrHj?>77<(E@&p0}Dg+i&$r*z`o1` zsmd+;iT(2jQ+WQ#Lh*Syg1TK9``C&jD!0XY=h|!)x^saV>IHH$-AtisYDZeO8{4p$w+N~7|Xkg97 z%>M93PL$m;D}&Tf>uVpIw>H=-m51Vlgg_0Gd0zBJ=_EJ`=YPyjq|Q&$Txy93d>_a3 zV(kibvy8S1X1YJlXk)!8A6M+>=0Zd6WnVzw^s`rJ1a1{kVf#k$X|Po}UEjIOf)lc; zXT2=l%=_(!%eK66AQ@ab-ZQV@>NTOtgnJBtT&OB#1qG6C}(n= z__ubDqCLmc*PyJ3#bZOFFyXS*bVEst>vTr4f8+em=z(YkePY@Pq9PH&h_8uH{e1~*9Cnv(f_IXcsQL~cqHG|JL;v1o7*AMtQN@cE3iZ{wv07^aj+Ao-TEep!93E-cCp65n<}9izbkyN7YMnU67RV1R>1^7jiuIi*se69tB5Vm~m zrG=o3d_tu^;E2fxsKFh*i(p-#`ssS0Bc&}x4Sz%UVR~CaV|d39^6ut<%QsFh1qEj_{^L zz9sO=>+j2b8I4DshTAl7`CG8+a;tiPF(r?L_|Mv)jOF3K&cxXys+=PS2Qa3*&RzgF zaKd+wMQ9@F{cTHk_rTH7dpWxNNo#h>o+37*dhfI!i_myy_jpZR)P~=osYeu8zaJ*+ z!*tt1gPyc>3z2L15~BHC$jC1yi% zCDskE%C?cROo%X@K@vwJ_>;;+kq;O^l)DD6+{G9VM=u+_-OjOwma@XMHbOmpXp|xT z-XZYKRO+TgW!LwlgHve{zzhCeSq6FKy6$$vZ4R>xrCRRY@t51-E2YXwX zpvKrANQ?9Hj3IVK92$7N+uALvdl)gPb9)Lg6@O=yi!HMKFN92ZaI6bP}LlpLJqUsALoA;Qr^|F5vG3aTqu+Ql`vySwe+ z?v~&Z2oAyBov?71;O@bKdvFLAAbaD^#$AJ3fS+^ksr&R--IuPOnXjhTL-*8LU(f2! zn^C)B!4B@L;tLj5i>gVFZy8zJ)sc3%wIklAF7nCOn zQ5)JLDxOcnO`n2v1!}H0iUX%I7E20)&A~%QXHkHNS=DB41emeaiJS~o1 z(r4*KDspXUIP#KSgdu+3T?z38L^5(}dd)HkJ~7lnB#bd8)PCH}l*Z=mDcv;-`E)%8 zNBEudO6w9h{-78Uk%`=DDZ3gJRzCWD3=4GKMPNz_{8V;D6tFM%o9x54ihFCC00xOCgddpC8{OHQnvvR!OuvZ7rgabm(&V6^0z#EA82=To!22YPQxhv!4~ zY!MNRtk43RezQh`xb#}bB7y%BDruCPst%Ma z;c^LQV4Rb3AKi&;n`Ib&;y4WI{35_p1^m`tIf1ZBVyw_TKB@bObLYz0oE!ZzbJBgM z)38F3FpR||EF-+JhC3m$3oU>)T?|EG-7j&79~EFiI-cFd5BCX+OD_}x_SPfv?|zqu zR(*_M65C|&ppxMK+uGc5mnr9=t_&EVLQR?Z$Ufp&tZ;ujWY2Ipr?=o7_cH$pZ6`I4 zP#^h_1$VQ>O>9!t>XBWr*GSyNzJwiv{-lFmWdlfST9_K9cmMu3?LEe9>o{II-8Fg3 z#Om;>o^YNp*QSX($Oq)biL8|+NMAotJ`W}vn zvccNq2bo@co7_eV#8;CS6PBW@zBRh1Z-)EYW<{;4(V1F1r1sth=IWe1>%v*i^P1Qb zXG&Z*Mw%3cO6|V{G))~#5Hf(Q5WQWC8je`Gy(Q@wizvk(%yvGrGU_`af*}Y5wy73E zB+VB4hL55N`>AYY**!tulB1fx)8K~F76=l%EXMMy3t+zEMd%Lq>OzU<9ug&qByp%@ zS!r&Ih|sj*7uhLw%XV-4<~C}o|F?fwFN$da5Y-*qPPIHGN0Cy@hsMv5uc*5gqHy!yYpO+tmfEF~zhKPK z7jgWEBPtvvju6Nz)A#`dYmZ^e=6w=U=W(m|E5!L)%m81XltRWIv4PS(uMxZRkD6GO z<3Nk(gW_EJ$b2)Y@V}yS31*ep>t=Q(Aw34)f-gd-gFkq4h%GR>h31XF3OPh3s z!*zzpaRceTKI5_G^u1jb0`q5m-U zH<*yLfd#8s<;$e2NI;@B7JfVACE0pZSfe?=(3Z;b^Kg@!cyy4%fk8#O$p~8sR+k`! zS~eeD)7&=|$+mhvK&?wJZ?Viqx#YKa@`q+}rM2kWywrQ_%X$p!%H;4Q`FCG02gPGf z)smWJR8u!x=IE&`xHdqnz9WXDYXqd@I{E(9%vWc*Vutm(hVJ&6qPm(%&brq%vQ7nY z;Dh)ddGEFMALGU@yE&L+aLQV8bbqQYC48=q6Jpj{0&Vhr2L=sLjpLcbcCUh|ZOQP> z2xP%8vJ88mMh?G4Pg=qHSaY}-D;H|Es^qVD<`;C{40X!OEd zvy}Ca%kQ7?0gj8yKg@Sza1JwTwYtfylu-x3;g#=y>~B($F{qbJh}y}6pGo3iO*hE? z&S}AutNT~5J^rb+WWNT)Bz<~6Q&XF-dd}#(5H~fXPXk9;t->19G0Fl?EhaE^nrnLh zr9H7g(;97{7D`Xa#rEANn}qr9CWXWDIyzKir<&Zg9-cdy%U8j8`HVImj<|wB?&67 zXJ)HUn*s4T)0P=pj7&*0^{LmqwpnV4Hm!P&@*0d7mSY1GZZVHqn$@FrF$l57A6qP) zXgI%eYh{qK=MXZftXTlInPV>M9@8#r;B6W=dO~n$?Rm(nm7|JvbazM?F$cJiOyEv~Nda6-f zPxz~ZZ(@yrcge<@TL)KK@p3)H^~=nU*MUL!DIBIP?RP_palpj!;%TwKt?BMDWaVDi z=ik4fO{YDjx{Yyc6Aw4r>4{CHGP3(+Lad<#A9}AL^T4n7+M;+9Lp?q;FW6i|!Gib- zTP;3xFAfYY!Uq_S%v@X|Tgtq}hXtPUL|Y~Ii_gXl zbpB8LukW!#1LH!;@KC%-`P3T{)z0M*zh&U8f>NE zlQsMT=b`uYONT~A*wSj*u92pGQyh|yI6T z>%+FzaGIA2v4!w(AjUO0@i@psapW)=ZMGGz9y(@9n56-uexPb=^j*p`C)^ls^&}B* z7hLV`15^5HCuM*70;W*YE%&{l7Y|Pph}RSC7?G}91LSvM=X`e$L?z+qP!Cv$d`O3g z%`E?RVf(hJ9Q{rxF)l9V>ura^l2*Avk*8VQ2p(pyg`o{(Y=hT4mp5N=fLdqO;)4#J3YW^U=3o-8(bD zqPk@5P1fg>XRi;Kh7>iBoM($FCh){q4wxevqS2Wl;;5AyTS{MtktNjD4(WF=#w2^_ zw^l0<^OXf2z*AGKLX)?_s)+|CQsuuK>V0!k;Gn9dg)J}TmpReFj>!kgS2-WP@Z5Co z>gV2sE1{L7J)L)@>h5zE{=$w z){3s+#b*EF0&)zTQaPR1iCV%%5=JBcwf*rO9j^rl?3bO18%oQ0p3Y0Dgxs&>Z4cDC z6r!6rNK3EeX$(*?bLC}$8^Es^umG3P(`xdG#cfMz_E*ObufKuqL!!i1!t#aK88$7| zI+gd`(16FGl$@c%)2-`{OtIlDC9SrHomgx7*pw%RwK?+hj*Vx`Y!gA*&$(ipkhAI4 zlSZmcBQ>j6s?~*A?*E1P;^j#RLSn)c5d43<6}J#?hATTP3s9=R;zX{1`L&?#Kw$!@ z;b^!6Mha%75pOAJzI8|NmKH09M=|UMV_LBD;c54<_Kej_H<9L)Hs{Q&Ato}CJ-H-$ zs#Ys+s%&QXDrJ4tgK-!Vv6%HuShI|dLOAiJqT}tZ2j^u6dhN_dsX~z&mF3S{L^9Q+ z0p*T63@UkyLjbbf+qKYdryd(b*7posLnEq8fi%a$C4X^wCkG)S=?514YZ}(djqIB< zt`XivJgOH=mJdTsd8t7Xf6>{rl>hzO_X+y)J1&|=Rwp3>qtTs_v1KY)xC-Cdx3m5ojWvS*`!oh~T_Jf0Mr0X86@5xYx-1bGp9S&x9a53)CTL&y+93s}6W zf4y&@=-i`)x0o>0tTZ1n{8D~TrMc#DLO7E8I&{IO1 zdtrpbsMpY=|`vs_j5u(iQ27px;L( z!BA7Z#80xZrV$!0Fq;DFq&2vuVDELu1kRV2uH%IKfXAQf(v9?%-Yk;r5EV=wF$W1-{nKLalf9id?}1y zA_uyA9>2cstLzKa6+J!Qwbj2|kCgX{`pp{%U>sXH8QGhOY0nRw))aIL`d;1^w^5SL zLsvV>Kdo<{*1Wo1T%S(VnL-8Pp4we?@zm@Z{h9W)zi!KiiW7fW6qB1Y!SMG`_B1`!M8#}5uX#~r94b74`Ruf^_O+D=zZja4Z;zJr@yNg8Zf+gS8x zuFcAdyY1f^{fCs2q<)KLjJ~NfXh*ldi$KKunY1SgGL%6okMPXObP4KU1BR+^z4ahX zq17)Us8KK8#gZRKb_I0UoC8op%Ww63+ni20&Hi$~yDu}MX`rtl3%;3Eu}!VKa;Ia^ z=Z&5BIWY~>$JC_BKPiIh>|nG+l}mT!;5f@cI&|jSrCUluI}tBtY1at0@|+6zPMqji z;+6^In;c2_nYhK_$p);fzXFG8z;)!|9&qDt!;-~nAdfuGYZ^`Ey1ukCP z)%uSVtbO(G>Fru`7U4-9Ia?bIyxZ)tl5%U2biAmF`BoowxlS#g6v2#=6wLhEvT;ye zg{AwH1?(2zACsFHHwe;3Ye5NnJyCGp3U{kh(nf83+&%-e0Qm@Vz zDoI*VRue{X(8JY-%5E?yo}jfhbJ(#>ir&UbA}Lm+*T3pak0TrO*Gh@c=DfS?KjH`V za_Bo6BK^B3&486K&V-S8IH%Hs3k(1^!AkU~0!r|SG7fN|b06#xi4RcuNRX~&rH~Gy zJvZ17DwFgl@$3x=DY1C^adj67Yxp_Q{WRl$h2+z#2jy#*CnAbfLcGxZ2Ena=1vT)9 zt5x;c`Z6iY5#kU&US)kLx+qnQxKP!y!p&DvbIn z0!M#E%0{C7k2e~e;N7*_UF_$61ydTq(@koXP*0ZFe@F0Zo`esg_W%bmUosH~B$>!eiQp%DLG9zPf=H;tRKpL#^0Pyyyk5+v#PV~99}lI} z-A_RltK|eyQg-s!R)#f9qp{b^@R?Aa0P5fjWD)KCc4QHHmtcw?uA9<7yvWTwwCx7a zs3VWA--KNU-0!07S{SzqMxh{bjU71hgtBP#H!}pLrcCpO2=#dOoN$ZLA?WpVN0eg? z6#_Vv_PcWT>M8ID$h2DZ7R3< zTV%gsl%B(+ASif+*44J>wYQy50;29O{;{uK6}T_m{;w}HfVfHEnh{U`LwT)#0oUu| zP3Yf)5o$@mTHy2n4u-J?ao7yjyzg@-5~^@#Qwu^~o;HV+HB&W_$N^4EgB-47GE62nhvwktUTLYg#Uyf!*fNMEZiz*Zil;I6RmNV}c zV5&WND}l>kA4Q6pGbmkdHoPH}&{pDxYm(*{=^ge!U2dHBH0ZV(IppN455Mo{l|bneXG%TO9A6Fy6lH6<*_*;|H})!kRUaP`ael*QBgE5bvI`VPjgE*YGxM;D{afq9Pc@K zL7dzmmJCNn1i_49M+62|zW4k*yxja8AU-A#h=~P_OU2nj*383_npswu8^q1?o*VRE zNCtruf-W&Ph?@CrD{5t1FH34}(0d^+S<5fBW=p8ERY!LGB=EJf@pi$3O zI!qB)Wk43t3!w>AcgB3aoAa^I>T2@Bf(;#hlrIqfz|)k9?N_+dk66bqiCxYQhpesi zO?sGQ5V-}l-upW`tgN!+G`#REM@UdlMg%Wq=s0B^`V<%!rczfwx?_V}E{MD-(zCy( z9q4iDP>Y?b4=t$kn&`cB{z;XQ<9WP1xkWHoDBLTJ=z&%yG-(3cPI_SQrK z<>^EZ&v~@F4*w-X5<^*#9i(0s61+Fb4shw9Ct8wbu99bG;mu8PW1{Y(3*5{dpoc5u zRDQo=aWI+lSH1{*C``g1i(^r2m?w_0na{c@7jhQ3tLu%AT`#fn6TqyR-wuPG(GA(8 z|7p}4r`%2L26ey-grf>%X>yl(g+_PjjjJ?_sa*7Ni0<;|SqP7MBzs142$-iC0LgC7 zM2RxTGhsO@OgXClhEX*N4((ZL?dT-#za9+-Sy#E1lwwo=SH-;(y- zjCcUByn7UX|2JnB${2G-pauj$AR{y`En7cJ>NgdQOPAW<8G@quLI`2=LSrTF-``Gt7pK?3|dd^{j= z>i;hUQ~b@)f8F{$zd%Z%Aqzl}p=4w92|wbl7Yq>-Kem`>d@zoOpS#)=~vRy1U#E0ugtH*D{xNR$lkpe0hMxJ&Pc|5z;<( zivOvQbk;Fvyw#3BBLX9=myP}7KpF2JF`du35n$WT*NwlDc|BI*yuPD!DQ&`;u`tUV z%aAdwqK-{HPRXONNirs0JNg?a= z=wofdPDXp-pYm=>O@{MFc*Xq1K9HYTQ^lo3Qka~dQpKi39GP?~q+r5VEriO7$X6gv zr-AKzAG2P3u(EIc!_>I~k!o;N0L`2;nU&Cal?p8Wp!tZhg*jk^Qd%TxUamqmPevr9 ztcG=z&ZgTYFO2&)BJYD_f$h7v5{zO`Y}KF-&9OF*gGBud&BTsWo0cw!jwC9F?P#?{ zqh$W5=Aa=QR(UPE^8Rd5`6=d>=O}c1Ok;hEU83!iZ zgB7F+cO-F`kr*VIvaZNH0)<+n%F~-jJ#>VMq?l6ONjx}(;-$)yc!)iae4&LYBS`Qf zSOLj>ZD;|5eG3=?x02#;(P$PR)3l&SI4cxenyQn9l@#D=vVZL!3HBIKlmukoHw5dC z96%+A9-M+J2n#zNsp{U+Ys-?L-**Xn9zp5eInOI)$?@$uii_lM;ON_L2J$CGC9_?S zMXjf#1esMUg3B0f={_m(`-SHg%Kn33T#|ZkV&qI=Ww>f`PS`)BI#QH_dw#9WefWLF zW=N)gK*&J7`Dd6+5Iby^iSK}-+0d%Gan(jk;oS;IK#0>ZC7Fh5l~-Vf?vyYa9*Nt5 z4SGJaGCd|0-JY|Le+FY|%2WYMuxI=z)s$9P_&J);A2b!ey?_xebP4nq@4(# zHk}d#M_PcCTK98d6*=)5oz*IUP@QQzP|`CF8J6!oC6e~9d(lj!l0ET%>gei^U+j!T zVJloNOiXv zUOdT*)%P7c8C4@K&3ZY{lIKac?Wmiow&cGg1?%mdy2kHuTvcn7Adb&VOxjE}4iwlm zTP&p|^6IyqbhE@?rhf%gN(jcqzt&Fuuml1$O8CapYV19rZZdC>w$&EEgm1Zi^)%8B zG)qz$R)wsCs;e*#K|ks0gP3nNC>_NK<%CD+N z0eMPgkUxKt@{F(jQI9mcSE*YT%347%-3xP+=2>ZeKB#w=AnqVNp1N^?kjRuN+&_zY z7Z|dOcO)M&f~TA}Nl#DsQ>+puQ@FBm_4saD?i|wj3Oo3I44N_Kg+Ph^M(GNmF)+xf H$)o)b#g!K~ diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index e7a9069e4c..6376552550 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -122,8 +122,6 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/mpt/insert/insert_extension.asm"), include_str!("asm/mpt/insert/insert_leaf.asm"), include_str!("asm/mpt/insert/insert_trie_specific.asm"), - include_str!("asm/mpt/load/load.asm"), - include_str!("asm/mpt/load/load_trie_specific.asm"), include_str!("asm/mpt/read.asm"), include_str!("asm/mpt/storage/storage_read.asm"), include_str!("asm/mpt/storage/storage_write.asm"), diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 0c07c436cf..b495d49947 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -13,15 +13,28 @@ global main: // Initialise the shift table %shift_table_init - - // Second, load all MPT data from the prover. - PUSH hash_initial_tries - %jump(load_all_mpts) + + // Initialize the state, transaction and receipt trie root pointers. + PROVER_INPUT(trie_ptr::state) + %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + PROVER_INPUT(trie_ptr::txn) + %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) + PROVER_INPUT(trie_ptr::receipt) + %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) global hash_initial_tries: - %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE) %assert_eq + // We compute the length of the trie data segment in `mpt_hash` so that we + // can check the value provided by the prover. + // We initialize the segment length with 1 because the segment contains + // the null pointer `0` when the tries are empty. + PUSH 1 + %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE) %assert_eq + // stack: trie_data_len %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE) %assert_eq + // stack: trie_data_len %mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE) %assert_eq + // stack: trie_data_full_len + %mstore_global_metadata(@GLOBAL_METADATA_TRIE_DATA_SIZE) global start_txn: // stack: (empty) @@ -64,7 +77,10 @@ global hash_final_tries: %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_AFTER) %assert_eq DUP3 %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_AFTER) %assert_eq %pop3 + PUSH 1 // initial trie data length %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER) %assert_eq %mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER) %assert_eq + // We don't need the trie data length here. + POP %jump(halt) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 88f5bce4ef..df7e1d741d 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -2,40 +2,45 @@ // // encode_value is a function which should take as input // - the position within @SEGMENT_RLP_RAW to write to, -// - the offset of a value within @SEGMENT_TRIE_DATA, and -// - a return address. +// - the offset of a value within @SEGMENT_TRIE_DATA, +// - a return address, and +// - the current length of @SEGMENT_TRIE_DATA // It should serialize the value, write it to @SEGMENT_RLP_RAW starting at the -// given position, and return an updated position (the next unused offset). +// given position, and return an updated position (the next unused offset) as well +// as an updated length for @SEGMENT_TRIE_DATA. // -// Pre stack: node_ptr, encode_value, retdest -// Post stack: hash +// Given the initial length of the `TrieData` segment, it also updates the length +// for the current trie. +// +// Pre stack: node_ptr, encode_value, cur_len, retdest +// Post stack: hash, new_len global mpt_hash: - // stack: node_ptr, encode_value, retdest - %stack (node_ptr, encode_value) -> (node_ptr, encode_value, mpt_hash_hash_if_rlp) + // stack: node_ptr, encode_value, cur_len, retdest + %stack (node_ptr, encode_value, cur_len) -> (node_ptr, encode_value, cur_len, mpt_hash_hash_if_rlp) %jump(encode_or_hash_node) mpt_hash_hash_if_rlp: - // stack: result, result_len, retdest + // stack: result, result_len, new_len, retdest // If result_len < 32, then we have an RLP blob, and we need to hash it. DUP2 %lt_const(32) %jumpi(mpt_hash_hash_rlp) // Otherwise, we already have a hash, so just return it. - // stack: result, result_len, retdest - %stack (result, result_len, retdest) -> (retdest, result) + // stack: result, result_len, new_len, retdest + %stack (result, result_len, new_len, retdest) -> (retdest, result, new_len) JUMP mpt_hash_hash_rlp: - // stack: result, result_len, retdest - %stack (result, result_len) - // context, segment, offset, value, len, retdest - -> (0, @SEGMENT_RLP_RAW, 0, result, result_len, mpt_hash_hash_rlp_after_unpacking) + // stack: result, result_len, new_len, retdest + %stack (result, result_len, new_len) + // context, segment, offset, value, len, trie_len, retdest + -> (0, @SEGMENT_RLP_RAW, 0, result, result_len, mpt_hash_hash_rlp_after_unpacking, new_len) %jump(mstore_unpacking) mpt_hash_hash_rlp_after_unpacking: - // stack: result_len, retdest + // stack: result_len, new_len, retdest PUSH 0 // offset PUSH @SEGMENT_RLP_RAW // segment PUSH 0 // context - // stack: result_addr: 3, result_len, retdest + // stack: result_addr: 3, result_len, new_len, retdest KECCAK_GENERAL - // stack: hash, retdest - SWAP1 + // stack: hash, new_len, retdest + %stack(hash, new_len, retdest) -> (retdest, hash, new_len) JUMP // Given a trie node, return its RLP encoding if it is is less than 32 bytes, @@ -47,11 +52,11 @@ mpt_hash_hash_rlp_after_unpacking: // Pre stack: node_ptr, encode_value, retdest // Post stack: result, result_len global encode_or_hash_node: - // stack: node_ptr, encode_value, retdest + // stack: node_ptr, encode_value, cur_len, retdest DUP1 %mload_trie_data // Check if we're dealing with a concrete node, i.e. not a hash node. - // stack: node_type, node_ptr, encode_value, retdest + // stack: node_type, node_ptr, encode_value, cur_len, retdest DUP1 PUSH @MPT_NODE_HASH SUB @@ -59,51 +64,54 @@ global encode_or_hash_node: // If we got here, node_type == @MPT_NODE_HASH. // Load the hash and return (hash, 32). - // stack: node_type, node_ptr, encode_value, retdest + // stack: node_type, node_ptr, encode_value, cur_len, retdest POP - // stack: node_ptr, encode_value, retdest + // Update the length of the `TrieData` segment: there are only two + // elements in a hash node. + SWAP2 %add_const(2) SWAP2 + // stack: node_ptr, encode_value, cur_len, retdest %increment // Skip over node type prefix - // stack: hash_ptr, encode_value, retdest + // stack: hash_ptr, encode_value, cur_len, retdest %mload_trie_data - // stack: hash, encode_value, retdest - %stack (hash, encode_value, retdest) -> (retdest, hash, 32) + // stack: hash, encode_value, cur_len, retdest + %stack (hash, encode_value, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP encode_or_hash_concrete_node: - %stack (node_type, node_ptr, encode_value) -> (node_type, node_ptr, encode_value, maybe_hash_node) + %stack (node_type, node_ptr, encode_value, cur_len) -> (node_type, node_ptr, encode_value, cur_len, maybe_hash_node) %jump(encode_node) maybe_hash_node: - // stack: result_ptr, result_len, retdest + // stack: result_ptr, result_len, cur_len, retdest DUP2 %lt_const(32) %jumpi(pack_small_rlp) // result_len >= 32, so we hash the result. - // stack: result_ptr, result_len, retdest + // stack: result_ptr, result_len, cur_len, retdest PUSH @SEGMENT_RLP_RAW // segment PUSH 0 // context - // stack: result_addr: 3, result_len, retdest + // stack: result_addr: 3, result_len, cur_len, retdest KECCAK_GENERAL - %stack (hash, retdest) -> (retdest, hash, 32) + %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP pack_small_rlp: - // stack: result_ptr, result_len, retdest - %stack (result_ptr, result_len) + // stack: result_ptr, result_len, cur_len, retdest + %stack (result_ptr, result_len, cur_len) -> (0, @SEGMENT_RLP_RAW, result_ptr, result_len, - after_packed_small_rlp, result_len) + after_packed_small_rlp, result_len, cur_len) %jump(mload_packing) after_packed_small_rlp: - %stack (result, result_len, retdest) -> (retdest, result, result_len) + %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) JUMP // RLP encode the given trie node, and return an (pointer, length) pair // indicating where the data lives within @SEGMENT_RLP_RAW. // -// Pre stack: node_type, node_ptr, encode_value, retdest -// Post stack: result_ptr, result_len +// Pre stack: node_type, node_ptr, encode_value, cur_len, retdest +// Post stack: result_ptr, result_len, cur_len encode_node: - // stack: node_type, node_ptr, encode_value, retdest + // stack: node_type, node_ptr, encode_value, cur_len, retdest // Increment node_ptr, so it points to the node payload instead of its type. SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, encode_value, retdest + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(encode_node_empty) DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(encode_node_branch) @@ -115,25 +123,29 @@ encode_node: PANIC global encode_node_empty: - // stack: node_type, node_payload_ptr, encode_value, retdest + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest + // Then length of `TrieData` is unchanged here. %pop3 - // stack: retdest + // stack: cur_len, retdest // An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string. // TODO: Write this byte just once to RLP memory, then we can always return (0, 1). %alloc_rlp_block - // stack: rlp_pos, retdest + // stack: rlp_pos, cur_len, retdest PUSH 0x80 - // stack: 0x80, rlp_pos, retdest + // stack: 0x80, rlp_pos, cur_len, retdest DUP2 - // stack: rlp_pos, 0x80, rlp_pos, retdest + // stack: rlp_pos, 0x80, rlp_pos, cur_len, retdest %mstore_rlp - %stack (rlp_pos, retdest) -> (retdest, rlp_pos, 1) + %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, 1, cur_len) JUMP global encode_node_branch: - // stack: node_type, node_payload_ptr, encode_value, retdest + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest POP - // stack: node_payload_ptr, encode_value, retdest + + // `TrieData` stores the node type, 16 children pointers, and a value pointer. + SWAP2 %add_const(18) SWAP2 + // stack: node_payload_ptr, encode_value, cur_len, retdest // Get the next unused offset within the encoded child buffers. // Then immediately increment the next unused offset by 16, so any @@ -142,8 +154,7 @@ global encode_node_branch: %mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) DUP1 %add_const(16) %mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) - // stack: base_offset, node_payload_ptr, encode_value, retdest - + // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest // We will call encode_or_hash_node on each child. For the i'th child, we // will store the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in // SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. @@ -151,111 +162,118 @@ global encode_node_branch: %encode_child(4) %encode_child(5) %encode_child(6) %encode_child(7) %encode_child(8) %encode_child(9) %encode_child(10) %encode_child(11) %encode_child(12) %encode_child(13) %encode_child(14) %encode_child(15) - // stack: base_offset, node_payload_ptr, encode_value, retdest + // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest // Now, append each child to our RLP tape. %alloc_rlp_block DUP1 - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest %append_child(0) %append_child(1) %append_child(2) %append_child(3) %append_child(4) %append_child(5) %append_child(6) %append_child(7) %append_child(8) %append_child(9) %append_child(10) %append_child(11) %append_child(12) %append_child(13) %append_child(14) %append_child(15) - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest %stack (rlp_pos, rlp_start, base_offset, node_payload_ptr) -> (node_payload_ptr, rlp_pos, rlp_start) %add_const(16) - // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, retdest + // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest %mload_trie_data - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest DUP1 %jumpi(encode_node_branch_with_value) // No value; append the empty string (0x80). - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start) %mstore_rlp - // stack: rlp_pos', rlp_start, retdest + // stack: rlp_pos', rlp_start, cur_len, retdest %increment - // stack: rlp_pos'', rlp_start, retdest + // stack: rlp_pos'', rlp_start, cur_len, retdest %jump(encode_node_branch_prepend_prefix) encode_node_branch_with_value: - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value) - -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix, rlp_start) + // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest + %stack (value_ptr, rlp_pos, rlp_start, encode_value, cur_len) + -> (encode_value, rlp_pos, value_ptr, cur_len, encode_node_branch_after_value, rlp_start) JUMP // call encode_value +encode_node_branch_after_value: + // stack: rlp_pos'', cur_len, rlp_start, retdest + %stack(rlp_pos, cur_len, rlp_start, retdest) -> (rlp_pos, rlp_start, cur_len, retdest) encode_node_branch_prepend_prefix: - // stack: rlp_pos'', rlp_start, retdest + // stack: rlp_pos'', rlp_start, cur_len, retdest %prepend_rlp_list_prefix - // stack: rlp_prefix_start, rlp_len, retdest - %stack (rlp_prefix_start, rlp_len, retdest) - -> (retdest, rlp_prefix_start, rlp_len) + // stack: rlp_prefix_start, rlp_len, cur_len, retdest + %stack (rlp_prefix_start, rlp_len, cur_len, retdest) + -> (retdest, rlp_prefix_start, rlp_len, cur_len) JUMP // Part of the encode_node_branch function. Encodes the i'th child. // Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in // SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. %macro encode_child(i) - // stack: base_offset, node_payload_ptr, encode_value, retdest + // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest PUSH %%after_encode DUP4 DUP4 - // stack: node_payload_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, retdest + // stack: node_payload_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest %add_const($i) %mload_trie_data - // stack: child_i_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, retdest + // stack: child_i_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest + %stack(child_i_ptr, encode_value, after_encode, base_offset, node_payload_ptr, encode_value, cur_len) -> (child_i_ptr, encode_value, cur_len, after_encode, base_offset, node_payload_ptr, encode_value) %jump(encode_or_hash_node) %%after_encode: - // stack: result, result_len, base_offset, node_payload_ptr, encode_value, retdest + // stack: result, result_len, cur_len, base_offset, node_payload_ptr, encode_value, retdest + %stack(result, result_len, cur_len, base_offset, node_payload_ptr, encode_value) -> (result, result_len, base_offset, node_payload_ptr, encode_value, cur_len) DUP3 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD) - // stack: result_len, base_offset, node_payload_ptr, encode_value, retdest + // stack: result_len, base_offset, node_payload_ptr, encode_value, cur_len, retdest DUP2 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) - // stack: base_offset, node_payload_ptr, encode_value, retdest + // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest %endmacro // Part of the encode_node_branch function. Appends the i'th child's RLP. %macro append_child(i) - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result DUP4 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest PUSH 160 DUP4 // rlp_pos %mstore_rlp SWAP2 %increment SWAP2 // rlp_pos += 1 %%unpack: - %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest) + %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, - rlp_start, base_offset, node_payload_ptr, encode_value, retdest) + rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) %jump(mstore_unpacking_rlp) %%after_unpacking: - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest %endmacro global encode_node_extension: - // stack: node_type, node_payload_ptr, encode_value, retdest - %stack (node_type, node_payload_ptr, encode_value) - -> (node_payload_ptr, encode_value, encode_node_extension_after_encode_child, node_payload_ptr) + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest + SWAP3 %add_const(4) SWAP3 + %stack (node_type, node_payload_ptr, encode_value, cur_len) + -> (node_payload_ptr, encode_value, cur_len, encode_node_extension_after_encode_child, node_payload_ptr) %add_const(2) %mload_trie_data - // stack: child_ptr, encode_value, encode_node_extension_after_encode_child, node_payload_ptr, retdest + // stack: child_ptr, encode_value, cur_len, encode_node_extension_after_encode_child, node_payload_ptr, retdest %jump(encode_or_hash_node) encode_node_extension_after_encode_child: - // stack: result, result_len, node_payload_ptr, retdest + // stack: result, result_len, cur_len, node_payload_ptr, retdest + %stack (result, result_len, cur_len, node_payload_ptr) -> (result, result_len, node_payload_ptr, cur_len) %alloc_rlp_block - // stack: rlp_start, result, result_len, node_payload_ptr, retdest + // stack: rlp_start, result, result_len, node_payload_ptr, cur_len, retdest PUSH encode_node_extension_after_hex_prefix // retdest PUSH 0 // terminated - // stack: terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + // stack: terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest DUP6 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + // stack: packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest DUP7 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + // stack: num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest DUP5 - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest %jump(hex_prefix_rlp) encode_node_extension_after_hex_prefix: - // stack: rlp_pos, rlp_start, result, result_len, node_payload_ptr, retdest + // stack: rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. DUP4 %sub_const(32) %jumpi(encode_node_extension_unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. @@ -264,44 +282,50 @@ encode_node_extension_after_hex_prefix: %mstore_rlp %increment // rlp_pos += 1 encode_node_extension_unpack: - %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr) - -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start) + %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len) + -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start, cur_len) %jump(mstore_unpacking_rlp) encode_node_extension_after_unpacking: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_pos, rlp_start, cur_len, retdest %prepend_rlp_list_prefix - %stack (rlp_prefix_start_pos, rlp_len, retdest) - -> (retdest, rlp_prefix_start_pos, rlp_len) + %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) + -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) JUMP global encode_node_leaf: - // stack: node_type, node_payload_ptr, encode_value, retdest + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest + // `TrieData` holds the node type, the number of nibbles, the nibbles, + // the pointer to the value and the value. + // First, we add 4 for the node type, the number of nibbles, the nibbles + // and the pointer to the value. + SWAP3 %add_const(4) SWAP3 POP - // stack: node_payload_ptr, encode_value, retdest + // stack: node_payload_ptr, encode_value, cur_len, retdest %alloc_rlp_block PUSH encode_node_leaf_after_hex_prefix // retdest PUSH 1 // terminated - // stack: terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + // stack: terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest DUP4 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + // stack: packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest DUP5 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + // stack: num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest DUP5 - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest %jump(hex_prefix_rlp) encode_node_leaf_after_hex_prefix: - // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest SWAP2 %add_const(2) // The value pointer starts at index 3, after num_nibbles and packed_nibbles. - // stack: value_ptr_ptr, rlp_start, rlp_pos, encode_value, retdest + // stack: value_ptr_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest %mload_trie_data - // stack: value_ptr, rlp_start, rlp_pos, encode_value, retdest - %stack (value_ptr, rlp_start, rlp_pos, encode_value, retdest) - -> (encode_value, rlp_pos, value_ptr, encode_node_leaf_after_encode_value, rlp_start, retdest) + // stack: value_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest + %stack (value_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest) + -> (encode_value, rlp_pos, value_ptr, cur_len, encode_node_leaf_after_encode_value, rlp_start, retdest) JUMP encode_node_leaf_after_encode_value: - // stack: rlp_end_pos, rlp_start, retdest + // stack: rlp_end_pos, cur_len, rlp_start, retdest + %stack(rlp_end_pos, cur_len, rlp_start, retdest) -> (rlp_end_pos, rlp_start, cur_len, retdest) %prepend_rlp_list_prefix - %stack (rlp_prefix_start_pos, rlp_len, retdest) - -> (retdest, rlp_prefix_start_pos, rlp_len) + %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) + -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index e5734f8046..2ffefb7d5a 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -1,116 +1,138 @@ // Hashing logic specific to a particular trie. global mpt_hash_state_trie: - // stack: retdest + // stack: cur_len, retdest PUSH encode_account %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - // stack: node_ptr, encode_account, retdest + // stack: node_ptr, encode_account, cur_len, retdest %jump(mpt_hash) %macro mpt_hash_state_trie + // stack: cur_len PUSH %%after + SWAP1 %jump(mpt_hash_state_trie) %%after: %endmacro global mpt_hash_storage_trie: - // stack: node_ptr, retdest - %stack (node_ptr) -> (node_ptr, encode_storage_value) + // stack: node_ptr, cur_len, retdest + %stack (node_ptr, cur_len) -> (node_ptr, encode_storage_value, cur_len) %jump(mpt_hash) %macro mpt_hash_storage_trie - %stack (node_ptr) -> (node_ptr, %%after) + %stack (node_ptr, cur_len) -> (node_ptr, cur_len, %%after) %jump(mpt_hash_storage_trie) %%after: %endmacro global mpt_hash_txn_trie: - // stack: retdest + // stack: cur_len, retdest PUSH encode_txn %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - // stack: node_ptr, encode_txn, retdest + // stack: node_ptr, encode_txn, cur_len, retdest %jump(mpt_hash) %macro mpt_hash_txn_trie + // stack: cur_len PUSH %%after + SWAP1 %jump(mpt_hash_txn_trie) %%after: %endmacro global mpt_hash_receipt_trie: - // stack: retdest + // stack: cur_len, retdest PUSH encode_receipt %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - // stack: node_ptr, encode_receipt, retdest + // stack: node_ptr, encode_receipt, cur_len, retdest %jump(mpt_hash) %macro mpt_hash_receipt_trie + // stack: cur_len PUSH %%after + SWAP1 %jump(mpt_hash_receipt_trie) %%after: %endmacro global encode_account: - // stack: rlp_pos, value_ptr, retdest + // stack: rlp_pos, value_ptr, cur_len, retdest // First, we compute the length of the RLP data we're about to write. + // We also update the length of the trie data segment. // The nonce and balance fields are variable-length, so we need to load them // to determine their contribution, while the other two fields are fixed // 32-bytes integers. + + // First, we add 4 to the trie data length, for the nonce, + // the balance, the storage pointer and the code hash. + SWAP2 %add_const(4) SWAP2 + + // Now, we start the encoding. + // stack: rlp_pos, value_ptr, cur_len, retdest DUP2 %mload_trie_data // nonce = value[0] %rlp_scalar_len - // stack: nonce_rlp_len, rlp_pos, value_ptr, retdest + // stack: nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data // balance = value[1] %rlp_scalar_len - // stack: balance_rlp_len, nonce_rlp_len, rlp_pos, value_ptr, retdest + // stack: balance_rlp_len, nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest PUSH 66 // storage_root and code_hash fields each take 1 + 32 bytes ADD ADD - // stack: payload_len, rlp_pos, value_ptr, retdest + // stack: payload_len, rlp_pos, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, payload_len, value_ptr, retdest + // stack: rlp_pos, payload_len, value_ptr, cur_len, retdest DUP2 %rlp_list_len - // stack: list_len, rlp_pos, payload_len, value_ptr, retdest + // stack: list_len, rlp_pos, payload_len, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, list_len, payload_len, value_ptr, retdest + // stack: rlp_pos, list_len, payload_len, value_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos_2, payload_len, value_ptr, retdest + // stack: rlp_pos_2, payload_len, value_ptr, cur_len, retdest %encode_rlp_list_prefix - // stack: rlp_pos_3, value_ptr, retdest + // stack: rlp_pos_3, value_ptr, cur_len, retdest DUP2 %mload_trie_data // nonce = value[0] - // stack: nonce, rlp_pos_3, value_ptr, retdest + // stack: nonce, rlp_pos_3, value_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos_4, value_ptr, retdest + // stack: rlp_pos_4, value_ptr, cur_len, retdest DUP2 %increment %mload_trie_data // balance = value[1] - // stack: balance, rlp_pos_4, value_ptr, retdest + // stack: balance, rlp_pos_4, value_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos_5, value_ptr, retdest - DUP2 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] - // stack: storage_root_ptr, rlp_pos_5, value_ptr, retdest + // stack: rlp_pos_5, value_ptr, cur_len, retdest + DUP3 + DUP3 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] + // stack: storage_root_ptr, cur_len, rlp_pos_5, value_ptr, cur_len, retdest + + // Hash storage trie. %mpt_hash_storage_trie - // stack: storage_root_digest, rlp_pos_5, value_ptr, retdest - SWAP1 %encode_rlp_256 - // stack: rlp_pos_6, value_ptr, retdest + // stack: storage_root_digest, new_len, rlp_pos_5, value_ptr, cur_len, retdest + %stack(storage_root_digest, new_len, rlp_pos_five, value_ptr, cur_len) -> (rlp_pos_five, storage_root_digest, value_ptr, new_len) + %encode_rlp_256 + // stack: rlp_pos_6, value_ptr, new_len, retdest SWAP1 %add_const(3) %mload_trie_data // code_hash = value[3] - // stack: code_hash, rlp_pos_6, retdest + // stack: code_hash, rlp_pos_6, new_len, retdest SWAP1 %encode_rlp_256 - // stack: rlp_pos_7, retdest - SWAP1 + // stack: rlp_pos_7, new_len, retdest + %stack(rlp_pos_7, new_len, retdest) -> (retdest, rlp_pos_7, new_len) JUMP global encode_txn: - // stack: rlp_pos, value_ptr, retdest + // stack: rlp_pos, value_ptr, cur_len, retdest // Load the txn_rlp_len which is at the beginning of value_ptr DUP2 %mload_trie_data - // stack: txn_rlp_len, rlp_pos, value_ptr, retdest + // stack: txn_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // We need to add 1+txn_rlp_len to the length of the trie data. + SWAP3 DUP4 %increment ADD + // stack: new_len, rlp_pos, value_ptr, txn_rlp_len, retdest + SWAP3 SWAP2 %increment - // stack: txn_rlp_ptr=value_ptr+1, rlp_pos, txn_rlp_len, retdest + // stack: txn_rlp_ptr=value_ptr+1, rlp_pos, txn_rlp_len, new_len, retdest %stack (txn_rlp_ptr, rlp_pos, txn_rlp_len) -> (rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) // Encode the txn rlp prefix - // stack: rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, retdest + // stack: rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix // copy txn_rlp to the new block - // stack: rlp_pos, txn_rlp_len, txn_rlp_ptr, retdest + // stack: rlp_pos, txn_rlp_len, txn_rlp_ptr, new_len, retdest %stack (rlp_pos, txn_rlp_len, txn_rlp_ptr) -> ( 0, @SEGMENT_RLP_RAW, rlp_pos, // dest addr 0, @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 @@ -118,155 +140,176 @@ global encode_txn: txn_rlp_len, rlp_pos) %memcpy_bytes ADD - // stack new_rlp_pos, retdest - SWAP1 + // stack new_rlp_pos, new_len, retdest + %stack(new_rlp_pos, new_len, retdest) -> (retdest, new_rlp_pos, new_len) JUMP // We assume a receipt in memory is stored as: // [payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]]. // A log is [payload_len, address, num_topics, [topics], data_len, [data]]. global encode_receipt: - // stack: rlp_pos, value_ptr, retdest + // stack: rlp_pos, value_ptr, cur_len, retdest + // First, we add 261 to the trie data length for all values before the logs besides the type. + // These are: the payload length, the status, cum_gas_used, the bloom filter (256 elements), + // the length of the logs payload and the length of the logs. + SWAP2 %add_const(261) SWAP2 // There is a double encoding! What we compute is: // either RLP(RLP(receipt)) for Legacy transactions or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. // First encode the wrapper prefix. DUP2 %mload_trie_data - // stack: first_value, rlp_pos, value_ptr, retdest + // stack: first_value, rlp_pos, value_ptr, cur_len, retdest // The first value is either the transaction type or the payload length. // Since the receipt contains at least the 256-bytes long bloom filter, payload_len > 3. DUP1 %lt_const(3) %jumpi(encode_nonzero_receipt_type) // If we are here, then the first byte is the payload length. %rlp_list_len - // stack: rlp_receipt_len, rlp_pos, value_ptr, retdest + // stack: rlp_receipt_len, rlp_pos, value_ptr, cur_len, retdest SWAP1 %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos, value_ptr, retdest + // stack: rlp_pos, value_ptr, cur_len, retdest encode_receipt_after_type: - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest // Then encode the receipt prefix. // `payload_ptr` is either `value_ptr` or `value_ptr+1`, depending on the transaction type. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, payload_len_ptr, retdest + // stack: payload_len, rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest // Encode status. DUP2 %increment %mload_trie_data - // stack: status, rlp_pos, payload_len_ptr, retdest + // stack: status, rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest // Encode cum_gas_used. DUP2 %add_const(2) %mload_trie_data - // stack: cum_gas_used, rlp_pos, payload_len_ptr, retdest + // stack: cum_gas_used, rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest // Encode bloom. PUSH 256 // Bloom length. DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA PUSH 0 // MPT src address. DUP5 - // stack: rlp_pos, SRC, 256, rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, SRC, 256, rlp_pos, payload_len_ptr, cur_len, retdest %encode_rlp_string - // stack: rlp_pos, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 POP - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest // Encode logs prefix. DUP2 %add_const(259) %mload_trie_data - // stack: logs_payload_len, rlp_pos, payload_len_ptr, retdest + // stack: logs_payload_len, rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len, retdest DUP2 %add_const(261) - // stack: logs_ptr, rlp_pos, payload_len_ptr, retdest + // stack: logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest DUP3 %add_const(260) %mload_trie_data - // stack: num_logs, logs_ptr, rlp_pos, payload_len_ptr, retdest + // stack: num_logs, logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest PUSH 0 encode_receipt_logs_loop: - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest DUP2 DUP2 EQ - // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest %jumpi(encode_receipt_end) - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, retdest + // We add 4 to the trie data length for the fixed size elements in the current log. + SWAP5 %add_const(4) SWAP5 + // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest DUP3 DUP5 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode log prefix. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: payload_len, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode address. DUP2 %increment %mload_trie_data - // stack: address, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: address, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_160 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP2 %add_const(2) %mload_trie_data - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode topics prefix. DUP1 %mul_const(33) - // stack: topics_payload_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: topics_payload_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP3 %encode_rlp_list_prefix - // stack: new_rlp_pos, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: new_rlp_pos, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP2 POP - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + + // Add `num_topics` to the length of the trie data segment. + DUP1 SWAP9 + // stack: cur_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest + ADD SWAP8 + + // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP2 %add_const(3) - // stack: topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest PUSH 0 encode_receipt_topics_loop: - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 DUP2 EQ - // stack: j == num_topics, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: j == num_topics, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %jumpi(encode_receipt_topics_end) - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP2 DUP2 ADD %mload_trie_data - // stack: current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 - // stack: rlp_pos, current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %encode_rlp_256 - // stack: new_rlp_pos, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: new_rlp_pos, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP3 POP - // stack: j, topics_ptr, new_rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: j, topics_ptr, new_rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %increment %jump(encode_receipt_topics_loop) encode_receipt_topics_end: - // stack: num_topics, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: num_topics, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest ADD - // stack: data_len_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: data_len_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: rlp_pos, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: num_topics, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, retdest + // stack: num_topics, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest POP - // stack: i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, retdest + // stack: i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest // Encode data prefix. DUP3 %mload_trie_data - // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, retdest + // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + + // Add `data_len` to the length of the trie data. + DUP1 SWAP7 ADD SWAP6 + + // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest DUP4 %increment DUP2 ADD - // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, retdest + // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest SWAP4 %increment - // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest PUSH @SEGMENT_TRIE_DATA PUSH 0 - // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest DUP8 - // stack: rlp_pos, SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest %encode_rlp_string - // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest SWAP4 POP - // stack: i, num_logs, next_log_ptr, new_rlp_pos, payload_len_ptr, retdest + // stack: i, num_logs, next_log_ptr, new_rlp_pos, payload_len_ptr, cur_len'', retdest %increment %jump(encode_receipt_logs_loop) encode_receipt_end: - // stack: num_logs, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, retdest + // stack: num_logs, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest %pop3 - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_pos, payload_len_ptr, cur_len'', retdest SWAP1 POP - // stack: rlp_pos, retdest - SWAP1 + // stack: rlp_pos, cur_len'', retdest + %stack(rlp_pos, new_len, retdest) -> (retdest, rlp_pos, new_len) JUMP encode_nonzero_receipt_type: - // stack: txn_type, rlp_pos, value_ptr, retdest + // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest + // We have a nonlegacy receipt, so the type is also stored in the trie data segment. + SWAP3 %increment SWAP3 + // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data // stack: payload_len, txn_type, rlp_pos, value_ptr, retdest // The transaction type is encoded in 1 byte @@ -285,15 +328,19 @@ encode_nonzero_receipt_type: %jump(encode_receipt_after_type) global encode_storage_value: - // stack: rlp_pos, value_ptr, retdest + // stack: rlp_pos, value_ptr, cur_len, retdest SWAP1 %mload_trie_data SWAP1 - // stack: rlp_pos, value, retdest + + // A storage value is a scalar, so we only need to add 1 to the trie data length. + SWAP2 %increment SWAP2 + + // stack: rlp_pos, value, cur_len, retdest // The YP says storage trie is a map "... to the RLP-encoded 256-bit integer values" // which seems to imply that this should be %encode_rlp_256. But %encode_rlp_scalar // causes the tests to pass, so it seems storage values should be treated as variable- // length after all. %doubly_encode_rlp_scalar - // stack: rlp_pos', retdest - SWAP1 + // stack: rlp_pos', cur_len, retdest + %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, cur_len) JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/load/load.asm b/evm/src/cpu/kernel/asm/mpt/load/load.asm deleted file mode 100644 index d787074b4f..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/load/load.asm +++ /dev/null @@ -1,173 +0,0 @@ -// Load all partial trie data from prover inputs. -global load_all_mpts: - // stack: retdest - // First set @GLOBAL_METADATA_TRIE_DATA_SIZE = 1. - // We don't want it to start at 0, as we use 0 as a null pointer. - PUSH 1 - %set_trie_data_size - - %load_mpt(mpt_load_state_trie_value) %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - %load_mpt(mpt_load_txn_trie_value) %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - %load_mpt(mpt_load_receipt_trie_value) %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - - // stack: retdest - JUMP - -// Load an MPT from prover inputs. -// Pre stack: load_value, retdest -// Post stack: node_ptr -global load_mpt: - // stack: load_value, retdest - PROVER_INPUT(mpt) - // stack: node_type, load_value, retdest - - DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(load_mpt_empty) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(load_mpt_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(load_mpt_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(load_mpt_leaf) - DUP1 %eq_const(@MPT_NODE_HASH) %jumpi(load_mpt_digest) - PANIC // Invalid node type - -load_mpt_empty: - // TRIE_DATA[0] = 0, and an empty node has type 0, so we can simply return the null pointer. - %stack (node_type, load_value, retdest) -> (retdest, 0) - JUMP - -load_mpt_branch: - // stack: node_type, load_value, retdest - %get_trie_data_size - // stack: node_ptr, node_type, load_value, retdest - SWAP1 %append_to_trie_data - // stack: node_ptr, load_value, retdest - // Save the offset of our 16 child pointers so we can write them later. - // Then advance our current trie pointer beyond them, so we can load the - // value and have it placed after our child pointers. - %get_trie_data_size - // stack: children_ptr, node_ptr, load_value, retdest - DUP1 %add_const(17) // Skip over 16 children plus the value pointer - // stack: end_of_branch_ptr, children_ptr, node_ptr, load_value, retdest - DUP1 %set_trie_data_size - // Now the top of the stack points to where the branch node will end and the - // value will begin, if there is a value. But we need to ask the prover if a - // value is present, and point to null if not. - // stack: end_of_branch_ptr, children_ptr, node_ptr, load_value, retdest - PROVER_INPUT(mpt) - // stack: is_value_present, end_of_branch_ptr, children_ptr, node_ptr, load_value, retdest - %jumpi(load_mpt_branch_value_present) - // There is no value present, so value_ptr = null. - %stack (end_of_branch_ptr) -> (0) - // stack: value_ptr, children_ptr, node_ptr, load_value, retdest - %jump(load_mpt_branch_after_load_value) -load_mpt_branch_value_present: - // stack: value_ptr, children_ptr, node_ptr, load_value, retdest - PUSH load_mpt_branch_after_load_value - DUP5 // load_value - JUMP -load_mpt_branch_after_load_value: - // stack: value_ptr, children_ptr, node_ptr, load_value, retdest - SWAP1 - // stack: children_ptr, value_ptr, node_ptr, load_value, retdest - - // Load the 16 children. - %rep 16 - DUP4 // load_value - %load_mpt - // stack: child_ptr, next_child_ptr_ptr, value_ptr, node_ptr, load_value, retdest - DUP2 - // stack: next_child_ptr_ptr, child_ptr, next_child_ptr_ptr, value_ptr, node_ptr, load_value, retdest - %mstore_trie_data - // stack: next_child_ptr_ptr, value_ptr, node_ptr, load_value, retdest - %increment - // stack: next_child_ptr_ptr, value_ptr, node_ptr, load_value, retdest - %endrep - - // stack: value_ptr_ptr, value_ptr, node_ptr, load_value, retdest - %mstore_trie_data - %stack (node_ptr, load_value, retdest) -> (retdest, node_ptr) - JUMP - -load_mpt_extension: - // stack: node_type, load_value, retdest - %get_trie_data_size - // stack: node_ptr, node_type, load_value, retdest - SWAP1 %append_to_trie_data - // stack: node_ptr, load_value, retdest - PROVER_INPUT(mpt) // read num_nibbles - %append_to_trie_data - PROVER_INPUT(mpt) // read packed_nibbles - %append_to_trie_data - // stack: node_ptr, load_value, retdest - - %get_trie_data_size - // stack: child_ptr_ptr, node_ptr, load_value, retdest - // Increment trie_data_size, to leave room for child_ptr_ptr, before we load our child. - DUP1 %increment %set_trie_data_size - %stack (child_ptr_ptr, node_ptr, load_value, retdest) - -> (load_value, load_mpt_extension_after_load_mpt, - child_ptr_ptr, retdest, node_ptr) - %jump(load_mpt) -load_mpt_extension_after_load_mpt: - // stack: child_ptr, child_ptr_ptr, retdest, node_ptr - SWAP1 %mstore_trie_data - // stack: retdest, node_ptr - JUMP - -load_mpt_leaf: - // stack: node_type, load_value, retdest - %get_trie_data_size - // stack: node_ptr, node_type, load_value, retdest - SWAP1 %append_to_trie_data - // stack: node_ptr, load_value, retdest - PROVER_INPUT(mpt) // read num_nibbles - %append_to_trie_data - PROVER_INPUT(mpt) // read packed_nibbles - %append_to_trie_data - // stack: node_ptr, load_value, retdest - // We save value_ptr_ptr = get_trie_data_size, then increment trie_data_size - // to skip over the slot for value_ptr_ptr. We will write to value_ptr_ptr - // after the load_value call. - %get_trie_data_size - // stack: value_ptr_ptr, node_ptr, load_value, retdest - DUP1 %increment - // stack: value_ptr, value_ptr_ptr, node_ptr, load_value, retdest - DUP1 %set_trie_data_size - // stack: value_ptr, value_ptr_ptr, node_ptr, load_value, retdest - %stack (value_ptr, value_ptr_ptr, node_ptr, load_value, retdest) - -> (load_value, load_mpt_leaf_after_load_value, - value_ptr_ptr, value_ptr, retdest, node_ptr) - JUMP -load_mpt_leaf_after_load_value: - // stack: value_ptr_ptr, value_ptr, retdest, node_ptr - %mstore_trie_data - // stack: retdest, node_ptr - JUMP - -load_mpt_digest: - // stack: node_type, load_value, retdest - %get_trie_data_size - // stack: node_ptr, node_type, load_value, retdest - SWAP1 %append_to_trie_data - // stack: node_ptr, load_value, retdest - PROVER_INPUT(mpt) // read digest - %append_to_trie_data - %stack (node_ptr, load_value, retdest) -> (retdest, node_ptr) - JUMP - -// Convenience macro to call load_mpt and return where we left off. -// Pre stack: load_value -// Post stack: node_ptr -%macro load_mpt - %stack (load_value) -> (load_value, %%after) - %jump(load_mpt) -%%after: -%endmacro - -// Convenience macro to call load_mpt and return where we left off. -// Pre stack: (empty) -// Post stack: node_ptr -%macro load_mpt(load_value) - PUSH %%after - PUSH $load_value - %jump(load_mpt) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/load/load_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/load/load_trie_specific.asm deleted file mode 100644 index 92471fd801..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/load/load_trie_specific.asm +++ /dev/null @@ -1,150 +0,0 @@ -global mpt_load_state_trie_value: - // stack: retdest - - // Load and append the nonce and balance. - PROVER_INPUT(mpt) %append_to_trie_data - PROVER_INPUT(mpt) %append_to_trie_data - - // Now increment the trie data size by 2, to leave room for our storage trie - // pointer and code hash fields, before calling load_mpt which will append - // our storage trie data. - %get_trie_data_size - // stack: storage_trie_ptr_ptr, retdest - DUP1 %add_const(2) - // stack: storage_trie_ptr, storage_trie_ptr_ptr, retdest - %set_trie_data_size - // stack: storage_trie_ptr_ptr, retdest - - %load_mpt(mpt_load_storage_trie_value) - // stack: storage_trie_ptr, storage_trie_ptr_ptr, retdest - DUP2 %mstore_trie_data - // stack: storage_trie_ptr_ptr, retdest - %increment - // stack: code_hash_ptr, retdest - PROVER_INPUT(mpt) - // stack: code_hash, code_hash_ptr, retdest - SWAP1 %mstore_trie_data - // stack: retdest - JUMP - -global mpt_load_txn_trie_value: - // stack: retdest - PROVER_INPUT(mpt) - // stack: rlp_len, retdest - // The first element is the rlp length - DUP1 %append_to_trie_data - PUSH 0 - -mpt_load_loop: - // stack: i, rlp_len, retdest - DUP2 DUP2 EQ %jumpi(mpt_load_end) - PROVER_INPUT(mpt) %append_to_trie_data - %increment - %jump(mpt_load_loop) - -mpt_load_end: - // stack: i, rlp_len, retdest - %pop2 - JUMP - -global mpt_load_receipt_trie_value: - // stack: retdest - // Load first byte. It is either `payload_len` or the transaction type. - PROVER_INPUT(mpt) DUP1 %append_to_trie_data - // If the first byte is less than 3, then it is the transaction type, equal to either 1 or 2. - // In that case, we still need to load the payload length. - %lt_const(3) %jumpi(mpt_load_payload_len) - -mpt_load_after_type: - // Load status. - PROVER_INPUT(mpt) %append_to_trie_data - // Load cum_gas_used. - PROVER_INPUT(mpt) %append_to_trie_data - // Load bloom. - %rep 256 - PROVER_INPUT(mpt) %append_to_trie_data - %endrep - // Load logs_payload_len. - PROVER_INPUT(mpt) %append_to_trie_data - // Load num_logs. - PROVER_INPUT(mpt) - DUP1 - %append_to_trie_data - // stack: num_logs, retdest - // Load logs. - PUSH 0 - -mpt_load_receipt_trie_value_logs_loop: - // stack: i, num_logs, retdest - DUP2 DUP2 EQ - // stack: i == num_logs, i, num_logs, retdest - %jumpi(mpt_load_receipt_trie_value_end) - // stack: i, num_logs, retdest - // Load log_payload_len. - PROVER_INPUT(mpt) %append_to_trie_data - // Load address. - PROVER_INPUT(mpt) %append_to_trie_data - // Load num_topics. - PROVER_INPUT(mpt) - DUP1 - %append_to_trie_data - // stack: num_topics, i, num_logs, retdest - // Load topics. - PUSH 0 - -mpt_load_receipt_trie_value_topics_loop: - // stack: j, num_topics, i, num_logs, retdest - DUP2 DUP2 EQ - // stack: j == num_topics, j, num_topics, i, num_logs, retdest - %jumpi(mpt_load_receipt_trie_value_topics_end) - // stack: j, num_topics, i, num_logs, retdest - // Load topic. - PROVER_INPUT(mpt) %append_to_trie_data - %increment - %jump(mpt_load_receipt_trie_value_topics_loop) - -mpt_load_receipt_trie_value_topics_end: - // stack: num_topics, num_topics, i, num_logs, retdest - %pop2 - // stack: i, num_logs, retdest - // Load data_len. - PROVER_INPUT(mpt) - DUP1 - %append_to_trie_data - // stack: data_len, i, num_logs, retdest - // Load data. - PUSH 0 - -mpt_load_receipt_trie_value_data_loop: - // stack: j, data_len, i, num_logs, retdest - DUP2 DUP2 EQ - // stack: j == data_len, j, data_len, i, num_logs, retdest - %jumpi(mpt_load_receipt_trie_value_data_end) - // stack: j, data_len, i, num_logs, retdest - // Load data byte. - PROVER_INPUT(mpt) %append_to_trie_data - %increment - %jump(mpt_load_receipt_trie_value_data_loop) - -mpt_load_receipt_trie_value_data_end: - // stack: data_len, data_len, i, num_logs, retdest - %pop2 - %increment - %jump(mpt_load_receipt_trie_value_logs_loop) - -mpt_load_receipt_trie_value_end: - // stack: num_logs, num_logs, retdest - %pop2 - JUMP - -mpt_load_payload_len: - // stack: retdest - PROVER_INPUT(mpt) %append_to_trie_data - %jump(mpt_load_after_type) - -global mpt_load_storage_trie_value: - // stack: retdest - PROVER_INPUT(mpt) - %append_to_trie_data - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index e72b40783b..f187a4804a 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -275,6 +275,12 @@ impl<'a> Interpreter<'a> { code.into_iter().map(U256::from).collect(); } + pub(crate) fn set_memory_multi_addresses(&mut self, addrs: &[(MemoryAddress, U256)]) { + for &(addr, val) in addrs { + self.generation_state.memory.set(addr, val); + } + } + pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] .content diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 2782e71f93..dadfaf4ba0 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -13,12 +13,57 @@ use crate::cpu::kernel::constants::context_metadata::ContextMetadata::{self, Gas use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::tests::mpt::nibbles_64; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::generation::mpt::{load_all_mpts, AccountRlp}; use crate::generation::TrieInputs; use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; use crate::Node; +pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieInputs) { + // Load all MPTs. + let (trie_root_ptrs, trie_data) = + load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); + + let state_addr = MemoryAddress::new( + 0, + Segment::GlobalMetadata, + GlobalMetadata::StateTrieRoot as usize, + ); + + let txn_addr = MemoryAddress::new( + 0, + Segment::GlobalMetadata, + GlobalMetadata::TransactionTrieRoot as usize, + ); + let receipts_addr = MemoryAddress::new( + 0, + Segment::GlobalMetadata, + GlobalMetadata::ReceiptTrieRoot as usize, + ); + let len_addr = MemoryAddress::new( + 0, + Segment::GlobalMetadata, + GlobalMetadata::TrieDataSize as usize, + ); + + let to_set = [ + (state_addr, trie_root_ptrs.state_root_ptr.into()), + (txn_addr, trie_root_ptrs.txn_root_ptr.into()), + (receipts_addr, trie_root_ptrs.receipt_root_ptr.into()), + (len_addr, trie_data.len().into()), + ]; + + interpreter.set_memory_multi_addresses(&to_set); + + for (i, data) in trie_data.iter().enumerate() { + let trie_addr = MemoryAddress::new(0, Segment::TrieData, i); + interpreter + .generation_state + .memory + .set(trie_addr, data.into()); + } +} + // Test account with a given code hash. fn test_account(code: &[u8]) -> AccountRlp { AccountRlp { @@ -42,20 +87,12 @@ fn prepare_interpreter( address: Address, account: &AccountRlp, ) -> Result<()> { - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; let mut state_trie: HashedPartialTrie = Default::default(); let trie_inputs = Default::default(); - interpreter.generation_state.registers.program_counter = load_all_mpts; - interpreter.push(0xDEADBEEFu32.into()); - - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![]); + initialize_mpts(interpreter, &trie_inputs); let k = nibbles_64(U256::from_big_endian( keccak(address.to_fixed_bytes()).as_bytes(), @@ -93,15 +130,16 @@ fn prepare_interpreter( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initial length of the trie data segment, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack after hashing, found {:?}", + 2, + "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let hash = H256::from_uint(&interpreter.stack()[1]); state_trie.insert(k, rlp::encode(account).to_vec()); let expected_state_trie_hash = state_trie.hash(); @@ -125,6 +163,7 @@ fn test_extcodesize() -> Result<()> { // Test `extcodesize` interpreter.generation_state.registers.program_counter = extcodesize; interpreter.pop(); + interpreter.pop(); assert!(interpreter.stack().is_empty()); interpreter.push(0xDEADBEEFu32.into()); interpreter.push(U256::from_big_endian(address.as_bytes())); @@ -173,6 +212,7 @@ fn test_extcodecopy() -> Result<()> { // Test `extcodecopy` interpreter.generation_state.registers.program_counter = extcodecopy; interpreter.pop(); + interpreter.pop(); assert!(interpreter.stack().is_empty()); interpreter.push(size.into()); interpreter.push(offset.into()); @@ -207,15 +247,7 @@ fn prepare_interpreter_all_accounts( code: &[u8], ) -> Result<()> { // Load all MPTs. - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - interpreter.generation_state.registers.program_counter = load_all_mpts; - interpreter.push(0xDEADBEEFu32.into()); - - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + initialize_mpts(interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Switch context and initialize memory with the data we need for the tests. @@ -311,16 +343,17 @@ fn sstore() -> Result<()> { interpreter.set_is_kernel(true); interpreter.set_context(0); interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initia length of the trie data segment, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack after hashing, found {:?}", + 2, + "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let hash = H256::from_uint(&interpreter.stack()[1]); let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); expected_state_trie_after.insert(addr_nibbles, rlp::encode(&account_after).to_vec()); @@ -389,16 +422,26 @@ fn sload() -> Result<()> { interpreter.set_is_kernel(true); interpreter.set_context(0); interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initia length of the trie data segment, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack after hashing, found {:?}", + 2, + "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let trie_data_segment_len = interpreter.stack()[0]; + assert_eq!( + trie_data_segment_len, + interpreter + .get_memory_segment(Segment::TrieData) + .len() + .into() + ); + + let hash = H256::from_uint(&interpreter.stack()[1]); let expected_state_trie_hash = state_trie_before.hash(); assert_eq!(hash, expected_state_trie_hash); diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 048dad86d8..a48bd450bc 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -12,7 +12,8 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp, LegacyReceiptRlp}; +use crate::cpu::kernel::tests::account_code::initialize_mpts; +use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; use crate::memory::segments::Segment; @@ -28,14 +29,7 @@ fn prepare_interpreter( transaction: &[u8], contract_code: HashMap>, ) { - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - interpreter.generation_state.registers.program_counter = load_all_mpts; - interpreter.push(0xDEADBEEFu32.into()); - - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs).expect("Invalid MPT data."); - interpreter.run().expect("MPT loading failed."); + initialize_mpts(interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Set necessary `GlobalMetadata`. diff --git a/evm/src/cpu/kernel/tests/balance.rs b/evm/src/cpu/kernel/tests/balance.rs index 40214405c7..d7f92f3dfe 100644 --- a/evm/src/cpu/kernel/tests/balance.rs +++ b/evm/src/cpu/kernel/tests/balance.rs @@ -7,8 +7,9 @@ use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::nibbles_64; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::generation::mpt::AccountRlp; use crate::Node; // Test account with a given code hash. @@ -28,19 +29,12 @@ fn prepare_interpreter( address: Address, account: &AccountRlp, ) -> Result<()> { - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; let mut state_trie: HashedPartialTrie = Default::default(); let trie_inputs = Default::default(); - interpreter.generation_state.registers.program_counter = load_all_mpts; - interpreter.push(0xDEADBEEFu32.into()); - - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + initialize_mpts(interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let k = nibbles_64(U256::from_big_endian( @@ -79,15 +73,16 @@ fn prepare_interpreter( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initial trie data segment size, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack after hashing, found {:?}", + 2, + "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let hash = H256::from_uint(&interpreter.stack()[1]); state_trie.insert(k, rlp::encode(account).to_vec()); let expected_state_trie_hash = state_trie.hash(); @@ -110,6 +105,7 @@ fn test_balance() -> Result<()> { // Test `balance` interpreter.generation_state.registers.program_counter = KERNEL.global_labels["balance"]; interpreter.pop(); + interpreter.pop(); assert!(interpreter.stack().is_empty()); interpreter.push(0xDEADBEEFu32.into()); interpreter.push(U256::from_big_endian(address.as_bytes())); diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs index 074eea26ef..edce3e58c5 100644 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm/src/cpu/kernel/tests/mpt/delete.rs @@ -6,8 +6,9 @@ use ethereum_types::{BigEndianHash, H256}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::{nibbles_64, test_account_1_rlp, test_account_2}; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::generation::mpt::AccountRlp; use crate::generation::TrieInputs; use crate::Node; @@ -65,16 +66,14 @@ fn test_state_trie( receipts_trie: Default::default(), storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; let mpt_delete = KERNEL.global_labels["mpt_delete"]; let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs).map_err(|_| anyhow!("Invalid MPT data"))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Next, execute mpt_insert_state_trie. @@ -120,6 +119,7 @@ fn test_state_trie( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initial length of the trie data segment, unused. interpreter.run()?; let state_trie_hash = H256::from_uint(&interpreter.pop()); diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs index 05077a94da..2902e56609 100644 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm/src/cpu/kernel/tests/mpt/hash.rs @@ -4,8 +4,8 @@ use ethereum_types::{BigEndianHash, H256}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1_rlp, test_account_2_rlp}; -use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::TrieInputs; use crate::Node; @@ -108,28 +108,27 @@ fn mpt_hash_branch_to_leaf() -> Result<()> { } fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs).map_err(|_| anyhow!("Invalid MPT data"))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initial length of the trie data segment, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack, found {:?}", + 2, + "Expected 2 items on stack, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let hash = H256::from_uint(&interpreter.stack()[1]); let expected_state_trie_hash = trie_inputs.state_trie.hash(); assert_eq!(hash, expected_state_trie_hash); diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index 6fd95a30b9..e68e7d2fd0 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -6,10 +6,11 @@ use ethereum_types::{BigEndianHash, H256}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::{ nibbles_64, nibbles_count, test_account_1_rlp, test_account_2, }; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::generation::mpt::AccountRlp; use crate::generation::TrieInputs; use crate::Node; @@ -168,15 +169,13 @@ fn test_state_trie( receipts_trie: Default::default(), storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs).map_err(|_| anyhow!("Invalid MPT data"))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Next, execute mpt_insert_state_trie. @@ -212,15 +211,16 @@ fn test_state_trie( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(1.into()); // Initial length of the trie data segment, unused. interpreter.run()?; assert_eq!( interpreter.stack().len(), - 1, - "Expected 1 item on stack after hashing, found {:?}", + 2, + "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[0]); + let hash = H256::from_uint(&interpreter.stack()[1]); state_trie.insert(k, rlp::encode(&account).to_vec()); let expected_state_trie_hash = state_trie.hash(); diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs index ae0bfa3bc8..68ba209d8d 100644 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ b/evm/src/cpu/kernel/tests/mpt/load.rs @@ -10,8 +10,8 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; -use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::TrieInputs; use crate::Node; @@ -24,17 +24,13 @@ fn load_all_mpts_empty() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); - assert_eq!(interpreter.get_trie_data(), vec![]); + // We need to have the first element in `TrieData` be 0. + assert_eq!(interpreter.get_trie_data(), vec![0.into()]); assert_eq!( interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot), @@ -65,14 +61,9 @@ fn load_all_mpts_leaf() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let type_leaf = U256::from(PartialTrieType::Leaf as u32); @@ -116,14 +107,9 @@ fn load_all_mpts_hash() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let type_hash = U256::from(PartialTrieType::Hash as u32); @@ -159,14 +145,9 @@ fn load_all_mpts_empty_branch() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let type_branch = U256::from(PartialTrieType::Branch as u32); @@ -216,14 +197,9 @@ fn load_all_mpts_ext_to_leaf() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let type_extension = U256::from(PartialTrieType::Extension as u32); @@ -255,8 +231,6 @@ fn load_all_mpts_ext_to_leaf() -> Result<()> { #[test] fn load_mpt_txn_trie() -> Result<()> { - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - let txn = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b").to_vec(); let trie_inputs = TrieInputs { @@ -269,12 +243,9 @@ fn load_mpt_txn_trie() -> Result<()> { storage_tries: vec![], }; - let initial_stack = vec![0xDEADBEEFu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); let mut expected_trie_data = vec![ diff --git a/evm/src/cpu/kernel/tests/mpt/read.rs b/evm/src/cpu/kernel/tests/mpt/read.rs index f9ae94f03b..67fea585d3 100644 --- a/evm/src/cpu/kernel/tests/mpt/read.rs +++ b/evm/src/cpu/kernel/tests/mpt/read.rs @@ -4,8 +4,8 @@ use ethereum_types::BigEndianHash; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; -use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::TrieInputs; #[test] @@ -17,15 +17,11 @@ fn mpt_read() -> Result<()> { storage_tries: vec![], }; - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_read = KERNEL.global_labels["mpt_read"]; - let initial_stack = vec![0xdeadbeefu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); // Now, execute mpt_read on the state trie. diff --git a/evm/src/cpu/kernel/tests/receipt.rs b/evm/src/cpu/kernel/tests/receipt.rs index e603561f50..503a4a6a29 100644 --- a/evm/src/cpu/kernel/tests/receipt.rs +++ b/evm/src/cpu/kernel/tests/receipt.rs @@ -8,7 +8,8 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; use crate::cpu::kernel::interpreter::Interpreter; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, LegacyReceiptRlp, LogRlp}; +use crate::cpu::kernel::tests::account_code::initialize_mpts; +use crate::generation::mpt::{LegacyReceiptRlp, LogRlp}; use crate::memory::segments::Segment; #[test] @@ -126,7 +127,7 @@ fn test_receipt_encoding() -> Result<()> { // Get the expected RLP encoding. let expected_rlp = rlp::encode(&rlp::encode(&receipt_1)); - let initial_stack: Vec = vec![retdest, 0.into(), 0.into()]; + let initial_stack: Vec = vec![retdest, 0.into(), 0.into(), 0.into()]; let mut interpreter = Interpreter::new_with_kernel(encode_receipt, initial_stack); // Write data to memory. @@ -338,7 +339,6 @@ fn test_mpt_insert_receipt() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let trie_inputs = Default::default(); - let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let mpt_insert = KERNEL.global_labels["mpt_insert_receipt_trie"]; let num_topics = 3; // Both transactions have the same number of topics. let payload_len = 423; // Total payload length for each receipt. @@ -409,11 +409,8 @@ fn test_mpt_insert_receipt() -> Result<()> { // First, we load all mpts. let initial_stack: Vec = vec![retdest]; - let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); - interpreter.generation_state.mpt_prover_inputs = - all_mpt_prover_inputs_reversed(&trie_inputs) - .map_err(|err| anyhow!("Invalid MPT data: {:?}", err))?; - interpreter.run()?; + let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + initialize_mpts(&mut interpreter, &trie_inputs); // If TrieData is empty, we need to push 0 because the first value is always 0. let mut cur_trie_data = interpreter.get_memory_segment(Segment::TrieData); @@ -514,9 +511,10 @@ fn test_mpt_insert_receipt() -> Result<()> { let mpt_hash_receipt = KERNEL.global_labels["mpt_hash_receipt_trie"]; interpreter.generation_state.registers.program_counter = mpt_hash_receipt; interpreter.push(retdest); + interpreter.push(1.into()); // Initial length of the trie data segment, unused. interpreter.run()?; assert_eq!( - interpreter.stack()[0], + interpreter.stack()[1], U256::from(hex!( "da46cdd329bfedace32da95f2b344d314bc6f55f027d65f9f4ac04ee425e1f98" )) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index ddb1fe5d36..db4a40476b 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -33,6 +33,7 @@ pub(crate) mod rlp; pub(crate) mod state; mod trie_extractor; +use self::mpt::{load_all_mpts, TrieRootPtrs}; use crate::witness::util::mem_write_log; /// Inputs needed for trace generation. @@ -195,11 +196,6 @@ pub fn generate_traces, const D: usize>( timed!(timing, "simulate CPU", simulate_cpu(&mut state)?); - assert!( - state.mpt_prover_inputs.is_empty(), - "All MPT data should have been consumed" - ); - log::info!( "Trace lengths (before padding): {:?}", state.traces.get_lengths() @@ -220,6 +216,7 @@ pub fn generate_traces, const D: usize>( let gas_used_after = read_metadata(GlobalMetadata::BlockGasUsedAfter); let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); + let trie_root_ptrs = state.trie_root_ptrs; let extra_block_data = ExtraBlockData { genesis_state_trie_root: inputs.genesis_state_trie_root, txn_number_before: inputs.txn_number_before, diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index 8d944b2728..a2abe1b28a 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -9,9 +9,13 @@ use keccak_hash::keccak; use rlp::{Decodable, DecoderError, Encodable, PayloadInfo, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::generation::TrieInputs; +use crate::memory::segments::Segment; +use crate::util::h2u; use crate::witness::errors::{ProgramError, ProverInputError}; +use crate::witness::memory::MemoryAddress; use crate::Node; #[derive(RlpEncodable, RlpDecodable, Debug)] @@ -22,6 +26,13 @@ pub struct AccountRlp { pub code_hash: H256, } +#[derive(Clone, Debug)] +pub struct TrieRootPtrs { + pub state_root_ptr: usize, + pub txn_root_ptr: usize, + pub receipt_root_ptr: usize, +} + impl Default for AccountRlp { fn default() -> Self { Self { @@ -59,14 +70,6 @@ impl LegacyReceiptRlp { } } -pub(crate) fn all_mpt_prover_inputs_reversed( - trie_inputs: &TrieInputs, -) -> Result, ProgramError> { - let mut inputs = all_mpt_prover_inputs(trie_inputs)?; - inputs.reverse(); - Ok(inputs) -} - pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { let txn_type = match rlp.first().ok_or(ProgramError::InvalidRlp)? { 1 => 1, @@ -111,114 +114,114 @@ pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { Ok(parsed_receipt) } -/// Generate prover inputs for the initial MPT data, in the format expected by `mpt/load.asm`. -pub(crate) fn all_mpt_prover_inputs(trie_inputs: &TrieInputs) -> Result, ProgramError> { - let mut prover_inputs = vec![]; - - let storage_tries_by_state_key = trie_inputs - .storage_tries - .iter() - .map(|(hashed_address, storage_trie)| { - let key = Nibbles::from_bytes_be(hashed_address.as_bytes()) - .expect("An H256 is 32 bytes long"); - (key, storage_trie) - }) - .collect(); - - mpt_prover_inputs_state_trie( - &trie_inputs.state_trie, - empty_nibbles(), - &mut prover_inputs, - &storage_tries_by_state_key, - )?; - - mpt_prover_inputs(&trie_inputs.transactions_trie, &mut prover_inputs, &|rlp| { - let mut parsed_txn = vec![U256::from(rlp.len())]; - parsed_txn.extend(rlp.iter().copied().map(U256::from)); - Ok(parsed_txn) - })?; - mpt_prover_inputs( - &trie_inputs.receipts_trie, - &mut prover_inputs, - &parse_receipts, - )?; +fn parse_storage_value(value_rlp: &[u8]) -> Result, ProgramError> { + let value: U256 = rlp::decode(value_rlp).map_err(|_| ProgramError::InvalidRlp)?; + Ok(vec![value]) +} - Ok(prover_inputs) +const fn empty_nibbles() -> Nibbles { + Nibbles { + count: 0, + packed: U512::zero(), + } } -/// Given a trie, generate the prover input data for that trie. In essence, this serializes a trie -/// into a `U256` array, in a simple format which the kernel understands. For example, a leaf node -/// is serialized as `(TYPE_LEAF, key, value)`, where key is a `(nibbles, depth)` pair and `value` -/// is a variable-length structure which depends on which trie we're dealing with. -pub(crate) fn mpt_prover_inputs( +fn load_mpt( trie: &HashedPartialTrie, - prover_inputs: &mut Vec, + trie_data: &mut Vec, parse_value: &F, -) -> Result<(), ProgramError> +) -> Result where F: Fn(&[u8]) -> Result, ProgramError>, { - prover_inputs.push((PartialTrieType::of(trie) as u32).into()); + let node_ptr = trie_data.len(); + let type_of_trie = PartialTrieType::of(trie) as u32; + if type_of_trie > 0 { + trie_data.push(type_of_trie.into()); + } match trie.deref() { - Node::Empty => Ok(()), + Node::Empty => Ok(0), Node::Hash(h) => { - prover_inputs.push(U256::from_big_endian(h.as_bytes())); - Ok(()) + trie_data.push(h2u(*h)); + + Ok(node_ptr) } Node::Branch { children, value } => { + // First, set children pointers to 0. + let first_child_ptr = trie_data.len(); + trie_data.extend(vec![U256::zero(); 16]); + // Then, set value. if value.is_empty() { - prover_inputs.push(U256::zero()); // value_present = 0 + trie_data.push(U256::zero()); } else { let parsed_value = parse_value(value)?; - prover_inputs.push(U256::one()); // value_present = 1 - prover_inputs.extend(parsed_value); + trie_data.push((trie_data.len() + 1).into()); + trie_data.extend(parsed_value); } - for child in children { - mpt_prover_inputs(child, prover_inputs, parse_value)?; + + // Now, load all children and update their pointers. + for (i, child) in children.iter().enumerate() { + let child_ptr = load_mpt(child, trie_data, parse_value)?; + trie_data[first_child_ptr + i] = child_ptr.into(); } - Ok(()) + Ok(node_ptr) } + Node::Extension { nibbles, child } => { - prover_inputs.push(nibbles.count.into()); - prover_inputs.push( + trie_data.push(nibbles.count.into()); + trie_data.push( nibbles .try_into_u256() .map_err(|_| ProgramError::IntegerTooLarge)?, ); - mpt_prover_inputs(child, prover_inputs, parse_value) + trie_data.push((trie_data.len() + 1).into()); + + let child_ptr = load_mpt(child, trie_data, parse_value)?; + if child_ptr == 0 { + trie_data.push(0.into()); + } + + Ok(node_ptr) } Node::Leaf { nibbles, value } => { - prover_inputs.push(nibbles.count.into()); - prover_inputs.push( + trie_data.push(nibbles.count.into()); + trie_data.push( nibbles .try_into_u256() .map_err(|_| ProgramError::IntegerTooLarge)?, ); + + // Set `value_ptr_ptr`. + trie_data.push((trie_data.len() + 1).into()); + let leaf = parse_value(value)?; - prover_inputs.extend(leaf); + trie_data.extend(leaf); - Ok(()) + Ok(node_ptr) } } } -/// Like `mpt_prover_inputs`, but for the state trie, which is a bit unique since each value -/// leads to a storage trie which we recursively traverse. -pub(crate) fn mpt_prover_inputs_state_trie( +fn load_state_trie( trie: &HashedPartialTrie, key: Nibbles, - prover_inputs: &mut Vec, + trie_data: &mut Vec, storage_tries_by_state_key: &HashMap, -) -> Result<(), ProgramError> { - prover_inputs.push((PartialTrieType::of(trie) as u32).into()); +) -> Result { + let node_ptr = trie_data.len(); + let type_of_trie = PartialTrieType::of(trie) as u32; + if type_of_trie > 0 { + trie_data.push(type_of_trie.into()); + } match trie.deref() { - Node::Empty => Ok(()), + Node::Empty => Ok(0), Node::Hash(h) => { - prover_inputs.push(U256::from_big_endian(h.as_bytes())); - Ok(()) + trie_data.push(h2u(*h)); + + Ok(node_ptr) } Node::Branch { children, value } => { if !value.is_empty() { @@ -226,37 +229,43 @@ pub(crate) fn mpt_prover_inputs_state_trie( ProverInputError::InvalidMptInput, )); } - prover_inputs.push(U256::zero()); // value_present = 0 + // First, set children pointers to 0. + let first_child_ptr = trie_data.len(); + trie_data.extend(vec![U256::zero(); 16]); + // Then, set value pointer to 0. + trie_data.push(U256::zero()); + // Now, load all children and update their pointers. for (i, child) in children.iter().enumerate() { let extended_key = key.merge_nibbles(&Nibbles { count: 1, packed: i.into(), }); - mpt_prover_inputs_state_trie( - child, - extended_key, - prover_inputs, - storage_tries_by_state_key, - )?; + let child_ptr = + load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; + + trie_data[first_child_ptr + i] = child_ptr.into(); } - Ok(()) + Ok(node_ptr) } Node::Extension { nibbles, child } => { - prover_inputs.push(nibbles.count.into()); - prover_inputs.push( + trie_data.push(nibbles.count.into()); + trie_data.push( nibbles .try_into_u256() .map_err(|_| ProgramError::IntegerTooLarge)?, ); + // Set `value_ptr_ptr`. + trie_data.push((trie_data.len() + 1).into()); let extended_key = key.merge_nibbles(nibbles); - mpt_prover_inputs_state_trie( - child, - extended_key, - prover_inputs, - storage_tries_by_state_key, - ) + let child_ptr = + load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; + if child_ptr == 0 { + trie_data.push(0.into()); + } + + Ok(node_ptr) } Node::Leaf { nibbles, value } => { let account: AccountRlp = rlp::decode(value).map_err(|_| ProgramError::InvalidRlp)?; @@ -275,34 +284,69 @@ pub(crate) fn mpt_prover_inputs_state_trie( .unwrap_or(&storage_hash_only); assert_eq!(storage_trie.hash(), storage_root, - "In TrieInputs, an account's storage_root didn't match the associated storage trie hash"); + "In TrieInputs, an account's storage_root didn't match the associated storage trie hash"); - prover_inputs.push(nibbles.count.into()); - prover_inputs.push( + trie_data.push(nibbles.count.into()); + trie_data.push( nibbles .try_into_u256() .map_err(|_| ProgramError::IntegerTooLarge)?, ); - prover_inputs.push(nonce); - prover_inputs.push(balance); - mpt_prover_inputs(storage_trie, prover_inputs, &parse_storage_value)?; - prover_inputs.push(code_hash.into_uint()); + // Set `value_ptr_ptr`. + trie_data.push((trie_data.len() + 1).into()); + + trie_data.push(nonce); + trie_data.push(balance); + // Storage trie ptr. + let storage_ptr_ptr = trie_data.len(); + trie_data.push((trie_data.len() + 2).into()); + trie_data.push(code_hash.into_uint()); + let storage_ptr = load_mpt(storage_trie, trie_data, &parse_storage_value)?; + if storage_ptr == 0 { + trie_data[storage_ptr_ptr] = 0.into(); + } - Ok(()) + Ok(node_ptr) } } } -fn parse_storage_value(value_rlp: &[u8]) -> Result, ProgramError> { - let value: U256 = rlp::decode(value_rlp).map_err(|_| ProgramError::InvalidRlp)?; - Ok(vec![value]) -} +pub(crate) fn load_all_mpts( + trie_inputs: &TrieInputs, +) -> Result<(TrieRootPtrs, Vec), ProgramError> { + let mut trie_data = vec![U256::zero()]; + let storage_tries_by_state_key = trie_inputs + .storage_tries + .iter() + .map(|(hashed_address, storage_trie)| { + let key = Nibbles::from_bytes_be(hashed_address.as_bytes()) + .expect("An H256 is 32 bytes long"); + (key, storage_trie) + }) + .collect(); -const fn empty_nibbles() -> Nibbles { - Nibbles { - count: 0, - packed: U512::zero(), - } + let state_root_ptr = load_state_trie( + &trie_inputs.state_trie, + empty_nibbles(), + &mut trie_data, + &storage_tries_by_state_key, + )?; + + let txn_root_ptr = load_mpt(&trie_inputs.transactions_trie, &mut trie_data, &|rlp| { + let mut parsed_txn = vec![U256::from(rlp.len())]; + parsed_txn.extend(rlp.iter().copied().map(U256::from)); + Ok(parsed_txn) + })?; + + let receipt_root_ptr = load_mpt(&trie_inputs.receipts_trie, &mut trie_data, &parse_receipts)?; + + let trie_root_ptrs = TrieRootPtrs { + state_root_ptr, + txn_root_ptr, + receipt_root_ptr, + }; + + Ok((trie_root_ptrs, trie_data)) } pub mod transaction_testing { diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 37176f7604..6d40193f98 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -36,10 +36,10 @@ impl GenerationState { pub(crate) fn prover_input(&mut self, input_fn: &ProverInputFn) -> Result { match input_fn.0[0].as_str() { "no_txn" => self.no_txn(), + "trie_ptr" => self.run_trie_ptr(input_fn), "ff" => self.run_ff(input_fn), "sf" => self.run_sf(input_fn), "ffe" => self.run_ffe(input_fn), - "mpt" => self.run_mpt(), "rlp" => self.run_rlp(), "current_hash" => self.run_current_hash(), "account_code" => self.run_account_code(input_fn), @@ -54,6 +54,16 @@ impl GenerationState { Ok(U256::from(self.inputs.signed_txn.is_none() as u8)) } + fn run_trie_ptr(&mut self, input_fn: &ProverInputFn) -> Result { + let trie = input_fn.0[1].as_str(); + match trie { + "state" => Ok(U256::from(self.trie_root_ptrs.state_root_ptr)), + "txn" => Ok(U256::from(self.trie_root_ptrs.txn_root_ptr)), + "receipt" => Ok(U256::from(self.trie_root_ptrs.receipt_root_ptr)), + _ => Err(ProgramError::ProverInputError(InvalidInput)), + } + } + /// Finite field operations. fn run_ff(&self, input_fn: &ProverInputFn) -> Result { let field = EvmField::from_str(input_fn.0[1].as_str()) @@ -109,13 +119,6 @@ impl GenerationState { Ok(field.field_extension_inverse(n, f)) } - /// MPT data. - fn run_mpt(&mut self) -> Result { - self.mpt_prover_inputs - .pop() - .ok_or(ProgramError::ProverInputError(OutOfMptData)) - } - /// RLP data. fn run_rlp(&mut self) -> Result { self.rlp_prover_inputs diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 938e83b8b1..89ff0c5af9 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -2,11 +2,14 @@ use std::collections::HashMap; use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; +use plonky2::field::extension::Extendable; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use super::mpt::{load_all_mpts, TrieRootPtrs}; +use super::TrieInputs; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::GenerationInputs; use crate::memory::segments::Segment; @@ -29,10 +32,6 @@ pub(crate) struct GenerationState { pub(crate) memory: MemoryState, pub(crate) traces: Traces, - /// Prover inputs containing MPT data, in reverse order so that the next input can be obtained - /// via `pop()`. - pub(crate) mpt_prover_inputs: Vec, - /// Prover inputs containing RLP data, in reverse order so that the next input can be obtained /// via `pop()`. pub(crate) rlp_prover_inputs: Vec, @@ -48,9 +47,20 @@ pub(crate) struct GenerationState { /// inputs are obtained in big-endian order via `pop()`). Contains both the remainder and the /// quotient, in that order. pub(crate) bignum_modmul_result_limbs: Vec, + + /// Pointers, within the `TrieData` segment, of the three MPTs. + pub(crate) trie_root_ptrs: TrieRootPtrs, } impl GenerationState { + fn preinitialize_mpts(&mut self, trie_inputs: &TrieInputs) -> TrieRootPtrs { + let (trie_roots_ptrs, trie_data) = + load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); + + self.memory.contexts[0].segments[Segment::TrieData as usize].content = trie_data; + + trie_roots_ptrs + } pub(crate) fn new(inputs: GenerationInputs, kernel_code: &[u8]) -> Result { log::debug!("Input signed_txn: {:?}", &inputs.signed_txn); log::debug!("Input state_trie: {:?}", &inputs.tries.state_trie); @@ -61,23 +71,31 @@ impl GenerationState { log::debug!("Input receipts_trie: {:?}", &inputs.tries.receipts_trie); log::debug!("Input storage_tries: {:?}", &inputs.tries.storage_tries); log::debug!("Input contract_code: {:?}", &inputs.contract_code); - let mpt_prover_inputs = all_mpt_prover_inputs_reversed(&inputs.tries)?; + let rlp_prover_inputs = - all_rlp_prover_inputs_reversed(inputs.signed_txn.as_ref().unwrap_or(&vec![])); + all_rlp_prover_inputs_reversed(inputs.clone().signed_txn.as_ref().unwrap_or(&vec![])); let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); let bignum_modmul_result_limbs = Vec::new(); - Ok(Self { - inputs, + let mut state = Self { + inputs: inputs.clone(), registers: Default::default(), memory: MemoryState::new(kernel_code), traces: Traces::default(), - mpt_prover_inputs, rlp_prover_inputs, withdrawal_prover_inputs, state_key_to_address: HashMap::new(), bignum_modmul_result_limbs, - }) + trie_root_ptrs: TrieRootPtrs { + state_root_ptr: 0, + txn_root_ptr: 0, + receipt_root_ptr: 0, + }, + }; + let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); + + state.trie_root_ptrs = trie_root_ptrs; + Ok(state) } /// Updates `program_counter`, and potentially adds some extra handling if we're jumping to a diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 3d36a33a08..4dfdc913f1 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -13,6 +13,7 @@ use plonky2::util::timing::TimingTree; use plonky2::util::transpose; use plonky2_maybe_rayon::*; +use super::segments::Segment; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; @@ -349,7 +350,11 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark anyhow::Result<()> { // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 14..16, 16..18, 14..15, 10..11, 12..13, 19..20], + &[16..17, 14..16, 16..18, 14..15, 9..10, 12..13, 19..20], &config, ); From 170ce5f27c9e4aa05ebd97bf96612a88c3c9257c Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Thu, 7 Dec 2023 12:49:46 -0500 Subject: [PATCH 005/175] Preinitialize all code segments (#1409) * Preinitialize all code segments * Add zero writes after code_size * Use preinitializing for extcodesize * Fix gas calculation * Extend logic to extcodecopy * Apply comments --- evm/spec/tables/memory.tex | 4 + evm/spec/zkevm.pdf | Bin 292595 -> 295898 bytes evm/src/cpu/kernel/asm/account_code.asm | 77 ++++++++++---------- evm/src/cpu/kernel/asm/core/call.asm | 4 +- evm/src/cpu/kernel/asm/core/process_txn.asm | 7 +- evm/src/cpu/kernel/asm/memory/syscalls.asm | 10 ++- evm/src/generation/prover_input.rs | 52 ++++++------- 7 files changed, 76 insertions(+), 78 deletions(-) diff --git a/evm/spec/tables/memory.tex b/evm/spec/tables/memory.tex index e3a19cab8e..883134d624 100644 --- a/evm/spec/tables/memory.tex +++ b/evm/spec/tables/memory.tex @@ -76,5 +76,9 @@ \subsubsection{Memory initialization} \begin{itemize} \item The read-only kernel code (in segment 0, context 0) is initialized with its correct values. It's checked by hashing the segment and verifying that the hash value matches a verifier-provided one. + \item The code segment (segment 0) in other contexts is initialized with externally-provided account code, then checked against the account code hash. +If the code is meant to be executed, there is a soundness concern: if the code is malformed and ends with an incomplete PUSH, then the missing bytes must +be 0 accordingly to the Ethereum specs. To prevent the issue, we manually write 33 zeros (at most 32 bytes for the PUSH argument, and an extra one for +the post-PUSH PC value). \item The ``TrieData'' segment is initialized with the input tries. The stored tries are hashed and checked against the provided initial hash. Note that the length of the segment and the pointers -- within the ``TrieData'' segment -- for the three tries are provided as prover inputs. The length is then checked against a value computed when hashing the tries. \end{itemize} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 0a409abd39bd363d8f4d95789aeaef2808374751..1f72b69df723e87dfef470e86e6e6bd75cb7c2c8 100644 GIT binary patch delta 116225 zcmce-Rd60nlO`%=W|k~wW(JFyEU=iFnObPE1+Q2ZGqWsavY45f(E@M{tKk>`mC9!+CN^d+1gh!z!DR$aGFCE26B`6UK?D{J zZzppy7Hwq{8*@`P1QvC76SsejNIAOLBd~~zJ9?3QW&JD9#`>>IAA#kcnSZTfLts%f zc5|`*E5Y_x;_v)F_rDTq=B|$JE~e(LWL$qo)DVzZ6wDng-K@yixX8#rd3v>&d%)rDF6SF7TN!?*ni*9>>U3OA^zhz z{!fU=|LHh`YALaR|5;4`w)fco*+l;t64_rTQ>aHp{-03&pH7CIl{W!H6j+n%7e)Ja zb%*791e0+xd2WH4n%o>rRtgmsh1;x%YK7d~IaA`v4QtDg&uGT&`t-QPB9`ZsC#o#k zN=7+tFwXt!D})js_GiNSy(L~v&x3pJD+~mP$fDAI$>1uV#ZgPxw>$EaxL6Fc9`Epc z?OIsGGT-#}f9f<}`0gz62|OaE`=n(C{XCsId*$^*6EIE+wkeTgvA z&1_ngqnvY5`|fgIzOY=4oi@oTjM^`p3ujAvYTZD*vGE1LG}d8Zcf!bzFB}Fw@7LQ= z2fElr!Nyp}5vDY_fD%D-oO_n72bfQKOVKm3YK3t!?sX!IVq^dYHefTUE2d;%HWX-6 z1Ut78@WQ$F!F|;|Whq0_XbM7zSVeWu@nNDssXFLbpJ+Zip7ktyJ5`bANIJthVDFoi z`Vw6{fb|wcPD5P>lFfF$t3Ii6e#l@4Bl=?xM-eJm1784-6!(cBtcTS$$LpEHAHq7X zME~E60rwv+pBoeiLxaloms9?KuHfWj`xjSS=!`k6NnvfAFY4cq4H=(yqSzHOj=T5@-$=-s|kimRc*Cjj4%m_HL@BLt6T^G zD~tnTdTgkKB78DVAzqPYONT={B@PF}0wU~2faU|*m^MZd+&Zi#QTPP76)?;RDeqwg~}tyH5>_zkj>Smp6>vl4$-L4 z_0Z!WKX+CRlaO43}Xi6O49c@co&X&^}Y5m3fcZ6?+n}mNh|?^ zE~lKtW&IA}khG}+$^!YTh&54TbymQCpoIji4S6_-zg z?reXo??p(b9wT5dM#sLQ?EQi#9vIB?hIMLs3U49HJ{XH}jkrT%MSbZ5YQm;XZp?9x zFo$`?ei*;Eo9tg7*dNrx>l;X0pOgkHBGS-=?06>(Q)mUbdcE&r>m#%N*;@h(vr%`R zvpauNEybw;&-sufj~^elA0LFLA1xoKmEEVBK>v*Q7sur8EZv%!=G~>fpKimDX~GkA z7Y9L>{Uqg%_6;nhJ~>Fh?T8o2<%oUs@6(uaZDH;q)J?oy83NQTCKAHEI<`wLDdhgg z>*1lN%~qM6;Lo+Hv^W922iF=na`33p=aW#m)HZ!3`w$J& z!jZ9C9c+IE;v(;W2E8O=(M|}1&nxpn(U#0zD?u%=HX+acIGP#<8g|)3)w6#b^$kQlvrvFR$)Cu)Na#GtB{{wO+W+ubD6) z`+1N2WZBv0Y~Nk7nVCcXXi)G}5aG84=J$9ZY1Z@Sz*U^yNKL5<&-WIZ#$Wqk(fakJ z4?T-%LwK9gIeI~hx{MW_H&sEgWzWbXT7__qZ8-%l57%9GMi#Dpg;{H}cKmfC{F}08 zv0QS8h)H~gz;$$MAu0vIMzV8Ku{o?kQ>2VARas^md}5G?UZ-PwkB|P*(>Dtl$+kPM zHdtE}QIOerCe%&*klEtyLWhRoz52_Ei{Tb*>KiR=0p!~VZVC&`E(ab!u)6!7$ppN+K z7&a$tKcd#*7`cbwK~3>yn=D4w!OseTTHpC+Q*(}YC;7~`L{xql;q|KI_3u7A%?{&GPA85ATN8yhHBj}AD{8B5Ik z7ZqstL98pc=@jh`fOwlLs5vMNmJQJ63-Txjpj7OR&Rk!>eROAA1hd7MnM9fH7lcZ# z_Kt&vpjgh0Wy$-5f@MD!rKT1DQVF6ERN3vJnU43&cT5fr-aiBLL%r0e^a6;DErr|; z`;*^}PL5ZgB}j}D;vsy26XYi}=`_$}q}y)i9neZ(SlAo>`w@NCqW3Vmk*9s{H|h0h z*OGqRv#1~=c`zS`Fxkmq@aG8Uv!1C1^4w*|;Yv#y|-+AHcfO`ah8}V{$UZ+eLCh192 z2$GPuxNp_VLu{apB{0mDmwB9g$i$KdB9&DfZ<>=yo3hR@`0M8ggyQ za;ufE+*X6@7S4AA{fTByNY@ldtiwH;&1vPVs9?YC1O3e}Eduq2_JgtjUfoA@499A5 zIytyC<3TJ`h4_yLZjM2TCwp^=0t3_0nctX6Aj$widZnIL-|J#5tB4x+N_osPoSl|z zX8F=L0!}i}FLl0Kf8GPtAM-n*)r*bpU$l+*8`)l}b)Jy{RT{#IwN5@W(V)WW854Qv zrr5>bI{L>1`a5{95K<)%8f~X#sX(xAB%TjqC+w9drKcV-Gii^Sp)itin@@*ynil`H zu=qOqAH32o#V2s&8x&-#RPg!0`wc!d4IW!oF;k ze96W`Wd{HPA3E2kA^CcJ4W57Cv4~*bkynT~4npsk!C?nxVZmh8`@OfiC*G=A0v6B` zb!p=G+rK+(O#KRJ-E{BVxfXPK-Y`*Zl;mc4 zA3F~W9_kN#468mU>l&1}99_yC>YM_4^o)HCBBgw@1$Zr=32d zQD}~??jD6TagCXCi4Pg{WI+_B57uk?8|KeC{|pi=!gRpjayUAgE<)nlflkvMla$9d z%niWI*q^ue+-Wzq+|mMyfwxd-Fko>}(!k7PFoyZGPk|XNxKDPV41~C-%xtiltA5`{ zAJL+P_WWL4E-!TbvRZcJnQ6YdEFi{P*JM4wm85h~oZds>G&$2lgT7yzZ($3H;EdN+ zcFY_Xx|oJVi5~c=C*w@w)lTnQPWT1ilyXmzHnys;{*D|m|J(~d z+HT&Se)f6#(`(iGR~t}BdB(kI1a9=Y&`)X~{tb=ww_$D3 zKf~*Y&`~nuZQfc%|2?_?k6@IGi}imME;ha>x@NMXb-rjF)1s&~ zK8Ko$?2b2v7jm6YUG#=vkB0^#Dy_c1Abk3u-<9?cb%EQ0R< z!FgL_fVMr`FH5B#NnVB3Oz@)X){#y`BP3)dw1CLll|2Y8EA)Dw`Tfkzg)+SyMY<{j-1UmXty$A#a8@5Rt>sgJQ?~T6l2=YPc)}OIV!_6<35cJVhav_vG9hwzUQ1{29&Hzno;yJxNp93py-il70=^b$R@bbsf^ubT6+gvp~ifhr3uAm=F`z$4qtg6 z(&TsOLor1Slw^achLPmE^6-uyTG92TE?SEHsD&pB%M(p&SO*@cGTm%omb|eh*NRt} zAN5xc_W(PD-HWqt4tt%Y9zdHc?Vjs#Eb8;-Nfgu8w>|p>CE!x#TD`lmadO~klqVW( z_oK2Czx~(Vpq3aC&7H?r80UPHGq{MHe3&NH{*#F|7M~iZ*t;N#HBR@vM@g@smKBK+ ziMyOlk2m|wDI|>zwrq3xayvBm$0V3VpJmCuZMB!An*#(h5G<^bp_tn$VeF~ zkOg^|8GRPxqE@#7+c5DI^^K(m@1Q#PXlULE4)56hegH;5S0@Jg3uAuH#m$Fp!2Lq( zA!b>+Jdl+(eiaBi7Mehed3UgXw14Kg#B+DTa`&O<{27tAJN?lrhxLu()!k|1N|I#% zGcR@6Ob~3gQY9$HRst#FL;=n^@v6KGLw0R`4*!+eGrGBy!R)+(@Pt~tb{y<1FbEQQ z%#2`aYAfI^r3VEbJZ&@$GqJ~v@@~#A<7EGN19%i2O5d;kwe${7w=@whL@<%mEW{e~*W--MRaYm16rK+l zBk+0)Gr7#Uq9B>bV_&zN?5!?Ljwc6&Fq;MbNa>vj%l+BqZ9{-S5bGkUIpw|UCi@FE z6Vs{98n?Gtu>Fdgg;&&fG>pCTh{JGYs?T8xCFqU1_)*q#XG@ zRA9+4WVbPz23k6xWsJkE@+f`yvHSU&?ZS`{S6O?`p|?LHM<=JkX(I$@EAE}-lr2Q^ag z_Z84WTP~iU?B@i=A&$6(Dxhj(#RGjYkT@Etk5%7|R=~wsyQe`bVAn!Neo4({Qjb3P z6gJv6^YO)rG-$+guY|gq)DY~!T=cEf6q1k@WQ z?W1eks48_(>R(91^rJn3&O?7G+%a0e7T>H1zwN5QSwRKI>PJ>bVHU==j+|I#0rZPj zEgKm)!b-79w=PH4QBebiW`OH>1Emw0Q3NxH_<1ow#ML5{Vr!K7i38U20t(%?h-e7i zG9&YozBl=NvS4o1J^9|R{&F~MYsp(|RVFHnb0a311BK{*2*h{YSSq;V;2&z1m{6g) zXJIDCBs!%v+R>M{lF<~fU+_7)A@0`34m};lU*j75S}D)X{NzTTTmdUTZ9IalFgb4W z$`Q`Sj+2DO@WpSRD_`uqh9(_yrX*?inO7F2lQSUNIikQCNW)ZQP)E({wyrr>ju3hz zHR;YW$nXv{vYXL7%mwR|Rat}h+rNj`E&QT9F4A9UfAQ*SiN=$7S&CyL9ne9fYW_Zu zGWPvc=Kkxx;+>4Wcqxz(xZez?VN7MAf~U!Kk?ihH%Fn3%b>Se<2i~qFmTz{vL!=`W znN{>db$GLA)KQ+)*kv^q`GR5shRKmY*Ff9W&yx=BXpsaVL zy>%k$m}?F6(U6|csFHTL8UA#oqSv2C+&sQEa>AdO;kLmsc5CxVWy08h`_Ag)-!y&r z`_xB*lJuFpPXgJtIl*C%G$a4sv{B88xq33O565+j@n{WjPDvl1rQn3EmPbOZ$t(!i zP7B2n2<(%`UPqB?!17AdELf5d$awT=Ibx&j5)AQY^6Ja=#R<6#?B&pZ-3b%&8b)ZZ zLODfIWT26wts`=Wt`(~Mb&9>-Uxym2M6R5GI)i6k3wd)e zSh#O33a_iG$4p1d8H-c{O6YfgpNHmp6K7!sP4$JtjVX8+-av^;9@pNoH_Dmo(%CP@ zuCx(=vFmE|DUhhGyEf|0BCuvJ*m3cRPR2Q8znJ_}l+j4tBR{it`CP*#h;73cRrrA{ zxtE4HcN$Fon=qXFT4}+c$`T+?3-4#t>y%^#XV=C&il(8*dyd=P zkhBaQasNf|!V6O6#sq!FD+jj*UEy(n&w?oMwE*h_r(&#e*@R+}%IvR7Ud6GBpydNK z*JOSDs7evznez%04zd8l3!CS-!=CrE+arlO7H8P~DoZIFqh<>NCA^fb6|R<+vF3IU z<*FA6%$22-iz{QKjHU1=OLvK>0R_7y^H!M2u!nbM6mE0h<-$}OpXw+dEiHyZg7(@+ zZlGF3kGat^9Qg-#1Y`>>=+ z7}07ltyaa2$c*9oNftN)Er`cE2Wo+%=7!wu&H9(oXUJ%!KYnqO8Bo=4to8c~&*y1W!@o-k*}qgCTT?`C{t5vL}C>w2I3rlrFEOh$LnTJYZ(RaRau$0p9oSh|1_& zYxZL;R*F;Y`3BwXzekh{HCn(-DO|RQE(HoCW?G#D(UVMJ5?y+cwBSiM2k3luv@gBD z?1m}Nd-STUzNbkuMM_OiMRnLjh64G;N7_HF&V}f-5k*fMJR54uz^m_2PP*W2*QfIJ zC+*@quB=07MXjJ$dJGs?BchUce$UXut+-77KsWtvP(Ysd76<`(8MsY&J`%Jrfj&Xi zaLE^w>`DVMTqYG`9_ktheZSs3%E6Gc{AK^o9LBvunP1C|g!)B@;Ld)pTRcFj=}#I6 zJ|xOj2EN`Q0_M8+bZN1P4UyNmo0V7wn{dp@rVUZ4d+{~jD16qtR5k#tS3yBYT~s2J zZHFQOqS>Y|x*JSoTb#TP{9V!#p(jjVw1n?D%Se3Xi0xSqe(D_7LC-H{qsJAIXT55< zQNTo$#%KqFgOQB_Me0x|(Fq`^BEO@y|XlR|#E^(c1v5OpwX zn#s|81pE-&)Lj`Y9Yp|W3h~lZn!whAQ2!3W`XqvQ8$CL zc6p(b6bLp*d!uU9N6#@^;=}zrjCV-qhHg6^X^X%34u0zcCtngIIWj~Y7CMp9H#7Z+ z#hmXHe`0%@>vtVSNooi3E5!~^)K$OoCyK!bQ`}HA_~ZqzG4*mZ0Erl7 zt*z+&h1Z-!H`&;`S{Mj+ay{ILSVL2=FMocB*i&84Hg-(#ej^mek9@!#Z}r^a!(UkfUE=w~ zzNv~^QsM)Hd54Z*R+YDn-J-HUQFSY2j3mzgp*bs3rT`UARV}oA-@`OTDpxa%(lo*_ zEs)C|seMg|;P8#NL+Potg6C?t(IsSC(4bTc#QFy&=M%PQd>EL}g)oZk#Zm9w=#h2T~_9!ep?)w36HM|L@~qeA=AoFzN^UL3zDRtyOI53JB@z`PQ9kM7)cT<8 z+>6{w7I(p8(sws$&&1&$b^;170*l>VZpxVt7S@J!DFzmfe-F1*fmyDPwZ3_EqsbqX zql!T$^@)lS!Rf2gNsI@E5IgMeJ(GJWu~6^Fdcu%i@VoE0T5^s*F5eBl5~z8mC!`ga zH4a^X+-mk*^+WO?!`YhKCjM0E!j@`f{UqZ=)(As#c1KUthn%>x@)XB+ zWT-dW+qi}To{KxJwr0wV`2##PhmuJOJnvD>d+{bky4oc0L#4@_5?M&|iGuPc8yjTx zwJcExO@2^U0ENIm68o!6bM~WJmBMx*B|Uo%WtY^s$|S~Eu{eB z=$971YsJ^a%}F><CFlPZ!V5E^A9_d4AVg;f4;2-N@@}*t-$sqf-gs2)_9dpzC>$%VV_DRl;1XQ*Sc#Y5%8F5RzP|R8vK9Z*AG01FetF{8k2t4gfl>R= zvb*A;+I9G>)Z3Ud|K+`PE!@U5`!czM27h^zmp8Ny`Z-{k+B{~JD z9yYsdTka^5JRw1#_Sl8bG%#u9=S%=3_5QZ}gmm-jV(O;;RK)-Lipy^*MoXGm^i;fZ z@YRY49)dJiuC0E%11>zVUrSU^$PDx*(o(?V4lEf7#RmcsE!5m$o+Y_R+xXMiIlI=< z!{UFap0_G`dO^0-Sd1j0<$2aV%Mk%(12Q9TFeG_Ah$qkF5{{G74DhY+OLW~S;e+Ls zB)-clf`jbVw={zn*;EdH$_6h03gtVMfBeU!i>|oWwCtV?6(nFZ_UL^y zY_{!9Jr^dju`y0+$v`F!0UduEm>QCGJ5wj!c7`7S5hhnW-23AbLqp*<1PF_|Md*9B z{1~qRyBLw4X|7f%_P!k@la@h;kQaWW0Y5L`Q)kFdv=pEvzg zIqfxdg^)`H=i&(03}_eC&A6Z=auRU-1{jKBaJ+(b&es1TZsSX3GX%$i;NbbE>I8zM z3IJMnIm*)9zl^6m_JkOVkA06k@OhwqKC8IOW;yC+{BZ7f{rQ49okf3Nm6 z*cDq%M`N6EV!b8fn&VH$XTaCWNNXAU>7B4RINS=Fx$r|L#Ba((g3*<1Z_AJ;d~;;h zPg>wDDbUp9w(m4-LY^u7UHWLAo{IW;?i@HGU(L75g`GPRqaXYsM*K1dpm2Iv=$N!> zC7YIV4DKo^dj`o&aiZyjd?sjwH{s}ILYE~ckgH5nHQG%3rH!`Yuc=%|MFiUI(e%tc zbv93z!4RCGzFEPvCz|rnu-s6(JjxhRF_{kY+2ays%IQmMJxm??VrVLE^Stlj?!$6`l5p5IN2)HFP@#^0xwhLPQL z$s28i!?^rg1t8R&7?aIGgU6JMDFQSsi}tnnp>>ob=7)T&T5+jZldnswJUB})`$KVI zU>M+-Nki#aQO;jves>=)8oPl04Ev_J!1HZ5rJDBiA(m3wiC3#U#+9GhEkKBRWryA4 zvAZ;b!LJaYeO`5nnup1Q{5duQ7D2}QQ=IqY$=2#pLjw+%!(raMV_HG(4Ic3NL_OZh z8nfqa1Wp3tcnIZHuww@?o){glfI8%h3*T=m$J8s^8MKeat(Gi_!Q|lanRvWeCBJaL z@Sm+w#RSW0X@Y}O=Cg0d#oX8s?o36-NA0)?1h0UTYae1<^a8YlD%xn|Y_ydM^Uy}q zv=xgbfq69Je6-t%@OPScsXz&O)sz?UG(Kz4rAdcj(3XE@v-g6tVefAt)Y?@DNNZe4vBkQq)9zDdInbdubjzxM0*Q^ zT7rKuS067rNt5!!NjgQzpR|*=XFX6(Bq>(kQ9eov%KmX$I>dM+DO<_{Gs0pC0R~&r zp?C*d+zchsFmJ}IE)ZQYJQlQW63#eZQ*5(wBvmMn6F4u#dvNT#7;ZH;4|QO3<9n!T zPryV`(OHZ{dTEt?g#k}5(z(9oQ<+7b%tOhZz~96xu3NMDaa~_^ZR_m<0c!e&tyKs6 zg+t*6vL9(}$En(s8bMuS`45flH?8;RzC^}xv6rtkUB35IvZmIPmH1z6OO0>yM4bg7 zoDB0@n5UjBRFuH)V4Bg5Bc6`NO{YS7Sk$`7cIyBV++tc47zWg4BJLD{F+iM6Iy%pICl6v-cCFD6Y4@ zy++yn5z3sUxfU3>4RbP1)*i1hhgu3w^nfoDy~1kJHM6&$zE&RAT|b8pzgR>u zT=v2dG7V>kCaWBCT2V6OYp$Ag;qI7}*AeDxTM7$+BW%?_xYiVL8-FLjKf6pto;PGd zKbUVWJ!VRY_S}$5aCAP==LMgu{mv+aMWlII6&mxHNw*=5St$eN$Mr?CYez|^Wi~Ip zI0;a$syEqK@LDIfVbt4Lz_*{%ALLD+#aY8Dms#ekc|e|s>0aDk_eWm70C3v+tPvHA zQWr&;rZEz(UO&Mv^D0LN$yj(HFZL$&qcp@7mcyq8Yh^(%-2X5)+Q-Y!KPwJQWkrA8 z7r~1C0vmnY@0@icBdGLw%V0-{Mb1dtIv${Ba?UN71wvFec<@A0q3!*VY9u|zeV>!) z5mHJcSvM~HNV1FECC)T?2QzVKGPp0WLTfS`KS7jFHB|nXy>r;IV;#lOrAiYG3_3%q zhuw;->yd!KILGlr_yE(?BRGz#@4S#E1X%=t)!&YDKxLRhlUO4PD013*5#-Tr) z2`6|Vv0v-FI_(IK$vBT8Hho2YknH>v0V4rhqU!#%SvCp!^7}_40yESk&2Pj?(L`t; z=g5cvvgvCSf*)5n-PB4_d7kj*5~ctuEQ{gRn+)OT11@no zrK27AUiLww2G{}i3c)lpx0&6no(nFz19>-@k61A@U$wCiNoR$OT(%~Dqrfe;5Tur) zhJ%TkaL4VsLt(Do#+ASet+h;^?&}o-95DnO3E8(^ki#eFd5F0mCLW%5bMxw{x7x#u zkxWI~S;uvL&szr#?KA^XS5rGZYY?xNW>)2s63#}sPbqQ2@kGKC!f(qKEN^eVwFml{ zi&r@@3$~mZ?(iv|@hqXyYcU@o+aEub_bEX)4w&Gq|5T`;*!Z~k{^=S7r80W};?BzS zIA_{kAm8mzCge%qBY%yz-y0g{=qo67DQuEBQzCK>gT9}W%#rSF?Oc2p*^?Z~Mq`8z z0^gVHqmJvOO^K<9PfMsAj`S2zgMV}+$+Y$%f6cd2QkGH+4|p)ms9NoyCrle2RUX3! zuV~{>sQVxW>N6H^U78$-O|m`#XwUAt5QE;_8RVzr9Q|6Zc8A|*o^+s2D<{_m*=_Gm z;P8KkLhy0Ql&7ho-vrDR-!JVEgG-@cR_3;7db83Vn1NRYN%03jR?D(NmAkj=R9#wk zjEyx0toPbA_}Dg?u4n7zspMwN+r-7!ho}W*`^e1_QbDsG<_;wGbY*7)d9k4mD&bAw z1P!ZRVxV>+O)%+L1gE0XL)uK2dTR#4TXWUe33TJ>`Ieb4DT3&^vM~9%h@TkGi#$fH z5lCEQ>`P-jM>6L6X_Z0&P^x-ibssU*_CwlYMZFiLRZ5Yh#5QEDI2tV&J}^QkZ#kS> z@{PhFy-sz3*`={D#*Y91@2p(BgRaWE=4GB$+@Bt;zKC6PvwVq_hj6eNhIQ?t%{KjW z4L=rL8Vwm5+iLtOl&LgV12_EA6$58x=N#y5>mu<(su2=><91mLM9{_R9={GJ{mz(U zsVINuEn$nwmB!(?R$1u1I{i+(`LS$)Gw}^2_(cKCk-(uc!)_a}Tb5+JYG`qw^>#MZ zz(?$!EY8hnHc_Z4Ps5&g5JN zr6!!`*ux2UHzz)K-nes@uA^D%rxAyF`1U_8n)`D&{SYNOE0t&Cmrc=)?r587qBvl! zKZ1--OWiJO$As_!BB!*NzAnJvOI0jX8*ElrS&E0fNOIq9C3$1LjXje2)}pt4Twx<$ z&saqOWnfAz_e1$NycnGMjh9S#xVIf2JG5LsU=8b<(oEb|xh&L~sTOA|U}qDFyO)Sj zrt#58CDouu{1Z5R2O;gP1h&8oBaB>zHB)2>&)(S0{fYoEqAyk`y67eqUnhkY7Oy0J zy=aoPavwDWTZ)OZ*HxX;DKYA}n$!r9-g-|lMjO^T3IBW1#eQg9$fqiZL z(nZ}y-AEU#J-2yfPprTWLEj9r)^bQQ%YUk(sp-{bgE1TQdtZ#>yGT9>X!;nvjC$LP zdTmcx5)l9}qjolHvjwwCHWJ_xU7Dc=dmL;dwW!c34tm8)p41gYgHGrkX7i9Y-`%NG zp;qy3639T2=z(xDc_cC9)%Ku*5avj7afhk4sA$%bymEN`ALQYY7cNGoFj`+Us(E;F zjQZMZzJW|}6U`6l(-{0cv3_yl)8bFn**Z;Er;>i9*4Toi6Ig@LPR0kfBBN`zi}F&w z*MEpOw?kk;I-C&IfzFK1aG$|ASl@(e+IIRt(1c)bX(emc7b9{kqaG4_vQo$ZN0|2U zM*kIHNLoua^x35JmK66PE?%?2m}3_lkVV7SWv^xSeBW_le|L4U>pDfFs1ea8-c#W1 z&O68yQG)7dnQEKJ&s99-^L<*5oKai);Ns-5?}(fuGgJNBR4l5zw7S@)$o1`4aYc-2 zln7qxN`3G)MB8#naGcsjEEg|)hgb&mg4%{@zsi|@s1Pa_)5GRWe2!;TFXqK2fT^5* z_U6%dJP<%E)+1yYm|WyN-%B9rvn$TyycxdDMEbX}gbGw&(btW{=P^=vI{If0+-?PT zM&*$#cY7~RF1zK3I>!}F(PSfNmpQ9|;aM@@Si@1C9y zMHVX)WIoYVfb1JAoW76yKe;C8-T@VYjW5-i<8O-jFTYC zH?o|t5tyoM@dqoAi1+Ql*2!cGKHgCjp1IVbXW1vZ{4f&T@S9q^{tf=mb#PPqtp2%s zKj&^jHI2P+1I_pa@=K5RB6Vu9YOSgO^*u9QY%|$wi}gX;q1we3?-)FuGQ5y*(>>2J z)jrh9%x`!pG28r%8sInU=h60FnhQ5`!hWqH=gtRyF=N<R24jm9`1)e(zINp4Q6HsYPP9R*7MK=@BeXsli;nSbvet{?EPi;N7Ks2f=ARl>}sd40!h(@J{CD9YpN&I1@$jQPB+_!0K z8#i-bQT6;;c9{|kVeMU>^m%S%0DvyV`EvNO2k!W}i~jVseZLmGkSJ3ULmr8cERseY zDwfj5N$kfN%p%i$iR{SIbaONxpxoD@2_gO|G`g%rnUWP6PaB1xE3XOw3A0^qcb@%nty1*4w+(uySG*O z8*^j15&x;v*UAiv&~w+A{^@SKZ@2yEoKb3MI`iVxjwXT+#;=>ObI;qYdB|8^t`R|W zKx9_FI8tZ-8hK+yUYrZ?Q|^{cD`lEQsM)%BmV!45qSF0P76Kxrwi>DL?TP z7tQw@1^yP@nmxl0g$6D^C{+X`#|GuD6Mbq*D@UYh?iX|q7_mzru3(*@0WQixaFmTC zaNVqfCV^8UY)v}Q4>Erg0=$j8cuFnLGZf;N_#!`@bjQ6BHP-^}IAzY{zP$-G^myTq z$$iR793#o^7A~2`DrnXd62b&iKhbE{1|;v0tMP?+wzBHB%(|4Ft6KwI`P3ksn0iR- za-(7J;O%gOj#TBf{Oiw(1znxEy&?2~EyG@E1cB4v z{&uXWnWm6yVu8yLL<0|yTDvh&^832^f}23jX|$PZ27w~GsU2%iyj@CpMMXsye@0=? zwKPILU4DLUD0y-8{mul7V|*V%Cb?e9Nx@rk0mkK*0q__krb|T6KWl^-Ei8~P2R$Fk z6+FOGkFyxSr>2`=;ttC$^BF2uypnqm^}w*p|HHWH$@>1D&~61{s=L~!IWF{_=2f!I z%U=$r96>xDowY7G2!S$`t}q|Ybtg>Q-j)GkgkiXmtj!E7O(4IDCcTy-A~8FE6VPQW zqd(g-`Gv)@bn)(IrL>c0^Ex4|j7d`N&>1*v-+2Q->iZgcu8ri`zTIo+JI-9>+Sa$eK9mCjS7h=hN zj<`a9Pv%1EZPqr&B?j&&3=4s7ukoW_xn;R$tfp5N4@ro3R-!ka^TG0ES72G`r}Rl- z5=SAB?$!~?7I3NP4^-}Pr?c^K$jIWDzoMJ|*q}pbUUI~~8B_99h`+sDuWZ*pdqCP8 zo~YFwDt|4%>A&EMuc=V?-%D2HWYjAh4bTpaEoN606ogm`=`MK4@1ft`I%0*obEY1B z^r*Zu9bOa`+&@o(3t_!Pc2E5(EHQK~^!h@TD1RuK)H>;rqtSv!~Wa-+P^^ zK~e6uOQwg7fFG7MCSTa&$IGtZou8A{>{O>A0~tDwSSc23^yOhieWT4#O`Co>QA+!) zB{?oO0e%1r4=qTCGKYXdi^4QX#xzTJ4+@%`gE1kZ_MU>8(bjw$T25N1d*TuY-1P!L zzQK}tlCcg{2@RQrgKwCLvEa+E+)}AoC?bVifWR_PVKR;|p#g=3i_HcHZGlq@=jm1T zrW|vP9w5m2=(Hx_996g;+G7 ziL9^1<^SmsNm&BvV~&Tff77xu^~W11VWj}WlfdKe+sBg! zpJoO6Ht=(y6T$*%8H^v~RpdHU$N7EDpb>U=!vd3({Pp!AX7ADbnI$MG6CjL@_xRul z7eH*EDFJ3!lk(!-6&yof0Thca+t(P>HF`=E&x?So5CO_)VNF#ePM%+y?*^7d`HRNp zKkfo7Dxj`}{F65N+TISj98OIa_$GQyeN#JxvP3pX*X~MwoBOT@&K*$p#VgeZbWz8p zNL9!fgLta!q7(}Bn%teQRxY|u>sPvi&eMM@Gy{MhW2DA`@no-6T)Ae$uR{e^w!)FJ zMIP7Yt)qr&2qOYFt4B~ed(>O2HEO#Ja9gt@KFSfOm171`#>HiuN`$e-U7-MB9-sgA zzIgxv5kUb@8c(F6m+C$K%`a<2wl}F)7=_Sk7A@%-Jz^P;O-1scy&}ef7rxL1306lQ z8z7U8qtiiKl5rNEks6f{TPgkaMCpegn^7~!!!&4uaK-rhFA_>au-I?mv zM+r>OWVn`-uZ=pa%rG?K^4pc5%xXZtE8ubbGd;#O7tSYK?q$PXszN1MhSkqxPA+>p zG1qzniVZJO{2N$;;6^w5x#$gZ0+p}B^!#>DaJKWbbLX;Z4Ou~e(mzVoAg}nIjRvluc z!1QMk2bWo8$pcDyc|Ghqh{`3m4TyZatZ(K!Y+k)OI~2|=LzC!blNO`PQDX=Pmq3g_ zSTCvgLa>zEh{>O4)HP+i{z$WNU@5ZivK*l50H?P8Y=w(KFBE8t(ga{YHeUp|MzuQs zyiY$Z>Z!e4u!mRv)01o02(AJh58CH*4nisouw^M9*WFQ*m~IS$rFW+@1jw&zKXGZZ z^BF>d+aUhog_;NpkU=EBL4+QXFXoJmO?oib#URKFtVKov2i{Aa;^Bk5rgM zdlxOcyuoz1co`q+eDlYKiQUgW;v^J0K7%)d1ebU?9(V{at#4gjEYTL>F1ce{_hmGD zM=P#j&11EF(^r2fASOm}1=zbMvxTe&SbfV=*sH%y(c%w~#8rBkPI+Q$RCrr& zepIvT-Twvg1SGyg``+x?{9d*2gZV10R}DR&LtKX2oAS{X{)e4E17IoIOiXXEgF)e2 z*o!AU(Q#M#-biNHB3xaY9zWPJcCM>)+G1D;2>NTgFKmW;eE+;&OWFG_y|!?LBQNIN zI@S!qckIcVWOzh@sl|C>!LU2&sKMJEWVQNpZfD_1gCC7&=Wu;l3*<;Q@;d|xz+N`13C0mh0rm>}6B4;jtbe5n#AYqfBZLnSk{hz}#VIVSD(lrYb8I#5Z|lEr4glZ&bB$NSfBmStN-a>X zgTw9Ul*wndcth**D!?eQ47ls+;4@jgxmegF(#O zHns=YooAGz!$Xk4L6G*~WP-g9NEObe&PXAggr880Y`ys%(THJ~5x$=vswdU!B@jz} zM|(RB1UbNXQeJPz{E1Isc8G}h$YFR-g64+QAlr|5lvEE;tH4kb&=xf8NrV*jM0oEYa}-n`N|-5*2^Qh^V0BQ@o~A83BSI zRaOP#U+kVE?HUz+@KA?dn4g0J0h4x+?CBe}NF?s~1DGp^VWE}7F^#HBqKm@_a5$Vb z3Q-Zqt-qZ7C*jH6qN+?a0P2NfE`cvmao1AT=!)ebL}q|xY;-{AOl5L7}IvK zhZPIW8B=jlzK}7qp1P~CcSNE0`On3*vRjW|4O+gEpuon95?nte4v3<4d&sHdj%IX2}H4HYF{CcK2~zpE31W z#3@MLG<+Sz@$PruwwF+ zGU2iuI=a*EZ6kwkgvB2`Q|JBdmz}4aS&Dy&*)}#4I)6Jv=(@(&bT9n^dEH-UchLIB z3)n2^odIM)lP7+UPp2=ypU?g8G=i3wkbc{e1r9AualG9ny!V?W{J33fFcgqd^awCTVAt% zNbrig~}9XytJ#M+svuGy4KFDFm=Li`c576R`<@mFdXbghSqX!Yq0) zsou7oPifjh%6c1SD&bPGocJOPbQSPb{C{knQ*b80)~;jQPA0Z(+sVYXZGExrWMbR4 zZQHh!3D4~PSN(NP)#;mFcYXVGuXjD2RM^qLkHG~$u7#I?cI#t#K?gRvd0>D*3wl5I zX52E}Kmqs38XaZ>_;b8TFH4MwsBbT_uPcxYfVT7i&Ve(XQnJCv4YZV-ebRX}V8l*C zG@I(js1}b6?9y4R8XQOeVcG{0(pB}8XpFM!c$pN_3@^lXAmiV|(5ggpudpwIN-Y+p zx87Jwm?Il<2x`SMI-g_%kSh8d2o01(Bxktqrp0ke+j%UewLi4j42_}SlOle%tx zO-tgT!dj;H)apBN1-A3!5lKMJC}V#2qpkXvpV_>Ht%xl0My^cBGBdOMVp;S*fI2rm zD~(=~B(t#tP^-`v%~KDRg@V;MWU2`HvD-^o5ck^!Pz{7^(mzLo^JG1iBV6vq@=j=? z!-q+qD}wRbt!_-1MCwGR2lc#;kls_!K_c^@qO(&|^frMuJ4k#@4C% zwxR4Jhk(0D)~A|*tISsQdbA}7Q$pi&&(C>Kvga-WsymD9qP)RWRfR|wd6%X$T%o8j zXv~ys3-D^Qi`<@3>IWgP=wF+B=3G=^aVQ|tR~fz#jRs>;eP3d~QqJyhm{0-<-Sovm zDPLIFTml(%Y)TPe7$tNXG~&~%Ipgd6nkCb{y40`-jdbOVFt4Fp7&6o|FV?cjCp_m5 zz_i%`&b+{|qMQ(E`QnQe3Jpxt)CgNs+blsY+(il7uwtPpnYd+y@+C&i^4m~s?7$z; zGeFvhK=Vzv)m?^gzK~&3dImvZ$w~CT$cd!mSQ*-ir~sElI5cX&P%AF)rIpkF?m=kwyM8mxpQsz*t2DW#iH{efe7Z-HeK(Sww%a*2ln( zS!U`{9)ydh{7k@M$wqbWVeLV1kKpCe)hnrEs!1Q4m)xZbXizM&moee;- zFBOz@-(ySGqzjI@mYkuO-D6bqa=?)RxX{(<#J>K;TZptaseoAktbm&9nz#d{iZw7+9Y^~BoUAqK@m zBCnMUIr%7iVvNh)S}~k}T=q60GiEos^9`AsTsZ?&Kyt4eH9Sx@%CVR_#$(6GU3ygzC3`FSHuTinB z?P@F@cT$jN*6I88CGG~xwvT;wO1-Y`gaUlT9>Vaaxuw%#hBx9MiX7=H1s2PRUU2%% zLiLZ%AK+Ladzt@FT!@MN{}#6-MMK~saxwp}l7pFOT+8A={RHimn}%RV6b70UC=}Zz}y?wL9RE2m#*aa8khfNmJdoKq$?9byBer94lWlLM<%hA#$1Oo9>olE#tiecL zpxi7&4f;`jCq-_MriJv+Rro75&DEG64cLR|#g3I0++*P1mAvekNBSP}#VPV#HsiL|esY4U*$LEI& zP9s@>7l9xCO3BZSzBZ&gLrMNk#6&&-hGzuTld4Ml#JT;G@(M1?^gQx$Nt zDh!_Ns2+lW;|lW@1Y~Oz2bsnMn!85(OR%3Y2pj-Lg_N8fD7=@v9>o+qjTJ~OiW~#Q zS;xtx*|xv=-1&TR^?u-Vj+Oeh0570#(>gglF}%I+PS~=WYs=6gBXJlp?9icSyFDI( zE1!Re+=;)Mjq_8!?J{9z;NuO%WiaS=H_Z{Xe>(mCVAJk;>ah}~!qKJWx7M)=s{>N7 zzYGA>ra>IM9)s+uZ5~^BgBIi4>%%dmaa_LsnLM5@dpa!7O|`G3t?a~3fgDP-#z zF{Wp4!&H#(be&bJWRuK}(;KNvPg{?1tFI~x{fVE5WOb1WQ< zA>jCb3yPQ=RM2PG=v63nnQza`-VXZ*z)9Lgv$LL=7lX;?fW0kmCd+%ZMAj|r^{Z) z=hlnihXZ*B5{MJa<~*^(gI|6q;=IJslxuuj=12eqjPmfV!2P=>f}AfytlM25Q76?@ zR@!n^L;*fTm)srvsu-1R1soTK(%>_C8f8H=l)E!qrl7C_w%Lw zioT0vqi;Z~MmON~=J^mdY7%_`II$BpZaqeH8V1pAEz`#elzBA>!ephCod}>}eN?_ltPrBL^?x)>Sa05Rjs_e^Nah9z4OlhuzHXN~UaCqNWv5CUc>IL6j@4h_9hW zk;A1`rzzpxAiT*dFPwsF9ELEfs4Q&Hx2`SUY|;pXJE5~oe=?=4O#Z9L?T|)K!3L;$ zQX<>`BpyTj@pgr|!951LuW9t~=p&yy?OgHe2QT3H4n2?mFM8g>DL`z>p!0{4Hlk25 zj}90+yL&0@uA-df0>f8BBTap?xOZL8=DUr7@_M@b$xl9H!^BHTl~m8uE1!$nV*5$> zGFbiZffsAm#wk>$eOip((Muo9g_E}dx^_2TodT?#l(we!Sz}W}5ok5-XKT{~f_apS zv>zZJ8a>~d9r}vN7ZBI%j^&}C7{`J?ZcqE3w{%4lr@gQOOZD7nqH-8uW93m{L+v_Y z1~A0V25C4wcQpa~cQ_iUS8-55cig-*K5Y3wAkWKyS~K5~DFOzy;bOb-uLI31&!%TT zut<*fc%%W;r@Kk*!NZ9zb9iqE2Z`xd+OvOmyZt`G^)xX)0QYS2?Rl5)%Tn>v0~xTo25KV^98(Bh>=0z(NxAYPCHJ{U;_*U5uvlgDn% zuItK&-6w-7pu%fG2scFPC)MjaWoRAJDUQN1&eZma$=cY&_E&*N3NJTY%j)2Gq?c;6 z)v~n=*eI_9z{AanncvWZy<}~0{cduS@cO6n$EJ0a;|DAB`1!=D`B@z^4YzmalYa>5 zP%t8>*YD%D_BD~WWWiGF0~S&4a_4F+5Ek|%CB%Ol$A9!R zNk}Si06S^WefYUnjZ|gntFhG8lnP1iInt)d1-N!%Emk zIh0}J-arr--fZ9fmzlgOz?+2e(+kbZn9noN!b8}~n z>}}pL-{cNU9%lH)WTDisG_YR#e9f{=bZ|HPjRMCTV~`e(X8pdeZ3?r-EA2XWn?Y}2 zP3((I2Dov4xOG?uvEKJPmuqr+>dcyBjYkG#r&$#wd}+W1avFF3038>W9Ntw}kKS1h z0k9SHC0p^z&=1w*Kzn&MH7+01Y`0a9u|gYTR+xKxY${mJA7rYilS7*+6W;4_${Mdg z+1TlLHaTmZh~*p)Bc81lYaB!-_h%$kG6n4t!14Y@6Vv*46iU;5Mz&5MZ8hVJChz~0Xr!aqhYoYXaH$vMT|^U2>GNv|4) zG+pKu@a)<*8!Cv;M8j^z`tSW+BB2+|udu&=1Ra$S^fBu)6@azz0FX8bX*_*~ysi~0 za~MiqpZ=RQB@7+)kyJT|11IAWs<4uKlS_-FmJ!m;;|vRC>X`)lFFWMK;&{YJR-%n7 zGN^0e7ZG?JKkYjzQe>mp4>{^?ZLvyV z#-r5`0}Uxs5aVKxa!msx6S?R9>8R%a z44=%-0PuSHWsEkF3)}KF5t5Bun?F8FYpzFXz+dF;v%?Xv01J+jeqvYo{xsc$kQW7~ zi4=jCP-gWzMc*eUjgxdax;Z(z2*;vdi!YL2=dLIdg9q%lRC7t#ez+@pKoE99i_2&~ z_(On{z|PCdIf=>9jStc8!J13d2c(@adxM;t3s?m{$ZyCiy#SD|j#HIc&pZ(Ji3Vya zRKu%E3(%=C0N>6z>P}6dg2eI25u{!n#O?0{24!ewCde4KINof=Wc~|9*Un+hFWcw%;c(VfLXxWYs8|5G|PVcg>!SvU!1od z5TwW=h=0Zi69WYS-2e^f+>%Z9>RtpYpH)(~-^xsV4Vyzz7+K^C6wMPT`+JNf>$*J1 zjS&U`%A&D*Z5OfPBAH|ln$B~wVR1(aCqGy635+-((yft-!z0h-`SKS7BQzeQ!r;N! z)uK@W)_5TPnwA)_1K*;c_oE>$+oVE>m|$L z4_KdY7RYpJKI~*yl+F~XITvEz2XF*u$*-Wt0HNIK%sI8(dajetw_muN$(?n{Le;-B zRJuv@0OoRv}^%?8rW$30T_?kq zqTLe~mV=#IeQRE&`vv+u8Mq3;IwF-|vx`#w~ zhcn*}(`fSE2mbNq6d>KD<%rxNY|r_JWh!XGo`XZtnZHz$5OI8Chb2E2rIv$97K2$F zGv~_0gKL=6-Mw=l^WE4FQ;d*7k_t?7F592v?Vh0E01Uw=P%6 zS!^^{Ks$2`dfCfeQgipteg1&r6I`!J zerD=f4O10Dwc1{#xdq1Uir|QGLxy{#TcF%3l2&C1mK6t60u<&&Zh z+j+J(=q|#(fL7Lz0o~6%HK)$=@5T6DM(ut^?|dJ3sd*35C}8Uwb3jqm?LUN-#;6aB zoydy!PI#X$>Y9D*XKyd3=d;?Qq*ZEBL^y!HMiAnkFDVEKQ|!t6bCAJ)M=$H!@%}rQ zM0?NEkEy|z=xO#2_bl|bk$FrKDiwPT%XH7u1a-WPowN_K)qdk+F-G=xmu-OKaTBT@AnffyR6PiDRYGP>d9tLVS8u8u2Lo8*zC`Jz!>0l zu!qq@`(%kSEQQgV`T!mV1QJ%;KG;l48>NpyO?p&&%PnoSxsuzhW1c)>*F(x;<5*O% zyQO~y|BinU826NwU$W5v3*rF>m)b3N`(=&?2cG73qru(Tta>tvaiLGXS^zvZ3IKg7 zYh(hRTX5w>)K#SuK;iFd?=Uc;9tNl=SfM0}5Ar`(62zk9A^rzNxU;S2Izl4T%anJs1S)g!JWldNfbO-1jEK9LJ)~_q_u~zxq&y4Js<%R znbrJWv`q%YsHhURr4HUF&{MnVYGE+QT&h}k5mG@+U3#*##=XL@d5Ic9-y>S261w)K z>3n=$=z$9=%ndNW8wv&W7r>J7)tB2$U@+rA%B0Fr&05x?_ou%QgnWNX*3{DFRO&Us zh<(uX_Y;kYWbO+_C-yM2hJV*n{5zw({z>~@8tkmxxbvg6Rhp}kUBA=wTyKHBd@hsM zBKnVP5P)F3+dy;+0d+^N*+4p~ecTu1U7t3Y&nn+^Bv(r^q2$DB8i3UbDx`;2ec8bl zVl`<(3^A}*n0_8S>#|TX>I8HhkaU~8-#H6Q`-}aN>p3#>tHw3<#Qvd$ka|@|5}N(g z(rv5fX8Y9dw>sy)EhvW)sw(o;$kDLI4buB=^|4@X&i^!Ls_v-1!KCxBZ5Efj?Z<#$JE9Ga7FJrQrn2`k!Yz&D{@T(XCG;YY!cMW?{W>FpnbI3J6m zr+Y7>-atv3+G+I0A8j3fQw`0@O=U*&$ zv5aRBu^xIegU6uc;JH(G907Z#Ww&@r zZ$`tE#MPVZ1b@&Ce8V8pu)U`F=1!PG+hm~wzcuRU5E|FNrXBrje5x{5;K10akypF<8GJ-Ii!Sw*`Hw5iI#2q{HUb&<&*{_iQ%E1S{Z{NC!vDjbYR#mW^kxE5tN~S7+ z6Cm6W2=c0$9VL>|$|}Y{+yvO)8ykJZzi!Vx{G|&(XOMO6#ny2GH zQ3z^FG->ud0`x8)&hIp{n(s4lok`K{uqwvMk`np1MSQOR%N7;=#5UdwSJ2*H(@=$H zlGlLQ;fZu@n77_>AaNh5%n$%T;XQe}2dpw9Q-iRJJ4on8!dZCa3TMQvx9QP>!#sdX ze&4n0vz3HaTC0AXPM0RV?ioX%p4EfH&YH(%9;)f4LRw1 z{(|i-q@?{8oYdpu@9rey-Rhvq1tV-Aim>}W+&I}h41e|a>PXuQCzV5zkc=aNJ43lg zF#;|8I$lhJz!PxE-;axoL8g0G0(fzVFUf&E!uAc;VUAjRwOS+mzP}5Q!KIx<5-q=9 zzm_3rYYMPX%Si!N^V3}M;Cp?XGhp!MeMm#AVABZu90WR`)bAUK!ls(DMD)0riWbXj2vX{r_4CA8J zc>co&K*WcqG~>0`_YJ!6kv6)9r!YR%(SqJok47X_1SmRwt%k}Uu@R6@;UVAPVGtOT z6)d#A!-UguM-@DR*$R<00$`0$8U5p7mGRpMniR6#FafIe!za+(vB{1yOqpQ}?TJjX zWI0T#9Ef=QoaQugvK-E)mL#7{b?<5t1pza95IP}kNnwcUEJVt(XQo8MON@E*X`XTT z3}>+E68A?qL>jNU=q_#;97!`-!wWZS(Boa-7G2a_mQa5M!;c$tmSK;@=mn@!`Zo4k zp}Vrqw=I1gYdetfyh#xxI_L@A)@8&7M_7OCSbyRN+;cgc@W0Pvtp76`lGYFgObzJP z+_v8wLk7I+3mpxk89cm1b(%!-lU^VDgU~r(g8E8=;L$0eAV)6U5B4&_iiX3lIOYm_D7vQ{KZjnAXJC6z~G9QDMJzPRVww2(B}_&QcNn~d0_G;xYf85Z|mqPGouI$tjkg2Z#ui(nI8f=|T za&Uhhy5l=6cyQLyMt>JA)Rl1@ONTzkwq<7KAbx-ZHw{f5D2lsRJ-8z0M+%8bi!BU| zXeeJRF3}th5SVBqr{eQfLPQ|GA(|4G@1C;tj4RIj#X#$1aS4r58(eOHldl|>TQ!@c zvCdS%7O?O$1~`$^9)h$0(1Z9$eQY!dS72!k-(yo5tP75x&L+In(OF%3r4t!zA-=07 z24QI3PD3v4+)^g$@-?luHhS*j;<{GcwkS3F|Hw!?I2xWnh@e{94tAX157*ZRG|Y4! zK4yT}$xWa4j|Idw^8y-ZyPgXIT4*i`rfJ?aIF=pC&S@Eh`^}I{&d-kZK;iJicXmq@ zm$XL8r$H+B&O!zieO`sYaIwISjk1gTgUtEBxLdI0D+61R4$qPHsQlUeyiZ~NJh>!SX~$;UHq*u_3cGdl^#};=d#@Wr?^J}x}nQrU9H65qqvc?>2pl<_ISo zAOtfWvKwdXsLw7Up^w#!58*#C(94z#x7qa-T$q}I9fUXR6d1aCa%KyYu#hHq?M{A7 z^%P*d7>@?m0&aQ<@>-Qo`DVIz&f%Sk|L-x%>_=1iy_bPQMDKTQT>};_2A8Hae@M?u zdoJ}o?qi4o0PAl3MPu+K?WVv=)nUThc z5m{&kliHHY37)An$&GiX^cYKcy|H#d|5j+EOqR4@?s!&XGG?Khok7xt^2JXZFcd40Zi*ilT4`0Ae6s@uJ;O%6Ffydj+D%Vjs8+ zHB%B94w%eSN^l+S1fc@pUqX7{Ie5h;AY)SAnhHgf86)Hw?4FH9{Aj3^xeGhklw9kN z7PmmXq%srwRT=FXARC^sY`@%vR+<~d9`|2|i|lx*O}+C<8P?$Nr+HnExI@H#zw_cs z=3`A<0W2%AftwXC%r%#w%0dQcHLs17OnuA!QpJAlsdDe06BiA?u{m*5%3_aj*I0}Q zv-cl8TL$mV!3wRU7gNedkCGhCmz7s4tf3QK$cbvZNR-KA_+LBv=j4YTDey8nZH2U8uf28!2&o}B0Mb+GZdF1#Gtj2`09_%gkeKmdXLzzVfLP&UNE;w@6 zMtT|kt~7!!eW$J5Ae9F`o>A+z4)t2()5FR@G0ZhJEXN0vLrF(NDpk{$mtTM^;$%ag z07TQ*!tIk6)qw^s%2%g4M*nMj3iuKwU^0h2k3stuSJrwPwFLvyU^^vUg!!sNp$dhh zQj#3eA#Fv_esZ|&4i`}t8f=VCtxA=G30B??+}Uoz=~<0zD@vI3%6JeLm~6rB%sQ{g z#dxJ&=Cl(^3+A)N%~SSpEVUe7F1gLS0D2X9qr^kc?V|#b!QTZtjm_VVODp%X!)T;z z%;E==@C)g%1XxMx*3o?R)bJ&;fW!P(@V3?mu~R(5{1J#-Qt^dHfq;uurQRw+;PE2~ z?(PD^{3l?70$EPuP=!Of@LCqXVnu~k zMr1O;`Z;eitTJiNq>s+lY+OnDG`Zul&00kxiSZ%ue zn>yxNuH+_}E+Z}Z$nH-g8nBT-9Pt>%mv!;8dgmOR{LZe&Z1xsQ=On>J>n8YJ8CwI9 zzh;jIp)X-*9d@8zuJYHRotQST0K3Q%`-u-sj@g9;UrUP16NljB#h3~Tzw3%zYthJ? z_gdw%O<5WDS@RB;4>zd{fSvvq;ZzV!0>$evteTa2EV%2h=C?mI9P&TD``jBN7#g$A zQ93AKiaM8%OAnZ1M?l+YqDIcy>lstSc6}aKE+eiYp|(+e7S;%&a4X=xRZ+dR+6rYz zzwWSn{g~PGBWeF&c@f(7%BKlf$1=PpIc;#f?H=wlPRj3^@X8a-0Z>&sEIzB(PQXo~ zajE?!{hHUixGIJW5bm3?Eq?H*3mg%TY8mT61M6D{x5s{Z5z;2TdCs=(Fc@V9I{sPB zG+*P@+@A2lfM5tB;q~@`6DK|Aex8_&3I8bZv^4rqL@FqoKnv-%WS<_FSH&6s&2)lC zYl`sj+m*WE>dk+w1fX1JYk(1uKkkr^#OvKnkgAzo=%xD24q(WB=r^u`7n{~x9hlJi zXPrggcOxCJjM4Du`i~>thnF`g2(!d-ekq>GzFnTpta6<8-tEM<=m@J(%&f(%W0v*OIQN3f16Dr~zVPk0tg`!-9 z?fOoE`YE<{-GK8U+ zZHggdopv#Kf1tP!0BR5htp1ns*#6T5MFU~_Pd1Dau>246w%AerVcxH;Ny`9Q5oI*0 zVg+a|!W;jWIk(*d-Z2zg*kx&4UrxuL_ZbJuYEl}<8F~adIE^I>&MXE5u3mKDFahLn zQD$sNWl0P&A-}FSkA64~UieSrUY}vjSQ@uM4^oHJi9L3QIDjq`gYWI|?d-!YKE0kk zHGw2xSk_3?p$!#111C+J5+IVA`s}W+5GmfiRMWzdvKpD9 zb@bzHjnyIR3&HE0XSZ6Hp;BvwZ`n}WFn6&48lX|nknUJn4;#E@tct4VhRA}g!v}*u z`VI_eD$dUNwIWm$f$?rxJCJuZZ)FOC-qe%SQfAg-@q z)cC$LpKpcBH1df=o*cUmJ&j_4i)K*rhc4Wal^_c-egJX%b;mgibH)?Dg#Dhz)~ofzTgx=YH3ewHrAwCZ)$w72|>0&S@Y zyhCsttHlsR8}Xz#{`Kv8hbb96;U-*drS&Z*{yUVM^q0voU6u7YUE%0afXM>@$PN}k zPkg(3Vr=(4g3s;{cLGDBF^F$r5tFbMzoN}hR^9KW+ErUBYycOYfxJq(^{}Q>{prR% z*$*xE2#@f{nR;Q4?3y(s^p@*4nWBMOib;V+a;(^SXY~L&OZn2+uWlrCtW+HO@Lv&y zKu6KwS{Ab@^e}k_S``KY#$5-1Ec+E{4!l(~!ZHLwBCx1-P40(Y`U^#@$_sQ&Zp~W` zE-zC1*isBWF!_Bw>;U~!`Kl7$d@imz0PGux`ne47;56RwBIp;?!|DwL6>a*TG`JQS} z(6E4$DZtb!>!mtV#U2^(H^;(p?8bMj#==bM&*f;T2&TxI!yEo_XnW%XMp7=K4f${{G9>9E;}+2C`soV-;s1vt6J-4qqh4Sn zu91rsd#h2o&?OoGdP=f7QF&Fg%KHVDrS9!1eA9HvVN81o16)zYoOhMtQV5Zlb68&E zAmG7k2oa0HridSX{Lp^f>sA-6#9vh`u2^f_f`@VAven{XRKzQL;|0}PZW9W{cx1CO zEU|x>4h^z6vjPW(N97?PII6!m*SG;h=QSs=!Qqq=$zcl-PKdhjMb3bbb5W?S-50kK^yly)0Qm}?pD?#4Z zyrcD2XmeAhJfmMk*A1`}L)7xh}mSj7;zn6-qBTdNyu}X1`Tqq$0OIk}^;+L~LQ_J~X zO&%Vxla)HgHw)TZd+&TbjOl_RGB3WRUt)MZ?0L39*A~N!65rkCC_l>GoudV*#9h{e zRX=1cn;(WQd~(3JZ}sOnk%7i^XfHivgTw?6(37o;D95|~izAW{D&c`PINq+%64tEH z%*^wU)hnX(&^(Uju&sea*5lF!EOBsk-?FxC&NG7373C!bvt@%U)`cEMl=7=l+@y1t zmc!Ol-waBY_{{ej$TKbHt;j0o_T{lW(CdWtkq0O@B;-TmSd-7zO_3)S1dxPHL3|RZ zoUm@4Qsu9tLnyHO!R=s#djF=4Bo7BjIbY2f)^N+nO;u9l)Yl zOfqcAvqVijM+WNXB7{OM7^=78P(@x=uQ$WG18lFMe4 z)|{cY>&A5hq(5MtM74o2*DU*~45jI<4;G+a#ASVh?*G>sr2axL18u1|9$2rdLP{IbCr`IX~N55^E!Y@o{FOO6Z0 z6B@8gBY5jHa*e17)DhUeHWt;vmF9}I9%*x0$LCQM>+1Q(sz^4%k=j603t1E5e1Nj^ zNer)ig3#O;F~}6Yih~_u%5Db<{QwmutWb zuCayF+%y|=-q{UU1Cq)yn03l$*w|~~a~P273W;dBH4xb^9pf78v0p0Zm~gZtt&HD$oISMNrL>6d(li`xUVy0(*Y9`-xp9$#$v((Lum zBnE5Vc}emc3_B4BYhyeZCQsb4DE3huF_n1UsO&sCpAf$hA+_+T5*+@d2g>M-rY2zY z7wu(y%!Nk)NQmX2nWZv$aEB4=4po1-8$P8mZ72#v$9r+bv6;tUw1NO`97Nf4fsUYN zodcF;cJ7UQ{#XPnc8&@}Sehn8t{hbdLL<0_y4i2^QnM#0Y^vetCB;$Z*WjffOsqXQ zosPTvU|^QRd78+{M~vqqrk`2owhTZqpfah7n?qN@_Mz~%2$g?LynbUn?Rj2T;Yk77 zzFfR~KiU0YNzDN!7r}2c$muyvx1b4HYC<@i7_8w^jdS)JAqbRb5a6#dLH78Q{OC`; zEO?}_5V2&y25yj)g=NcA~U%K<0gj*EbWxM<{lq=wQH zDgxBz6 zeSX2n@J}&Bb9c^^qD?SqtCQz4yjSp2yPGK44Ak?E15eKR3m4r77+R5q#|W>|Ks+iH zsn{WcKyU6iZ=Wd4BTfJtl4Jr>mx1-rXIP$gCTfX9VyG~#S13=760#an7H@@3$(>e8 zG!sMTI)Nnk8j~bK`yVCN&-|;yClzc5A^U!72l*zQ{aU?Szr9X82qD--`fa|D@>6^y zYFret=8x}_&Nq>5oqb9f?~-nfLePf*Q27Yl8Qv4m^y};2E|UC^=9sYhu=^B|-&Rk; zbkd8MqT38{WRV~G!?v}8I=W$;kl~$dV)|c<2E1*EZRw!%IwLH>nm=QIS@wGw7yf&X z<@nz)?|)p$9#KAk57r3Li3Z-o`4eD|xoDdGjJ2zRxcgmxpI5)ech|?)KVYpPeD)lN zEdYam@!ETr?~hX6@bFUE5-kmaMBarA1F4E|;PE4?>2}5YPKF@OYSWT~T=m4!V~iia zicG$S*O%@w;u+2SdYk+gA+@5OI;Ic~qJ zwc-kED^31~H`M3QAnl{$`9-ZYP)|$C2AebQk6J>@_rONm*24WCa_aJQLz(Cw6-xNW zPirJ8H%_h6=G9q7f?baC|)G#*Y zlN+6#i)DC;3+tHoSZ?FnIl1!L-bRc0Y(RkbRy-bvS5i|#na$S5`1h4^!LxQqOMuQ@ zG}Z4Zv1Trf###T<4!eYes7rxlJ}lVgZngSuTZI%y)>l$m>-!hw5mr_f_-4RX0r~2t z7WOM3k1V6I&Bc3x-+*iE!7IXC2Kp733oU7T-^1LCMG>{Sh+`y#15SB+N30TYcUP3~ zYlAL*EaH>>l!@l!kd|0W80CY7M>Aq@esXdjj}y#3XESfHFoeGpOXT4#7uLcPY3?Sp z;o~VK$;H!NjtBL=f3nVY>mXKD8sC0H6mJL++B$se$v`> z+HYxU0?xgsymB^K;W$Cs8eAW?f0hi69RHUpaHD&K~%1%ITln#4;eX8tWe1U_ohBG6G75gZ#R9_5oD@j&aR zQ;uddz5Zlpc;15dm7~@0yYO^4k6Qu z1k?!yYxam5-j~CMtyAX;YC$E#fecbp@KmWohAQTfVn1KBkd6wv3@U|=-4Xy0aT+el z($0Y7Vz;G}<`3~FhMm13CdsrshW-XoqY<6zEeYQzem*FvB?5uR($$Fo5|R?;u-d=b zF20y9@QvJ}4uoTU#d?zhmHXQWQIUaCs78ocQzR%oEYp<9Z<|~0@B#_%U?7Gd>ua6& zOUVDi0&fa6fYc3vOrBp${dEa&iOeCm8bZC*O7b|aToQFf?JxXiR#YBa4*|s#FR!YwV%S4~|sv|M_LjMXTGjqJYLKLu?rS%45`;}xa z+4P#hC$6xVq=s~3#bAlrs3|qYy~1d7`nvDJ<_?*prSYX6xp<`Z=L%=Y3HVaEMU7kjx$kwcL+*1_Q zDN?T&c~F;uR-Dj&eN`Gg8IZ+TLRmwiOpvxfe)iaUdT?u6CT=P&fhpq4zwIkGceuLaVI>< z!m9}y1DW%H`Ri~Zu-{NBkXT)Dx>qm|Nee$=x4UT2%E25-%XVcbTLuqWR62)GQv}n? z#f8={BobhSL=-V#R1qraM(6v*Ru3;&@;=>MZ#8hccUHz7BJK&`cD*hKB|3&6vdo7? zfRts9AbQ>#D(B&dGJx4r3OhldfBJK-6&>!NtxOYb2yNF5vaw_DeX&8!a=(Uz0nD(1 z-;&4ay~*pR&t?H|wT;4RvR3MNsJ`r`?>>_*7z^8Y_M~QP*x8vtrw}DzdFOp&tm3mQI8 z%8i~K-qyUhB$UaU)>5KG25qP!7q*7z4H<4~`}V%<(AA_Fuzysio=2?_rw=S6V}$7# z>^&sa)(PL1SqnRdNeoS5hTq|;Jm^x9UZHA?Hma-88v^*yR8iKyDg{DTHfXD zypXM|h3(A&g7an*#bp{_T%E^66+1>k`_0L~`1B6~3SfI^Z#O!|-q)Q&i;uS*=*T*d zh)XgT3oOj#qrluBLFUrkG!&)FdVR&e1#NlXZ3S@rsF2Kt$k;G|N}GJa*JM={-fhqt zfAN~;r-3D88qV|x#b2&`Nx~8$@}Wuf2INea_+r@s#)HV#ex|L4wAoE?MVq|bJ8pEi zRp^}TJn2?QZFC3?5RjZ^jj5@VyB_vha2kOG z+*Wnf+?A`l#z4tla2a$@m%73c2_eytiSyIzV~lr#na@!b;NLSzThiq8hthY~ z{23b^WMswhWGE0l)$1bhjBv@&YcaqU4l{q=-vMepp!37~>;IW`FgX6di{Af7a?8ZY zk>W@TOqBqW1OwQno)M+x7f~FSAxO7&&XV(z#c^Mkn@_W%^Q!twXG_|)@%aG?rSyAc zeQMkE(*O|+7;z_2;-_UxEHzdr_tUaX8asA)+Ka%l^GJH_q1D@`5--9l1r2DBB4-c( z_*hB0-DH{JbR*2rh1FIO@T( zcdE^sF{Hl1dIT`O_=|*zP>3{&1b>42i9*KsJDmg&{?7Ni3;RqE!q)-G#&Qikq^e2h z!{=v&Y}VR0-c7x}A3BxLdkz$ zdA@*Vu4w$zmRE*(e*Hh0mlsL23toXPHyhW-Qz{5IJp#JDMD+$->OhCIGq6fV=X%;pLT9rLC( zswHTrB-umf=Kh$}p{RV?hXd{+pf|SeELTu&bDnmO){9ugNwWq292&QVw-d&VJEP=N#xG8cCLWE{xx6*WC2?hL358f9g`r`TKQ?kX=-$ zDOpW^4PFth5?-38o44h{Y}lLx@5bxOx*9c~#h)U`D@h-KT+D6ZOw+sX@6g}MIaa+CQcDz4#)+9K zUo#avNg|ZjL6MUZPoGfF?%e`a>7nJ%3l|j6!OD{GvSaRI2MryZCBm&TZT0{|`aBQM zX8#^XHRmGJ{Sl3MS3;t8xwX6x~Y?pF(Jsi@7t77lmY4= z94T>*{9Hq0zt>JOmM@G%TVM)ZhDDFN$Q1Y3LgNuDh|I@F=-I4dwN zA{43a5IW=8Lh~6*=rlrOQeWc+`=gMX6ZORMyt_IWEszA|7%HP}ahp-bgzZwS z29qXP$h)?)Y1Z8Hu-V%AIQh#=#PM`y+&A;3fw?cmKPc$|T>#)G?M&eA@MqRG{um?+ z@-hE*x^lZs_3Zy`xv;R8o;>T$vIEh%TZ+({T^T7Cvt{eUCk%3Kq{7TGwM?@|j-s*I z^wAY+At26(O?f9G|LqIpH^Tjh3irVDn0V*U!p4h?J}i@pK5Xfy**7&MfE|KtdVfnL zI@2B21CmL_@&ZuTNnCT~MhZT3v746&YZRub)&S_3)(g3X(Gc|Mf)nhO`fzlk5eJ?^@B=;T6xF|DA)P-G@1kS8*y zWEV118mo2*(W$yz1Dr(w739%r2xy|{BOn-2{%R*xQ&^S;>aiGH=w;gpQ@o^n(T&l{ zR_Bu@lU^|N%cJ2zf3Lq0x_7I3#~U%#j)!A4!^`5CqlpgeWN0P5XeA!@Eynj?yOWlB zOeC6t!(zeIqc80vC-ECr-meMyKgT>K=l|leXJTjnZ-$PMg$%5jK%K=7FpZ0+mVB`2 z>hS=?X&4Ss@0)Rc4XWx`|rVIZ~EuYW(1{FYzAh{?7_qRLv`96x=B zuwe|JRAJorLg@<6>Gr+<^tu~)V!#0TA;X!kh&Sb6{=@kMZ=3a9hCq|dDqN?u4;&>i zRwZ^3gD4Ewqb*X={IqTW(5+aE7o3cXDUnyuK3IdDs)Vy9tMSk*!lM%zuF%zf zG*#x;qjgOlst$_M<^fs%RkFMuJ0-8j*A);fG+d)+OtW8HbhG0K_^V!nf{m96cRc?7 zJi9O`8ykw($yo|wJGvGU9C;C);<(YS%~4!|LAclf2Zzp6#w@J0o4B=+>Uz?7qlYwg)l>k7;x27-)EWOe-hbKl@N^8%cB5-G52?JyyTu@c@iy zb!x&~?CJ-((QIc0=#f=i-WpubC@%LP3ur5vb5+KBc75?ceVStq-c?2X2~m)#u!s`k zeht=R4U?^Qj60>?_p_t6lSkRq*Z1$V($=9*C#|Ac)1FW11WB*rs{H~Z(rO$N#Mh$R zZ(&}7YFvo};3zfexpTFf<@PYHCckCYkRBD6N~nDIy{#WE&JxC$WF^NA+fz+k&f!dY zhq9w)nROTo1gaEABZDm6*AYO&z*QWAmNa(J+{FsDBUkKfJN*hBl$JP z&6Z&OX1ZGp*x_~KpDf@e=umT#C=pGv|3`cLKCfmQLNYxz@E-? zAraH8gI5n}ni8#kdmmjgb|_$%?n2H~@{zj73Yo<|fVVG1{Js0-FZu}No`5+i49Y_$ zz2=X)8gC{z6?2qntQHO&=Z_CFVHhyDDKGqWge&I{0Jv;8_7{g*9CIku#Mb^<@El$? zDy(osjoQkUNylV!5`NBU7yL(HcZ%C-pE2VJoGTgiRebQL3ZqgMl{~~BXR;FKeuk?r z0xVUcwmr+AJ+NzX^jtHnuaL(Fb6@k_5yDyC>Da%Si^+|R$pQ=G7y7ZZCNO%tkyEK6s5xwsX&({XWuNi`!$=LY}jc z>r5MIv)%d-mgIRITm>INbBk7B6C@hMVxVs?V_l({)y1&YZnqo8@OM>AW2Dm@Y?K4; z?^@hN=A%TkUcoEhZou9B!v^;=DzYC_($F+s*h-u$87xfB{VcmX{pd z%kOK9L15DchxwxGu&dvlI2FdQPEBfOREXx_;GS#V>q6}QDliKV#*42{VI+-;{X!=D z^GY$b8v|~TRq&PFd3Yjh=YKtEPt;nQw-$iiTDV~)HdC0*`JvW{mPXl{=nnp^fVgdu z-D*Fv?VsD34&>I{%N_`lL-5NPP~1Mkh(4+%W3<$w@-n33ks)$U^3jp5Q{8RRVuuT> zOizXaZL*soz3=>JY^2zCb6q!S%Alk4qH= z^zU+v3G`6A0NX9DTl;@cz6ONKa9Y)7aZmj&EN6Kg8vWkLl0wM&?b+nZr?*LXBOr+l z_rUBjAbYqaahGA+RqYRM&29AGN z0=X8hL1+rVf^dX8SOZuZEg*q&(m<{9OH(6*M`U4U$J!)CgNY@R!x>P*GX)*H>MfN`!`f;>zXA4X|O)M&Ln-BxZ$5Ix!=Z( z4Lg#24lu|U`k*7fIsm_geXG4=&9*{^U-YCCH zQXkRsv|Qe)Lm^dgCWE0R1*@QNGxo4tBS9AE{S>S{8!Ro|H=5vVRduD* zv{JSR>=*?KsMQs~v*8W1=s&7X$FXs;o@QU{3n57B4BaFDY+<{>gM8S~j^|sA$d>a% zKgJQS+L+ij+%W}k^R;rTt){2D*+&I2O=U@3~ud>z9Jc-U>4FwytJI^ zkpqwbE6osBlx$PWIpn|VH(O*t7%}K#NJDZF26r|D{Tu>h#ohWbMUMRuzQ<}b#GJsp)4gKU~%10v0n?YdsUg>rWlUNrgPrM87(Rh zF~ELrLk2H^w*EodA|3Ufi_Rh)+t8{)TpwDtwmuUa7I4|uL^Qq{9X?~RNg5?Ik{CIv zCnL=F0qYm2E=&4c6~^hfU?@8FClK2?yBu5nq1%ecL*<%#qKFLPs_-Pzb>(D8oYxp@ z0v}?k^X?~fOCQ^fH>nz)>vGYt*}SKc?nCWE3<7IFeniPI7Cv4@iN&v6p&4T!107OD zwkV7r`R4>eU5Ao!ZYxm3Ao=#`P}IdF{UgBlU71jl!rZH6{@B0+^4X+96`gS@=mGg;`zEyx=Tl2U7V`dP04$~YnXTV%L`DOg}*=qx+nX=xQC$buh^KzHF2F&#SWsw*o!vJ7r8 zr6!t6)d#sYz5X5`#F39h3iWcqZ@T*5NCgF(fL&q3jzbf_&!LlrhV^MtWgg8 z4eNd&{UH-JCLyB|zGbi;^p!pmui~=}FTx+rrMj|_uXa+39X2js&3NxXQnU5tlXWw| zY;{;BU6cEbPN>+fTkvT$WeH&rK*J(>TMskF7q z(nDBavUk%<6|t+>@bwUw%kGXJix&rSm(U-CcJ|o1;=z)3FK2}pdbxjTkxwmrFrq+d z+1H$N2XJJBbf*3yv&ZIBJ|amtw=|LJNhtH)xg%wm5R5UK@EC=?y5S*9{1E3=eWDfM zTf5@kyN8Y?07oBJr#ACH2MfkGV34T3-86tG;5!uNoBKt-=w4;ECSJVx3~Ep7 z)ym{3JyA2Ih`nj_m?E`88--$haNwoe`yLWo3P@+papJ%oU!Ceeh75;AhH>YD$D_|j z?jPM!AARAthgb57kQ!O5AN;@((Rqn@<-2dcPpZzTM?Gj~7>-(Z2^(;AjIuPJT|-w7QJ4NqG(`3X&C* z@!AHKV^KZEWyojc)g)+47d=}C-`%TF_i#huWoy3sV3!w_^~nasej3Xb_y}Jai;VER zVhT`AkHt6?=6&884XPIR*?Iue`}OsN1)%Z~CUCY{I$prtunS@r_vkFIGT-0N z<;Sy*9nAy}Jy6tkN7S8&Wh|p+co;}5ATcCXh^Q(gPVkJ)<=k<3Dk&gJ@}doS52+Me z2G@#SNM!jX{mEM`?Lo*7!M?SUm|?JZihGuW@z>#?^zD8VDsVem|Moer)XS=2v{?{XT}fYyTsyKc>l`ezUD~3u^MpdH__0tPhmI zC{5Ilw(s`>fRKa!yZd9<%<^7e=gK26_o#HN2G&5-($J?U=#}q-AC(3p2S|5*p1mzC z9WksN*R93uiOjPK^+Fjv^C4!>`S-llVC1-BoN1Ri0$W{z56m z&OaZRxToQ)bE+++$>*O?0aDYzK5CN)`UKbb&3~unsoQk32<)Q%xx9dY!ew$K#@#0- zLgtUCcP-U<(tsIIz-mPU`6{I-PSnADTJ9$~XwaX!fTs!oOH~>A}e~GXRGcR_-P=^MMt3-Ys5ISVM7qC%ZdKSRicYwco*!Ft3v$OLgWp8Ayvx=ztavmpHoK@DKk4ZiAtpc;R?}- z;RM2IZ&p-MK{_e(O#@?$M3_OHx&Mq6eLFkbKd;^xZXxQzfq98oxSpV3LLgwKPvNZ8D_R7GpX?=}}NBp|qOd-yflua*&W#QW9mX~Ha08q66=f#5W?<&;AL_=qxEP$R@ zBvovG9~0tbtU+)a=aK0zxN;!q?_fdC@#ii9SVqM{r?uD&a2E+PNh$~lnxS0~nvmq; zDS&2#FD7f#CD3Tf`h7mFv%Wl-s6qH$LNS;_#Iws>KyT~#NW?cLP}qzmP-%r6KJ=!} ztj{u}=*0%4vjQW;n$-mD;E46~<6;&FkOrs@LI?a_2MpP8H z%2^n=^9jf#uL1Lt8B&_qq?(lGBANP#$|e6&7-C|>&pH$hKg~m3enJw?Wa1W@B8)It zKovpxLm5Gh6kv=HvP)@@8m45=4J9)s$m~@=phZP#4=qi~!(jn9Ou&R={v%b-UU3@= z*GrzSc`5FdW?>RH<}uE!V{ui1-bR1Xzw{KxSgEJou`o_1YA87B=nvV$NANMzHh zHUrD7L7J3>iQS*O2u(LF%qU9XeIrmh8I+@8kE0MaionkSV(4&nnn=TKn~aw@ z#DxukyObS{h?xSsZitx%AZ|eBsX!>Re$beW{!4S9c0nN2EFr@FyoNMrbWEh&tOg(u z8wHrrRAsBquvSBQJ|GL4`9OInoT5yX5eks0VIh85kBEy$8 zeFmO>j;z*E%Ik{xUs3K1YAwF6d(XRj565PUqzi6r=S5kJ^Id^75>kl4D`a;t{@4%Z zGpIyTu&ES4C~2Mhiyw#F(KWDR`VAOZ_@p-`QR@vp;{7&*sPWG6Q)LEj3#@CvR5ypx zvF(Rv-nA>h+j}blonReeT^8fYg(;;q`7FwFVY(w@tuX5ay7uP1%Lm~2T=`KcXF`F* zRVY-VacmRPHVQXUMpF#lq9&7}LR=Eu2TD*0>K+TQ1OiFpQ3ay%p4yqo>(KFojK}4A z{OF!G#O4US`uV7zWxD2aP*{mQ~TSZ)tVEWQkx_KM&4yz4Vk)ggk%VWJ$EWJ^@ zl3hD+T@jua*5H{i;7C6oL~(X=>Oao(=*sZ39c!|C?@W8mJuiM*>7|`eR0CqCWOBjF z;Q_XB@(!!Inq-*ll0SZ^5u z)N-&-;{NoUQSHYg=3%ZYS3K4$l=atpkbv&Xi$xBZlez1QMU9N3QlX@BOV1; zeYax6ammgb!{lVE@g>KXyyh#^fiBIq59P>!T3C#H1y&+^j}(Q)4^u~igR_5qtAL!p z$G5w=`hB?1C8?ah0iB*?IXQgv=qnF7QEv`)uO80T6kADkIr{D?Ed=dn9d65M7ZY~x zXUUUqrtWst)AYr~|31bi%~4p|x|wozub<~Lr)nluy_cr!j?A5WD&`&z_fkK_t`r2$ zz;JdfLnp{!Hb1{-?Y8NEHzveN{uCP}%gb)r1^eRQ9dSn{*KIrK;QfAEv?auV_@Ejs z0r6UGs^tAmC^t7g8{s(rI$)tiXb{=%U>s|*8PBk}4@12f_Rq`x)m#S>mU@6l?H6fq z>__C=J4i|Di{mdH7%jDV-^Qcld zYFKWcTb1^bwA-515qT#MLPBt3q-UZs#oxN#6zeCG<)AZHJI1dKR2Po*Y7x`@T3eyLE;aU>4# z?n=&DVSYc~xJY{JW+1av@Eh%+el8?cMJL4Rz4G*ia#Bn{IOtW7FrP;2jcqPk1CCBu zAXA4T%}|`4FQBMvQg7zg8~u~T^bM2^_;>ez-95pLtYBDK;*e0ln3HVP|pA!t8B3HSF|>vTPDcgu8+1xRj$Mlk72r9vSkN4~GO4TM-^wAF0An_(8D zS|B%!kGl<7JBY7oey8^;k3=AVQ7yyM?*E9Tbn5C<;5NRZD#3?qOx8r&X)74C)>f#{d_4GW&pM6K~7D{kup#J2uWMgLa7S=5aIQWB&3nMMtIdBHN71W@u zw01|jNARf80%7;`;}eoRbH8B!l7Qg3^jN-08!Lz8FP6mDr*o3~!2`NqgAR>hf>2NS zA?ZxsVWRR2WP)FM_82TR))|Q0-vJk3Qz72y9#sXhRK2}4T&!ou7_=vWCb-iLktsA8 zjfbno{=zr;f#PL=T@cwq5j6PXnw5s zdp?KUel#@fMSAKZ;t69BNH18OCqdRwF7#XfW{b3>Ilw7XcF2k780TdOy|O_<&q2G#iE*R`5-DM7?EQXHggH&4;PPFAr;k*?G%r`+ z6E;xjFcX&4wHO7%+PQPk%2@#>WTefkzui-OgPDDj`RdP{ZC7EGi}`ca<=4zIhs}Ho z`-MpW$W6?$!*#L4t^emJ@jTMkU6AgsB<;{dfZ++=Fzh*e=nE1~{3Hii&aNt;96_HH zbp*K@Jb_@geX6^8q>SK1IfQFenRftGt^wikZ@Xb?bR;bM;q#FQCPEOUfOz)T;!&b< zutIEbZqI0J(7IKeV6uQb()U7@+7ZD7Hy_swfV(wcNyuz1)nkEdV&h#!7Nq4%rYP>A zX)^wBiz>1;+TVxrXB$ASF*|%&Ny3`kCzxjibAEQP23-qT`1S4=K}W=sdS7n=Iux*j zehO+fwOdmeXXh_HCD7Md9~aH~hcXk$S;c*3wFZ<9`@6Lp6H zpsv0=P>46Ctt#Luu7{y@nH{n$ecues$cCvS>V{4K+XYFNVPP|JItcW`>A>t3mOFN@ zc;9H1<$M0-ddqN&Uj73|^gi}6ew||_hZ!1?>ri0J3;k8 zEvRmr%umVK(E* zAo}~$?B;7?zhaP>r8yjr%ex6%dV1+yG|5VGJpJ$Y=c9+*w0V(3X1EThd9#a=O+zK8 zw-MnJIuG}V?#?u;9e=IYHjEimK$DnB%!Vk2*r7xCcd)LDVi>6bbiCf4^}>5Jf~EOspcK03YG9p^kKm z03ICO`VDtg{@*%Rx=^Qy^g&7I@_shS^!7A0(cI`Q*;b&G1;Jdfa(2HH9#=(9!SJgC zH(1in-TE1HGu^9qlD~`u9YQwa-W?bEtLCA*eRLl#rIXEgF7|0@{vNbtkSZczl4i-6 zrCwAAML&`J2J_SHIl$S}2fV*lg`He-F$-`8-K1#l@^9UI#KK5t2^aOo*p#?_R@_1@ z@?AgAn4-5)Mi0?iC|PJEoGqsqj)dtL-wA;w##sC~UMUP$h|~~8#iF_Sd||Vk%nsxQ z%@2v^5+=5pV)cA6e!0EVzAfTCeM{B}v}%?XpsXGfIVvPY=ed#F0j8xT^QK7U>hJ<; z2rYJ1)FQb9{~@3bkhMkFZ`2VNVXGxr>5hr|y85}_ zfdVKjM9rb8=yU|uZ9H*w)q@UN9%WWuiycYQ<@IZCi`b}KD*O9f8Wjvm{Az@L&nv)` zKt1Lc%L5{MNDbJVofeFH2++!{eUG0WmI(wGk3Ab%wJa`nN~E&CF$MrYAHN7@|F6O0 zf98(=Lg_$$o{Rs7SE8lq{DY>Y5RU_(hs$;SmKA?z>ydct&KYV<^f!AB!kz|#hrl7?XeREo+hq|Ae;y{5E5aG9E|Jx78>N%5`Uld2LQnNX#@e6ff67tMN{9}28u;W9MCMZS&2cA z(DV6uJur2@FcqV|`qO&`be*ksBfe9-Hn}WZFlm-*AqGSO0awzZ;u|%^Mi5U1V}#_! zk^YULV&`~MP;qtcl1UpLTZk_w4Xx`R7KX(7*sfoZetFTfzn6co=W{p2KiiOrfBGgu)&f2dw&oyocfTg zd~;st{SVmQWJ^p`33|IH!xYg$s$q%w?_3E%3(J$PtqYFW}1GYW`}J30J97 zNI=0LOg>8tPA-cjep^vWcow)QuFNl?!XnO%7Y39_V&aNYSgCnOw}_&M4Z&B>lTMM- zE}OJ$Y_8#Gl%qm{_9QwCdMb=g75jZV5u*Um#;$()e=XzBuGcw@#}+x)_kXU;Gv;SZ z5D+%Z`IFw#%5Cb4DcybN^+orC8d2VU_sqB_n>Ua`%fugs*1Cu%TrJC4@Fcq>siIX5 zQ37hW*qLBa4cJnh7R; zZZTrOKwAb_y6t*JAWZbo=JDiW+qY|yRR9zjYT}v?W_-KrYC`;k^!QZxUu|3#|PlRQDnai3H=*M37V%%*}w_H@BAX&KIXC9@&q z?oI4r%s+8?d7~JOPXZSz`3|w{d;!r0Jp?x$evA6xWh`}<9qE5(UK_>cF{rwtz8h>R zy(OI?(Ap;kXn-<)X^-i>n~s zc;5gVR;WJTJWlUaJ+LO9E>)C!)V^sfdU6KAj8ces4rN|Ms87*+u~*a3p8>!gnXA3m zY)-$aYF_OuS0+09Gc(n16CPmRn>@hJtC6na>AANlGe zxr{gp^%cfBkV;}8YC>Eol|io-mx}n68~x?mlBYi7ir!@9`8`D8yok9VHCDsWtbigOv`49R zooM1=inh%Ka(nOa0~C|y6G;eh3k1pzz2bn|Lc2CO3=D@KK*vIH0O~nY%II33?VLuU z5A``?otK{N-z0*S48VT%-GHMw9C}gg>D1mY2#T8vKk94{NpiEI_L=nZSL6Uh2jgX> zkXaa2lW{VfX0p9$v!cpr99WI?7dovE8{PK{c$po9KPxC=vL37(Lrsn%FMU4fHHUro zs38&c3u&JU-gQEdXkzeITVn3jyw)WFWK_3Oy{U2W>m(g5f&lX!YO8Lj6->cb-s83Y zC!8syndI&ri}$L=f5Rz0YS7)}!_YygvV=z27Kr#J95Pd60lX?9l>BxYiwSySaMTw@ zPt-14^)s4#++E!TSHk+0N8IPE{Icmpcei0kkhvUX5ZItLkGNp{A>HKt(fw0@bqWyJ zM{NIjyMmC1a0ABgKVI37St9@)zvJ=CG3e{RCstr*MjDC^$}|50@5lo`=EIy8uoV!_ zaH#d%=$9E1B^uA$f)+mh+~HLf(7i6H}DpdtPO@f&)s3sg6@r156QMkdq75+$1@eO-oP z(aHc;Bl@YHPA9RmN}kI8$p3zMcCc&HJ)L<`H2vcAudhET-f}T*vo}1nZ;GVuhjELU z(&9Cc0Wd(lxW5U(Wazpf%gFFUFu&aHbLv*NDx)^Uy2d}#)gR%w_dau;RUBLXe51pu zOF_ZJo`;Za3J9FQH$pt2^=6?Y_HJRWkZA%Oc~b?6F-XaG$X>a z?gQ!)&n8=6V7y%Di%{bp-PqQ{HuMo9W3}0d$+W;ifa~BSh2!rNEaQ{MjV=w$=PtrL z4C$_q{#H;Izn6BX%HkA)059cL+7h0g`p6K>K+p)Cgu_B4TdT~=F)#&|SqJb0MF?R~ z(*@vSg7qTpApSF$26u7Zy&x@BL_F)c;yRQGB=lKLMZ*tc9HI zKCLh4vPTF_nQhZ67e}(##8sSa3)V#@qMJJU372RmE~SvG%ozZ9YKz;hP460a=d z+cwjb3&UIKqls)_A?jSF1k^ZUVnl2vuqs&&wu1a(lDMdi2N*Mb4nFm)ir_cGn4;L3 zIJ-ER8ruG6QnUZhe8$T8-x?p|e`^Rwn*VPNp;;vfqA!6kVLkA{fjUF0WPg-mB=qiA zrD2*dHK};S*S}{_(oj~@v7}2wYiUT3s2}yizb8Ap(qk zX@IiE3(gjog)3-xcFP6rhurTWi-gi zOJUzD=VvVq@NfU<>8RrZpeylX*8;TS5BcW*G!fWu;eJw~Sw2N6RCstXyhfC1G|A(p z*yTFpnsh!LJD0urui7pZ%b5gQ)w^AEK0dgsJ7m5Re2!+HwZ%@%a))1R)sJ{eHtn_r zjgxlUDZD5Z#ob6L0GtZl_baOTcVUk(@jLVkH=l(NA5u39%+^$jB=&I_YZkRN3v^qT zWHmCX=k5c`mEDyCUE%>=dPw}61FNlDQOC{Q_pPGoz33k4BMaY|&s-9gz5uu-y;wPG z>VHA=g;LUs!cvR`mH~+*+O?J+blMSTJ_DI-YdMsaELYX3fS?r-*KzuLl_|JVy=#vj zO(9W=i_E7kSGbq&6Z68!HaiSrs;M8w{Oe4yX*H)9P<`4X=)zJup=l8Akl(wt(eGgz zLqa(-we{r>Hve|NS%tvCI7{Gd9SwGT*c259-3%r#hgxB2JP;qg_=T5Fh2_sHghM=B z+Z}aaU}%930DjpgxHSPH9D<|J=&mCkMBE|IE3y&fSsbiS@jPY4TQ6gC_T>0-Se&~J zS0uQ-xnYo18BZ4k7Nw$hsGN8V35DhKz*h6Ipt3pwXqoLnEhk+4M22t>o?Pf^-(A`! zS-gTJr~7uVzhh>RZD2?nq-_^dB^_DVHOz9BOof9xU<|~5{5`~&U}mT*)K5$p2Hf1a z{F!H~WuuzG9q2o>wP3r+a07f>oLr1K?R?N5P@IG_*)_|vy&%%mS5`_Uhh&Yq>o8ju zCAj0zdEbvz<7s4b_a4i5yC}SRohC&Hx=x3498E8qR>}j>_Y=r02F8m)ACX{3js|sa zQcBDN&fH~Px#}^J4S_luLpYv0FJwm`C2@c$@j?z05p3S`Y@=bLH-2v>?baVJGUdT? zZ~Cjn4F!2>5GC#%d&xvHmW@}RJn`)(dK9b=fUo#!L|Y=Hio<@w8Da8-wss zI%p?-LJTESpYFHH2$d5nj%)Kh{wz!C`4He`P9ayL`pRhZR2{RDHDYUig||*9+wNJD zTUSejY+6;}Jzo+3mG7kNEOZ-yhPLe$gVx79rd~nyi%@7ijQrb+5PBnwK-NT;w>*v&zMl{{ycMh} zLuzO{x8j=+Hao%i2qO4=HE_x`Fj;h)UKNN4VDiMl z=knY^3M24oXnC}HA!ooY)b>d{d-{U*1KBAe*o3(O!ynOWH;T=`fii5>a~IR!LKexI zh<-b}7HS}Ea||(xPAD>O_<(55jfY5;3VF3+SM6cGUzCL1uDx z*t#EbF_h7zL&95L;ukL)9I62-z^*omUTDKk9ura&se`?CV?-7sA?p(M?Z|d^4|DNPt4IKEs@tc_wTe@O+(zH-AD-)(V zsg6QbS)MBw5kfc+iv*o=lZp;hOq%B2X405pnPgbr6pjUjbPi*-wNNnzKu0hOK}qm2 zYb0-Yh58ESc@{+c!6YBrI|82;Ky(FW`QZo$gr3fQgK&R$0!VeU~0@~4RIUEQt(|H(iB^!KG767Ec{l&4cv1HhN}$=L)eLr zj%D;^=7GKc{Fl(K;ZxEAFz>KCPJ1C!?#VOiD?oqt6@ zT2zn>8{t>V17@fsEVyRhx&wUiR#~*!WE9?mg0rvRWC&GHk)lM!fE;7J%2!~PDiZ#~ znFryoFal3Sr+zI2G@RbU+O=FAOTT@Px5VK8Nnmv`nmmkH~KkI2qaFH z$9Hix@8El3QZ+i83qyJ2d=LVaLDaeDac7j_q`8|53-b?WB|Zv2Ay3+cr+mJ=ZyBU#!dVU5$(Hc}BfeZ&mpogM~we z2?0?QSu3zrrBmC7Y@RRDEGL~O8YEt{6^U=@w`Jc8ujsfZ4))x40bh6WAzNt$?}xMB zc{RCy0H2B%Q96qp%&02-_&(+WbLzoG?_18-d!<^YTE;E?&r949`+}M4+Bf@a*iVO- z^EI~~OY?YmQms;BqvXJj-@rtt5}ANx+*AXG-|@V!HEUdUIce`v>J)atCVoD&moGj@ zpK?^uCAHh?9eXAjYYf`I9hk)Hd*ZQ#%DjD70RO^a6Q2*iQf<~1w%jSV1LY#^fb>*( zM{b@?mF^y4%~~Ghi*7)hmb_DQs}#!jN{I*+@q%>KQ1J)VO?%%FE{#uA$gy*d4y{Uc zY5pVk-k_H*rPZ^Xd+Z*g$ zfQN^cbY=ULwY&QE&jow%dONPLA||GX?y-Mhr;`#$?}XwI{cQpNd)*8wqt%y6xaQ^ZerYP zYPZ#LVlQ!mw6oRh(ogv#*Lvc|6OETvS^;ZY7)% zY+1AkWLKAg#*8)OeW?FL-N6A1QS&cvab28zR0M{AsItka&S4p2x zoy<_Ej4DLp?Q9l9MSv$?hI9ru)wR>=14emvk&pwv8BO5_?q>NUz=AnuwvjeSLz3@> zEVQBIa59Kr&N?7>FnA}mj)pBq&SE%^W(s-Dw>O^vnpO077Oql9gX+kauq8IIkLoMNd<=6Zz zYxmWE9fBm}*LpEn016D1ZPTz`G!g32_ z3OUm^NF|+WPfl+pSCDc=mmPJJ<|$wyyynNxf8DDoJQ#m9gxL!JQUR0wMdw1{W=`_+ zF>cV)>IaBVK#ELxSr8A14JKie>Vimul0FDe*qaIRp@ve2A0WSBGf_zFv|O7YdH5y! zOqNxIX)hjpKJ*R$dclp~YM9WE$*TK!z{0!nfw%o3^3?bT5j&0$uhvX1nk2f^hOGSo zv6k~8CGy5KnX>@`L zzf>22FIe;4I{<1oQ6f_^Kh9nb6p#v;0J*FPXeQqd#Coq2qMvrV4cR{tvmh;Q{E4%@ zGG|Z=M9gvLP0lL)5>A})vbeH#=t`UAvS!6xU?PydUDYd4$U)3~+f)-CCkN`Gp7qe8 z?d6>l_XE9CvWihA|6&B;iDuTG{uNS|eV0N=2h=i*&I3yG1|2ep?ai&;N)fsVR-_R7 ziZdxeectN%waK-$6O`4FAP?BMZg3Yu_OTI`A;g^#1=P7z!s!qoH)NuNV&Xfoz5PoE zlFr5Wh+8kV2yr!dZ=h%9%6K}2^BH(HPfc&*KXY&@lqR`z5n!q4hZsQDe6WW8iO2)L zf68?Khy|2r`#ggok4wU?ma7?)PO3RuG=DuW><|%a<&YgPa3I51PkvWic@(yn=@#za zv)!+7v7ugh?SQkUFNhr zE1$+$IVC>K8=Y0=Yx*B<+@`n)v;WLBk+;IGBRc$GWl%=TK<11QTPby@%=xCo~eXarC2U`GHyGhtu2+ zkI4nBVrz^CF~jrqy8nrDrK#H*5=wtYNOwMagd$rcw?EHjedn!YXfjT*{Z0_=2muJ{ z?%@g^RNp+h{uHz+ciVFHBZ(xt>zl+p9utbx*8r3{cN;8A%nyo5A~|^?Cwz=bK$J*f zqb85GtMrz1*m3<0mYI6ES>#+|+Bq<2375L?)N=H_;~nX@>brH)-@;OmGgmiVPOSk4 zk`<0w6_or!~$@s?`=K* z&5`}mZjx9|W-N1UruHRVg|sqd_T0U9gzwKFl=T(f*qM*F3>;oIkO=5VgQ3DU3&!?v zlX$gB^1i91Iq zZZT+PRghSgLKBMe#2W(df&kU=(N8hkR?aU0o*`>394ih98=+i?pIe}Qp7coOJr)^) zR43gp7=?Ufy>_~ZB1sScggbGY^1@{|X#;@Jjyw{`WzFdeBm}gZcMf%8YbZ0?k+L0f zhu^)4bA&4?YaQDOf7x{2wz7rhltbnhHc7uI)Bg>QVgH9B%As}-~wz8z61o?Cd!CG(WcKLn=IWT8j6(pJ{23jy4n5+MI|IcEwI3ZWDS zYz~<`uOkO&i|oc>PCEB%^x;zXW8FXgy_OnO7Sl&1p8vp8!zd7Hp!V9(Hy?ECn zx=jC7qo%zP3KBO+R0Xl@Vc-NbJ4z%oHtvSVV%olr;!erG-XU_Wl0a>VXue!sS2{cK zW9lhQ$F*f_JS-kn1@9(W4nP`1Ats3jMRmpZ5dR#FVCAI65YcVZa$U3tX0iaVI%uxD zp<@gXxcDWb{!qXZbja#{Yd{uo5l}^`G(US8if0CwqgIVma0Aw48mnY-|4|#Mk6=7w zsxoqXPhbUByonG7X*D!F$F<`x^!`v`aI;A(h+2To#R|HP#z_P#pz)BCA<}E9KnZdj zAZ;W_AhB>fMDor1qi0xKrCVz!SUy2sV6D}{}6*q1wcwUmkb6IJ##5Bv}hS0RKw{bHX69(7V z_ZC_h2<$bV1OqOboPVcozMuxc^<1lFt0^!rB`1l){>J?}cWBh=b=U@gvcg(M{n~cX zhbbhC)xo?5bCB1x!KmA%0z>eW{tG@L0aZo^G0B7Mk5PB zDR~VU!Qy)dJCI*lW29ZImeft}XXFc4+iL&PX?wfx=Bb@F#nO_Lb<+%9v(hm5EY!L1 zk1t?0$(aHt8`fUzrMUtaEgYelBCflA91URO-`g#VNS$n#5z)GrN=A;s>dX@(RA$ml zD}m;kCm}zNT%xgyh=(1Qq#w3)N-_y$uFczXmbmXe0@>YB~oV zORERi-_XuK9i9OhR1n5Rz_8yR*u8w-&KAek3m6K-f9cW$m`wvF?@dxfqv?*#Rj^!M zH^(kqg@1T-%f=RB1!!{TRDHgst)BX5@mo+GSGj*#_d-|(f{v=lqbQ5#sJFpBThgvn zt=bL640+KpM8(Jc+=i0#gv7}Gr=_EAU5itPK+R&<+Ywz;79)C)?Xfnu$DcwZNhn&R zEh@YixNBVO(82>eoa-;v++LuDNYueDRv1Ldu`sPRu`oSfna00w^)Y=O?NFA)INGY~ zv!j*&rua>^RiRaS8G%ehqn!|t`#wD}UohJ_e{o`lN}Ic|K8k~REa}-54g29edVO3% zqG{L|aW2Frmea^l^zdhw7nR3EWLA8Va356kQ(m7%wbKGLJz`K$eh`?&7>5`^t&wr{ zOYP`4^?J6s?3YW7!|Ss)>GoaI@5hdHfEYufv}4Jo_6LPm1jfoO?b}PD{X#C+mZuvK zRmH_if)jipfZHbPWe_-y5*m?z$`AHCqd9KBVhStgxuc+VuW&)Wk*y@5->smqrFm)C zfYV0AAS45b_0v&2pur$JLoc9Cnu73H!XeZ+dW6`45hf9mr0g=aW z9Pye(&bs%yG@m^{1whb{f)d81n1gh|#mBv>0|EpQ6< z4pmjO6d>k{ipj=JBC@)9Ur!GfS1-~)7X!wFBAnFe z1Z@CpmUPo-QAknVSNxS=TX?abf=CF%V>C_PnXgk*SoWk zSl+wsv&xiR-{WZB;?}=#2{yZg*Hq;mqzM3o=9dKbZx>nf&kdkMDErCnP8f0l8+w;p z7T!Kh0d?948th1*laef;Scx<{CmEa$PyzjcH%jNcSfy#NS#@JuuBf2cfY_xt=|ERm%=)x%-xA%RY~daopU z*fdpP-S*48K5o@MPp*zky?`MHW(fe8Hkj!qoTZPq%W`^vD_-YZe*auWCf-6mktl_# z;*B&Qk3gkb_91462lR1IhXlr<*dS36ykMBHVZD3DE0Sh4?=v(DbJ`(cy>C^L{H7SM zS7DbU`R&=4YTXT;9oQF`vR}>t21Cl1ej~sTi(f9`p{>_zf@%*A)tc2~C1b%!?Sp0t;9-Ph|9Y@7jgwTWqyq}xm#7%B`AK)1cf)cH2n zui_c8;su4B+7DNSp2K0Vd%f@P5N_6OQs5LiZ--5*zroR~xjanca7R`3neAZF@3es< z`R(|SWPxcreVNN`8#O=00su8IZWb6#1Xkgooudm&UnIT(-m3kV-1CR3AQ>!eTbryMePpdY>QX%F(V61=K31HeLbIQ7m@mEszI-I%G#L z9TVm`tFmU-dWwRUU7d>Ev89}^_oUyVigrX}zrBr}b=DeBk*t8>&{bbplir9tp~72u z{oD*e!|0Yo6pn5$LZaW`ZApH^|2OXWAK1f{#Dep0Wt^MqKPIMs24!8j|7>n`zyC8R zSDM5gf39RL1Jybd>oIGQWL5=*F(?}36LbWZs>btXmf1im0zne7EajjPNQ}BqbN!!{ zqq)BZr9H=rab-v|?ut$~qqYs0M+lLNj#$SWPODy0BgEzfcNj+hUeXwReTlL+C}o~f zzrO-HeQ(clj>szj8TX3Oj1G1GX@$QeW2}`T9s#`Hyloy19Z%#LI%Z~ z9yWY8c=mSv;Y?>v3B2U8m5ulMOf_Qgd`#J@Oy%{{8Ec^w zz|^1xt|DF7I`wQm^y2kFFg1*g3yX+aCLKQpDI0brF`6QV3O15)>rc0ML_H#7qyOMZ z&2+cCFHr;pE9qI8;MrezHg(3^f5cEx4k=Ia(7@sRvCFje3bt0q(TsA<`LUE`!}7;) zZ;5xqV5W4jRPp=6opjjnOv42m)w)isI7>rczw9rjh*0D9^wNdNUN*tEYr{AHTx1FV zQ51wwvQdzl@L9dTWtE1fxtZV6amB z-qEhZer?~aJ~ct29DQt!Fvp+JZ2#rfP@#GG-lZt{(2++5{I5{d57lYxmMI>3o0FW zL^5c=>Gq(AcdvE>bgZ>j4QzGKQA`EEWB1UrO_q3JG1>E(a#J9YGt<0%@Ut>VdVT@X zs!Pj3S8pwbxP57j--66CI|@pO;zLRGW}1s5If?WL$ED zkf{ZwDOQv{;Y4~fU)!|?omb_-r}Cp_UM`20a^Z5vopv+;yA%dgmfIq;Yx{aRKxzRr zv36nyFG6!t2ETNt33Bo$s@`?_Gw%qYbHo9M+X$to29HBE24Z2~z>W>4^<-&tIKDHW zp{@KUvRT23$YL&gCa*|iJ}}u}^C~?>z6GE?No-{E8=nc=?Cq^h^m-@8g{h6iTO2o zuIl`R9c_nL-bvE8dKOv?@f_50m4iS4F*NCSA-+9tm*9grUi;%aWMQmpd5y-irZhIV zuM1lwt0$w!0elf%Xe?yKi#Eev;$gYSya0NC+J+A znxYlinC_=9PhWV2b443;G{eS#z{a7iob#%n-yBhD6-#&eGxq0hd1EEYOIU%6<81c4 ztSDU$$n6MiYqU5i1n$&o@d%)=DfLYA*D1W=G%X!Qx8@H2dQZFioelqRHV77p zS^GLNXG@w&dgbnl!#M!y>`+3r+~m_+C9I62NYLLQOac+Sj5Je1YdJ~*2sM(Og?3?k zq`_so`yQ1D`WV>gcB-ghH5G3mB2{DT-El+F-fmJHp_ipL1OCiR!j!CDtCOh;fo5%+ z?2xF~=f)4V(yszmSWn@)X^L#M6bG@gQBRZk1ZN@ASx0mIgszI&FL;r?bg>)rN9AR9 z8nxa$Fxi5A74N zn00V8TQ0R0q9v@T!Fn`p@wY5tb_4(a?mMCJ=3eI zsz+zH#b4|{!ot|h^Z$hQ0eK!AmEkZ4pL4o)q#l%&qfI_my`@v~{WP+#R7(Ukeixb+ zhS^b53sPURn{u2ZDq*^>g#=5GQydcYL721k<*ql2NkVYW>P=_<`}!7rPFA-7%HDTS z_wO{~sci0;&j(CcC2JAZqsF+fl%QOrAgUbSJ{*=j`^S*MM>UI3V~b%+V-Zf-tLNqwqy=P_|g z2$+0G*vx5Qn{>AR){RgyRU%kkKp!#s%N(l;DsRSHA#AGqeSp;42}M!J+jHV|DOyorqx^bVa$qP6FI$nw+uFYQ}rp0|@kw>nznNeJ@A| zYGt)5DbmR(gga3IE7>978nEL=5~yZBydIioj11KL(5W=G9VziYMnMK@z570Dn(W=T zA0rtGHOb?aW=fl8V*RI^D8}GcD3OvuEb_P(&Cm4ICA2aT~I!C z6)amY9SJl;C4>!{`9YT0YQ=8ZvVNGFDD5!pfmoH?vhQuMz4*Q#_i{bby+X0SVQkQ;JaACTry-N`7L!+8g()Li%v`W^iVE2T26OppI%P>SV zp?cz3EQzEW1Y)kGdnWy3R`E> zf#b)wk|NC*h0(2GfXp64I;Mos;iT*%Y1Yvm)oVLIbd30jiAH)R^JujmX%R6s*x~h| z5+aYc*yTI^;deQ3lijh2RK1XSm(;l0al_TK62#PD-3XlHz-)fXJ!U!=e759JQCDlq zQ9X(1k(cRNk{(IhN9M^4YL!mQdO9Z3xVPzAQ@=z#R-`G~m3kH#b^1ulS$eFre_!gz zD|=AiPpI`AG&vYipGQt^et#eM2B+xl|NdV=H*g69oRj;%ZizoS{~zXt(GbVE2ZO2^ z@4GfzR^;JKBCsVUai%Ch2+{-5`{%J0G*m(s4g&k5P^_B5a*}a+bvm*vkUzxGmzNGp zZ)$C!I;yq0>il`pV(FA>CL;(R_xh6J{;DyIte_;lr#D?=@pb!^0kd%&_~|?Iy|}k} z@4=e^kRi0>L~j_Pbd@$WHme;p;dsaEdi}WD+`D++OS6OpLe8{A1zrl2d`I;7Z{FUE z=pV?3b8{&NxU$GpKQc_2Wm^rjf3VE$zw%|oxR0)wVX66F?ycoaH7OJ>MT}B<{ssTtQFTQSFv%Xbd4BPs3QznS^I|cp%uFP_vfqF zm)cGj=?AR4DMd9?bP2UUe2m}ZN64CPeut*0Z8dHzSh7a7-`TXR1_sts=|HQ|W=Yln zoPUkT^Lv_k$h=bmGFV%ccUT!{D~BJOsa=kl@uFByveY1xcX$;M;btbV3iPjX|BhotNIHMSeNGOZ9dVjfzY@-${t7|>K_T<;X zF=FD3oDr&B5{`hy3p1vO0$)mIQ|7`;dJm(r#Y*2MiTKQz3u@`IkCLVTf_4D_UXOJJ zZJ254C-|AN^g}1Quy}_}%=|MMtxp5a%?N@8caV)j1BJ5qWMI5=wy1tvC{xshntd&LvNSx1;$OChIbJ|D==Sgt&<{s~bpQRQp^;v`XsXh^iDZ9J)fVNukBQ>aroTj3jkJ1NXba%!HI zU0kX3P#M~NUE{C9tBiKw78)rSAY!Bxxad@aLM9x~Uwm8HSt#owBnVwB5~(uDtgEpxOgD4K<6AF$0CHNiRA@`>5CK=*3hQ< z)4XDVp*7KdA&Jgs-CQg$yMl`?9;aBk)DviQ;=he>$W!aS=`ZZRWb{%s65;%DKX1AY zcxf`Gy5un^iMFrdx-7;&;*Mr{MXhUm79cGaRzj2q7hRVcF31PSac#(S&KdPi=xZoQ z4{>k@Hc}_pIF^h{icVzqKX$ACnhjYgONfNmvA;`L*?s!B_kR;tgCrP#iYoP`LI^ zs@iO~m%>g)@KXc~B~u`o6VBD+Y~~zsD(zo^yStZL-eVOh4tW;)|_pw~(M26`ha5=fu^g}w6?erfssf_ad!d&pBz`{T*IP;3es zaL9lfmZw|X@AUe&^+%=Oi@K`oVW|ag0giyc(_bSEf4LBVGvbdX5H5>)J$v^7ODv^R z$%__=`IL#x?7v$1YNc^lgQ+2PMCBMIAFCdzbBi#ioz=T$)H(__>zSNC*?cwi|OP>#^6R2f1Q)ewXplubgm8 zBlNn&1Ia%C3~}=39Q_A1C2}_*!KZRXb7Ij?pp-E&f{1+(b;8V~g^G|#y({CpPj^39 z#zvH)u$1~_{SbeUQ<}P9*5BQ~TWT>#Z5~`yg-?6UOk8pv{mr^QE~o($6n9_HL4kVf zfWuuew62dg$X#k>=0_-r79E?JPcX;_!$bGR;LvIU_U=9#!o|;0so=u6MLL0-wmFY@VVsZZ>^rFVWP0vj|zu9Nk0+p0GF+d6)%Gv*%x?R}8S?w?AQ>0&?v*qIRk zlOzmIFH@Os9wgk$aFcJ5VcNVuEQx!z#prc9(~w71+aef^;3c7WeIi}EFC9~mnuQnC zYg#YLmOzLYy7+P$1u07sKM}D_S$NqQk$Am1U=AS%7}17{5-NaZ`Ud*X$GxuKe6lKY z4s!DJFTfw2u^1hlDc6+#SI+a*QDYPU1DQATpf%z6JBiqP(5#`g+L&`6nt2;qU!>MKu3Z0(-&`!FRnn=7x76xMta_wT9A z>A+eH=oWqsU?$zK^B~D@F_oC^d;gjW$?%g!>CT63%V{qdZiFTAM3zZ)5A_Maz|v5~ z+R{~JycDa@Qm*s{L_c->A!v?4tDthU4Lq_YCU%bTKavj1db_Qvr*k`b%yjPMFLa~D$j;Dm4Js^EZQ;%qbS`ZEjq zv<_!1pFW6oAM)_y1Bv-7hwCzq(=Lx*yXa$y@iWZNM1d;cD7N*QWS8v>Jv>XPZ7dtjX^e*B++2zyW5a zn9~EUHOsoqzemCF5#S1|D13O7Ze&m0FtN(E7{|SNS_;0MppZ`Brq(zkt zD^dx}>WF9~%0;F&tr1A%o%By0x5_fjbd{j%{&;Tb4Z;vnku80C2_&oA(7zZ#?)a6e z{9+2rmhG70I2^$|_O^Rb%T`!X$_X21{&CiQr#}9z{s~5zO5G9x_&AS-yqf6sZ#(|J zKf4&=N~F?51K6?4{p89QlT6?1LaS1^qxJi|-J7zxJw8igJ6i#pnON+758?8e^wcr&AY3Yt~{N)_AKifJt>9YM!ndQngAE#6X^UqW|i)ZbtSx zX8*iJT!u9rq&w7BMHz#gU}x3a#JrJ^oVVA-f=_LJ0i-$%IInZ8n}GMEC`{ljUt#N& zk+haO3czsk3wS_K*%4#Q+0U6EB23IAkGUCW7~byZ%0LHoc{|P}cyHJgvL36{ZfQ8Z zD=%y+(a&DFZ5_H$>pX2U^f{|qyqKT&xMsSW0h2{b(q7lPwgP=~OWeSD&s^1ZejPV= z3%+sm0E*}9hyL_I-0(S&U;7xUAkqbx;RS^xq#wSP)$j23u$ifcF6g8N{_uDel#5scLmR)^&NhLHBwv=uO;3INYG(-{oVe7NB(3b}8J7uf#d z_jV-=aYI9QR+U^&{s6VE+52>C@*TARnrt9d)HOEayE;yMJ(Td~pHzmQ5Dp1Q% zoZA|rC?R6q=}X(Z0y^ug?sIj9at&NL zL?|7MwY61+Ib8&h1wMJ*nS3E+dPo?LSH?Bt3(Dc+*gPP@m=C6BgP?oi_s}TNnA;*3 zbcr%7ZAfK1*6A_!30*Cg_NZEyBuH7|j2kYCR2#%__&f;3@qOTMilA8G)%486DHZ%b zGerN!|CG487ZXoIwlxCCM_DU@(trH~i!2(g%r8^Eps06-SAdM4kOyVRJRYk0T?$69 z@5v)B1o_7Z+x#Lb9CJhX7Dw*$@osPGVhCOYad{I~Ij1YC4So`~yq}BJ@2sjlST6*p zscbg|t?hW#hes;y3KjM<8r6!DMGWFe**#|WBIq#+df|T7X&M_~JdS43t>U>tQ&MmPlvr=j7c+j#Yq&p!?q`53C3YX({UQk_qI79)=` zad_$-Ob5b{B7KFIZn^b=!7$x;T+*ZB2vdguvh(k{%ALZ$u_OF^oF1R7bmV`+J$fLzNRNJmHlCiiFeI^*-|2xPUq%Y82uMR}zD*ORE3(VV_#-ptRm?nFNGJ3(1woSs*m z)neovjXz$!H!qB^wRZZcuJ2~oUn25Oed=>npOekgJ-ikAqvL+FQ1c(^?Y1CgCbx80 zVAt8`+VHayLAOay6 zx8oKT{9NCnaiz+#RzlnCTh^CuH@Ht$g7&L=kH-LzVhx+V4zaiKrd7XFPC0AWeK%jB z*39u4{Q<+bldC?1A(o$G8%vHt&8=*3o5E{s6_IaCkLp(~4L*&px6A*S(rzv4{+VOv z^U?}B!%$DQ-5uSs!RSvD%|@xt>!zE1_l16tAl`o|QoO~)FFb6$NBNr+2kDS% zvM{_D1-pr`SGyK(fc$UpGF##QO;+al-%_x-(;OK<=>UKJg&H`-fgCs*_7P6VfSt6i9xMO#0`*)B`(0l-+wG~^( zx%b6aZ2f&6F2iwb$|v@i6^NbE3`;(g78R8DJl#`XchktRN|cITP8Gk_SN~SA$*?g- z07K0i0CIIA=%t>PgSH8NW7~ebJXs3e)Bb#gG+1W2)6FKtxS!Y`Zs+Z{GWeduI8JaF zMTiR)YmTBNa8T0y;py_Y`*h#x(-StEilzPKzMM|1+v=2f&u7tDezX{kj{S|WtQO0Z z7rVTM*y#3m*Ap5*81yPRf`8T$dQK4u^jw`}z%{nIQ>k=n!k9W-N*L*Ui0A*H_h4afjGU>n>)}SqEd-}*(#mjZ2A}6#ON*Vj zLM24YsN&H&*29zxk_I<^re~xneJh{11azT8_OOU9o zqME|&a5K!^^@m)yx?yX&8J}QbLAt7IYX0WBzY0JVflWp^t9g?&8?(0FJ;Hpq0$9Fy zBGlEkz;UQ&%m^<+@jj&mdevbdl;A7A9Z{?~$(+ zp%-L@8C#f&e(Hc#sx_54t^q=s0gN;(?Jls~HN+XTH&`s8W!6HIyVN`Wpq^V80TbT3 zjUn=)G@@iXEARy{C?mIyFt`Qdf~aDO$zaeRgg6#?xMDH-kddmBF=6qb&JPn-DRA+o zs!a|;IoaPIf_Hg}slWDcu6^&Itdz)I{>@};gSdD)%vKxaAc`cz5p{260MbAdYiOF) zKas4*1FWbA@2snJ^5la7lgr#E%<1-?=u}}3uu!1D3PFJoTj3r>B-B}fl<}kMz*eXj z2v?OwFXz$UkyZgq0nloPY6S&^m{!zbgifcv8eF~e;ZB6(zi0bhswGF+H1vLNYjl+# z&2}wl8jlUQ!KN6a8w@`_1JFzEOuRIQf2f>!t8peI1MykFw7g(1;TBN0`(NefJ1T?s zfM*EFQ6^hk#u@07RBJmdc5cRn?FjpovGkn+lNamzRj~p7SSO_Q1HuU0%OG!dD7*;i z6fo+=DCMC+MlgDCPpBxi^}vzgqnqlz4U<_lJlujMS&froMfNC#L_OsEvMbM+4 zEKJL<-D7Bu8ggpldku}b{w~fokLwoo^0^9aYmiFBZZY>ufKTB2<2>x1w2oE+qE1{khf6EiJ3EaGeob^d zvED0s(1ck9nHe4fVBY=sJq0=q!uZ<`ww3eH0LnNy2L2@_RTR&D?FQaBid=79i)ef6 z#rKt)yx|b#FQc2|K5<3pB`->%mMDd0Qx-2oj zEoEJ@dJUO-X8B9DJ*Zq0w6)v4FUlR$3uB|k7k9gs>-OzR4qWxB=Ceg;wC2vybbJ>{ zhonu+p8Asp5cgT`xeyn*5_dNcuHjgsY@4jv7R(39V2PwR{K&Q&X^(Ay<+rEUhl+D0 zu>faZFd^6U<>c+msnxSfCw7TbvW2Wl2iBle;fcvDUojgM!$$W|>P+1b<%}viUPeg% zn_Egx*2KKF8ykDqRH-u_yo_iB-m(jafzC0ap^K^>kjFPoX>nPz_8x=9Z&G9~RN>SEu(;25hw}TqoZ~`bqurJ!$3|xm38PN(+H3Swz?9t-P!|0py7v#Fs>{ zJBVJrj6IaK<~VET9n(LXLaJ}ip)syeC{O$iO z2xsryoxRQ&z%8d;DUB*Mf=d)2^qtBB$U_6HI|mp*j@8iPN~MgVEgW&g4>11 zgrSM~dX42=!KV5pK9#0|S3_;qkD1JRfKbz6?&_M2pABEa_(KPrPh~vD_<(}c&%UwL zaml69>dVUl$XYUqr9$o~O)o7@{8GD0c%9IJ1wt2*e*IOcs1gody-T`MV!63}CjgDa z@NVk1zTwm2Z{Ko*#SsIF7B`M6Iy6z3O0dzca->dJS= zBQ@LKgjm;-{a_8(O`@^MZf=>&v-T^}3TE5=y4)r|zf=Yn0^%!lcz9#e*Y#py?tlU) zeCxm*njbW&`tqr;;=|x&QDZCWfPcbF!l)!66R%Nr8|%!FMe*-Aq0h-+xmn{=>E=J2 zi=n?a98awCxRIVU$jhiv&e(lh0{C)j^6Rm1!ey0 z*C4%VnD;5FiI!5tn9ZI#>ai=rOuKR+Ax(+L69=r5U&Rv?ggGqq>6!W+*a2do2a1Hk;HGM6E%|YQf^8?@U^g|cXa;Y2dK^9sv9sEv5&_DBjt^hwEw`S7f0;_Jx7o6TW-2FD=1Xp}Gp zHarc4qqo|Lp+XvmKOmJV0sZ(!_6p7(ld@9jPGXOw}| znT>G+{3FhNLw==w-)t>2*6uf`7~r&qj`r*RMs`VnbyfEAG!oX6j8DzMq35^ql`whh z$!UXBzC!TaZa{t-IBN}JbuaB!ELsVdG;<#wffiAG{2Y2#t2Sc^0Dwildn?~lN+t9u z8yZoJcQ|xcTqQp17P^G1S)=>Az68{CXjWY@@2z}qEzBmXBlc4!i{gvoeWgNkC8`bSvCq< zaN?`+NR~oQ^nz|6+kfIW$+uu*S!UVIJxe$8(3}ft*m|CU$`rXiO*$i#MUek~;hiir zR*YN@(Ordszw}@f6y%ExXy50JS5*JW?3{_XK7{PWugMrt3t*q-Uv|kKL@n>h&A+qj zGQaQXN8=rr=WGgb0(xI1uHs0}g4>ME$800X?T18KFB9u%qW2!%(JuK%BzaEr^D>fE zq16ru`5$$_UC|hZMullen5i4>cxU0Zd-#5FAaUjw;E0!w2K$V7VW-om;1&7Zz+&Ti z@MYLrswBDm0pu{`z@~41-<*C<)?ncdo(Ng4i%V2A&D#lm++Q}ilASIW+-NqE(%SMt z;~{{m&A_75b2dxusb9@eDC=fech0~`79S5!q_F3+Zm>mJgVb1{RbnW2_mYBV>qO)h zL$F1vcucssu*~13Bcb$)4_vrh|04Z)G$0`KJ0Ds_sbvhU))n}HXq*J@;q_VZt zk=h}ut7BP)8%?t(^}h<5j%KUrZ8{SfI%%H|y(9}%?CCA;W3u+}cB*Y7VWuWEz4sp* zoeF9Tm(yyM48qcMsYmLRw|Ms+2l-gNGWpQQa=P_Q19XYz`o1Xs4PyI3Amr0iy9f3` zhN~vD07#-SxiV~ze2eqKokdP#Hu)NJrly!kHHu)leP8s$BKRG)uls&&?TGfcf$P9D zKIMNh6L0sQ1Cxcip2q%?f`S%xTb=X`Rl=d)t-fRYZr^FK5%Xebe;y7Ub&lg<73{mH z%!=D=U`n;(&~*8!CnVWlf)gJ!DPQw&+2*PP0oWJZyWXuT2Vk0Q8Lv4Z|B%MIvjP`y z7NU^)N-H4~(OMfh-9@?G*D-NDz$!PDe^5L51fru--qBxeo=nC-bWHlhPRbf>VR#0 zlat=gBuwY20tFkCTaeuAW@N&gJAtbiik!w+=E}8kQrO=Tvm%t#N%k%a#U0vs$Gy!<0mz($i#I$n= ziyOBRe#coGthTH5W*Vw*0k9tdU4Jj4`yyI+r2DWhSeVw_EBWC=Kl7^ITNNVgZtEs} zxfA(rgUZ{;rA4RRQolj5{(mFH=9u(DBIQI%LKKG_R^b~S-y8JVe?Io&CZkzj@B8Nm z_IU6ONi&HuBQQLF$^*($xMp||3lQ!C^~lPldTz~hwyi;T zCFE%NiP<<%NtmE=dZgt?XFWUH{l#;-Yi4d+0)!l!(kV3jIWg0sHk)=kTgOr>e`ihDKuRWyyf+d|)fnlM#I!Me zP*OFI?^REt$)rj}TGa@lvx8R`3B7QspwA$V=anK&&QM0Vg>l2LE@w8Xul#P+a`U%) z9-0RVN>i1jb(=SI#4;njKIE_t)_?Vl+Txd5fSrRb(1Xa+QOvnwu4g=nT> zL3>)Q@pF?pTwQR{!{NfjQDeqGYxxFsB3;b;Tm=)soi!`62{?(b!nQtHbLO5QagfmVZXJqEk}7=`}CTN8+}|bh?B=mA9O}EBg(OuX)HZwP?KiNe-@V{ zooWKK)B!lds)m}2tO4jq-!TCDh=qmln`is67~M(q%opc)u|72rnfZQjoEm5bg7|sx zxLL%55;xr&`tuiS2tzi*Z6EQ$qoA{78H{n;XsfCEGD)dKk$btqX$-CV=t&(S)8?DC zM+1qUez)5vYMf+qi~1Xj064X5dbd_7FvBleazL+DT&DIILtdcb`mHFrPfhar-@PYg zv^X3OOl~j8vn)7*+c|Bo1x}JYFcJP~+}-Y6x&34@y;H`CT{EBCnuOgo%rb>|jFh9- z+Pziw%u+q03}aZnCON=rcI2MwU#a}H3_9TOQ)S0Fe>bT?I(Zeh=LFd-7UXoW)0qn4 z8bID+fJ!c|ef%Fi7&3#oW8#n=s5tM zSa!y*S)YP{GJn5;@uhMdvckjBhk#-s=zwk&p|=s^Lf@rXOY8eaSPukk^z$Mv*=mhp zoxO=G$2Hpi4ulMsd)@H1##YE#FptXvSO{ZXS~E_GMQnP(YhWPL+cfM0^BjKk1BUux zg&*`-<05pGjF7UiBlhI{AEtiV&j9@w_fScVW7LR&C3?Kj>oo;;4Y_dOm6$0?&)LXS zHkkP3MEwz!WJ{_a*z5K!r1ElhN7y7J(b4bK*662(k~G$ z+Na%S89@j~1cSdNp#b3%aggF0K#Q;3b>+S!oic1gKDcTMni4|t%>X;$SYHG4{4zR; zb89I&V_k(JmJqzt7)8Z2z2>c+R07W0I(iHZ+I2@XL|y-UstFh|vUp)yswaCg`Z5|f za|axHpFK=K92pYE8*T=!70q=79MPZL*~H&f4t9gA3lSDz$5FVN{epQ+QrSshisoYP*uHLZz)&XMyWh5;sXOf8k_sWf> zqPJePDxQhqU$sa)Zz@pEb^BxIV^dGyT~+t@9DB9TWI0SA3LtXi=sihoO$1Q-L5 zIq4=dwv{u$HE_&b;cV!DLx~m~=I~6u!~yU2PzR?PT80Zu&1Po3r!f4!lsp0mAW=%g zW^epO!trkyuXAxX0PCKrj7M~Gd*yncr#iBEIWITe%cSaDscJK_*szwFHw4wh?;f7w z5pp9rAIps@uud|$-Jp+eu5@|L3BvLVAVW8|6GL=Mj9v95HT9C~_^%70o0@a9_m~KA zHDI6?&#HnPR=ssYLW6Y#?K_Td+}1?S1of94k&aF+jG4>V}Y#{6%y3x!+Rf1qZo zur`Gu9+6|XZw2PMXA}H~GFmML_gFFC%KNLDySu6#61(XT5`xx;iTlFR15xt+bqr<2 zHm)YDB_FzYU03_-gSv%FLQ2Hf%Ao4|XqJjYX|G6j5+66^< zCv+OMo&}(onPG)606OdE062P^=&iuVo+9Acxaq~~DMgbz9x@%UDsPs<{Gs@aJsGl< z;<-&_%s`K+7sa+m1+Sz@0*JGB+8UF!~^T2nQJqs4kC_F0TT?n(QSC=ck;((MTBn4z&mPaEkCfToUS+!sB@*)WZPevV4 zDg9n1BOsYg9+8ugw<9X6g<3^R_54+JQ_iPYK4)F3xUlR-t!I?&g~NyNP*U8b!vl@& z-nbegzZG(^IWk9V{Yk3Hy$Q!ud}MW#Kl}#`)kE&zHLBuNax|m=f_8SK6B-N)*~fC! zQ8=K5Z^JMw&*M5;K}{>s>2DO}B`@D(zwe=%MrlM-53E}lCpb0v=5=k_H!y4Gon77b zH=et@Z|=;o#x`u+itB}+-;M5(O`&`|eDdqrvlHQm2L!k4saoMu5#x^L5c>|q@?XYz zOHxA%iCd?-S|U|X9xAOU+S*ZfoAOBfw10rQe=PRwgs=fq=)oZLPA=VeX#VX&f6oCiaW%N9Ue zt9ZlM&Lz^-Sdt~#(#gW2-$*S;*QO4epZ@d`+b_B8rp3iRsXbq0tcItAv!LJD_c)6! zQDD<$*46eEg5t&1ut2aGSGty6Z!E3AKOo62y=aH}9o3bU1SadQ;UHO@`w&?R5D4wb z12&ekrdy5C36dZW@))RS+Y8$Z173NV=fejZ?Bp3e|3`Rg`5T<)ivp8YKo%z5HZv6O(zz!O(VSz6@v zg?dWMm|WIMuX~u6RdOLlvVPYXl5W4hQS9-Fv3?xIoZo5uNV zQ{f0?HmEHvPUM23m95Q72Gnr-q9{Z7Vk>CNt_C@sZt5geCAgi35&S@vNu_DH#PZC{ zk?DC4NR_ytjl?aXG~9i&ZFLj51z)+AF_Vil2^y-)VecT5U?Mf1`AHEv2xtuNg2ELhCoMbv5K!-yA8)U6!bX+S`>T)RIl`kc z`mg=GY=$EUgt6wCCc3z>g(#FisGT5G#o(0nv5F%M20jX*6Zzo4JaQzLn8n~0}TPiTG%uLWWYEf>+rj~@ql`Ln1NO7|5o znM3Mlo_l~%LnUT)GxHnw*9QmH>v>+% zet~=G(>KC6bHqj)Cp@&Rcu7hv1fs)eapP{=8#(6>^@!?jA#iT=Sun*LIbrA&lh1>d z+fWh*KhKzsKCgIWTWUGD_CAk;-m;*GS8m&gP75VCA9QER!osLKt@Wv~J571CSqRl} zR~1Xo`B*?aAN`p4;CUq~#t`T4OqmldpU?0BS<#uzImZg=nrTn9hUj$6<9|`Mx40!K zIxhwcnY@}8QUyvCq{gScHolQBm_-!2r2;_p_KvM1SpEI{<_&8MmqEQ}pkCYHG4SJ$ zKKhmjTubasWEK9VzSLEszYatDO%{f$?S61_N-PZ{bUwR-A>0A|ynfg;n&cPk8+j{=$gK`{M|d4zvyZ38 zP_84+Js)|X4+PZr-KCaXcPYfx_Gbh;whUmdo)w!BdclHuV%Yy4oDuXLN3WeR#}Y;v zM_DvX;Gt!yVOvrht#1x;)<5A{fY{&jtd!${xoHD6NjEen8c-rJ5OR=jA3Sl**QfFkCMg>PFi4L z+X-QnJP<4G1{IUggb1C1n~sW1r8WX(#FYzZXdxhVy&ug$fF-T^1t9VvQ#-k-nNC5* zDj*B!4kqP3(58|Xo=5cRIL;!q0i(jbmPR1ErYVF0i;49gQDDXmK1R`ik#K`ShazVg zi;o)4L}GXN1Z{-V9g{m)BZ89Sf?|I00vHW zE+e591Q~zR%N7Jv2k_@3(-s=jFiH)m4z>WH66bkJpY=%S<&v(68 ze_sF^6vZ$u-jU#S#XU5$xmo%R z_9#_J@d| z&nxCoNnwht0ML=#RQB6+0^(UXH3gB#KeRNuvL~j>KRP-b0&M`Z+3a_xrLP~?4e!~R zjTa^ls_I!w2_w&XO%I-p#aq=_4`vU3?msjBrf9zY%aYF#S!`%#T5vvgnaeBte7?)7 zb4omBIP|db`A3!IHxg zWQ;){d{*5td1Ajb)Qee5U7^0#_>ispuz5gohq>O32{Uzjk_p%4W! z7%h~&9sm@jE!={^6y-Q*4oY``WIAr9|9wNxVotV^7s{g`RgjaJ%Fly}!ailU=`Lsb zDW|6XD_G?Vw&}-Y#fN^?3^2OuM>7X zp4u^pfYSc>?I0Hs-e*7wzj4JRy1JNdCz;rS{ZjWwqV`|aDH}GshOb<{<>GsNcPx8D zOE1kV@w;Z8wW_r0^Gw70_WDT^Ma#I$qI&!WulB_N0;Hy;<1;W_cg)Af_>FhnI`R-( za)2e{UM=?UN$S7%l~QK{P`fVGqqs5yc1L{3Gyb&9hKA0vlDWgH0Z(P7oH8cN=f2>Z z#WO~AuL9@T!A&bSDsGViHpQouVpn!Xxv~r)Zsgo%)|;@=6oy}5$YZ`EPd+Rw`g>e0 zA_op&P3Kztv9hb1>NQXPP6jRUxDdO`)qo)RJ|S*|O>NN!f6#-BzkNcA!oM5)gn)PL z^q?|XmcF1$r+lipM8G?_k+L7qKc27!)qd0#nJlv_xpYYl^J2rM!GW8jH&ZHklVpn0 zCOg%K(=cvJt(yNC1Ja znLQkicl<}W5I18jBXdsB3m9hV)b9NW*+CKd7V7YT?nBjjcO-)g=vOYUHAXXs?mlq|x8#dh z(E&PC-t>bRjT>;GFG z)mn4;Uh93AG;`n4O36%q2GQSHIPU-Dw3UoY-B4S30|6C9Ge^mFj;r{1&CGW2Cy^gX zl95s0)${g#{f+^Q-rSTBB-toRMr*9Xt59$0`?Or63EreczcWOaxQ(iad*J+q?`)c% z9IO=-)Ti0$M0tz^|KcoYgLHhprl$Ev2 zN4#kn&^2vd+bm`9$Bae;-YS9S^Ve5G7!kT=9cfaY)9LGpziZA8uHQ0}h6(^(X^VQs zI7OG_8#V^pkm$k#{cv4MdDx*p;kTN!H_W{ZIUaJ3Lus6^$_HJnJovu$%WF>MBW*>C zHntd5&98bjmN+XB1c4;yp?sYBPk#z!Bq(b%)7aS-fNw?zc3$oY?j^yj))m8_wKbCQ zkUbQ*Q+2J|p!M+oblEeaQ$_*lfL|6%{}fYz9@6-Nd{&|eWmP*frPzqhD*^d5GH8tD z5{+lY8|G6ZYmW7wE~v-L`j%eWN&G&uGhl2RESh**pDulB#gp1S`Gv=M9TpS18q|1B zLifGJqf)+*_Q$W}(6Vlwyv$YyofVh{+VY>CyCbG6j*%SoH?m(w-(&qr>9iR+eDu$` z_}Ajt2|o%Z@z$y-jzM0_v?fYBEp5qdG?+6Eccjg{#R{8JS_U0*Q8C}4wzYE8p9*D2akFDz9<{A0~a^XiV3eLV*193Vbr~ z&Pw{1!nU}KsT%jMZUd-?g0(;h?@(ILJSzW`4p)>G5$c=d2uHw-;Nz}VAF)9lLaNNq zh1wM*#=!10W&Mt0Trff=8E|KX*Dmf7j+pp$@~9s00&u%BL|!-pxMhqR7`L2E*FkhT z9hDpWYT(pK+r3GuG|eYMXSgk;oFckla(lAWu!5QW>D97K(ecW(654Y6s=1@dSe?l*)O9k4-Z>c%<)q*X&m9I9Dr9c@^&i zAw`&AOzD|xIAQERwc^MW4H+?XaG$EyFX(rZKmg9_Aj|-}@C@B$o}}Q*Y7+OW)cs4~ z7;9>4S*xo@54#RHDq4Ui*i$GOpEzbw#tY{MqMiF67P`73#*5cf7XbP8T zd>TNAZ;OyX#w@jv|$!wJUUJ`(*HV>nPEK4uoWgSy&>DqAw`o!5f$Lrr> zaz>?AV(~0ywf#SI;QCGs2L%&E=bOUu%Z=&N#IP@q%iW&q2@e(!KWQ?b8}{8R zw6Fr5-^#*8aBfloO)!OV$Di(ki zLBg)!26wwL%65l6P(T~iJpT6KrM(o9KeFD#`>KBjL29pFovel*4WO`Xt#Mb4n{;qO zEqKdoYET6chnI?Wh5e)lI>CU0t=tZea z7paQ#;d#eoV9aXbU%@W}$}qug@&RZL)&6}(xmuQ>irCO{6kG@s+X>8o1B1j23vK^% zcoXyjYe7sBFQoCaILzT@i8yERs#yd}|<(7Ta1{DUw>n5ss=gWiieR zW~4ot(SPjn%$rP98G}$RR8qlVzmDC}MbJ-T|6`qB*(V>Y`r2_&(5ugXmh87@i221t z1$(}^1fKB-+Duf&TweT}5)L4=Ot9s|j-G42K-YeBQ`6%0ayA2702WoX@PizX4TeM5 zskd51$(^W?PmeI*J2$3Aa5Ems9zFj&zX~tU^4m<@AYJS|I%R1NpUdIIj|= z(4wE93?(olFiOs0weC#W@uKy^0O~JQFDJ1Z#%NmJfkaB2&fDso6|6d3g3ku79QraMVFl=Sz( z&c(4tiZjPa$+A2mte#QiF7WnpN)Yf+$!pIsC7nbPt`D_O;%awP%KwdGuqVu@V1Ti6 zCFJVS0xGq)zFP!nep7~dO$A3}SY0swtiIAFyi2wwZ)gYJoc5N{aYm73Mct9na9h|+En#Re9)SVtVTs`O<=n1Ob^9}8vYqu#Rh^1dwC9NHJ<=>%Za zxLjgpEC&+eG3SBNX=!Yn<3Z%Il>wrA0N@CvHNRk5IbRFcp^<}X0;mAeD7&`dlgDiC zuZ?FbOV<&(QMP;tpI*C|fH}#Zv5|b2dk<}zIYUAm{UG?pAdOVVEIlAnU{FS4_yIjA z;LQ9++riYRP$bkiHAGM(csKt9)?Z7$i4E=w?(d8_7LX@?+DeSV)>< zaPnhPjBd|I5^}8-U6T$$TtEAqD zmwNWU1E_a34RO_tIL6L(!3cZ%c zIq9R?Ph3N59;e65^Kr@nsU*$w{4$2|9AB7#a2fMmLq!S$T5S2@jza(-EXW$l!ltGy znGE>eRs=!RPr^j?ZS2Z&K{URN5-iY(sa>Cw7WB=q(E&PRlMWrH01GPBtUc3(^B?n7 zohXD|sO9PoMTW8vyoTBOnH^q_r&f${N{z#yotRTl&l-A^e}YYoav{L~Z7`)&*~{kB zn^Cv#bwmc4=gM%!mV|>!u@8TSZ)P;Fs*2P?dN=w+nHc_{$);vE>2&uehHQqlTqS{8 z&2^G*f=lo8$Y^bW2jnl5fui4h$bf;-N#Pq+K=V^^isLknY*Ym zc7O4nE|J=!0Cw`5NxO7u?dkFq2<)4t{Iwxy_P-f8Rnq(5KgV`|l*aclI`iGb0)rcKtLeBg|n7U11eu!h>dZWMc?PIa9F6tB!gGhOp&P6YP8-erS*0O4q*n5vwf3hxhek5ZEkiRMF{6If_YT zn~-fFB7vLZn|G z5otzESKsGx7t!+G^Y3Jy#8i)Dg2x49M!jN5Od=9qINe#n*TdStK}P-CB_=`pAPWtN zn@v~T93-cc*kE-LI6|T4=hnc=8q=QJ{Z+u6_#wAGK<*7ZQE4KOu7Ag5p5VJpXy$uP zq%(C0eOsc0e*NjA5;JY%k&epZseA?n?3N9G6!sE&)7UETRckJ`6~^PgC@3xYXx@)& z#Ts8ViPGr`90xTT4qn(a<}w)#{};v+m!n)uGPAfbOOpP|+3E{IF=_Rb%MP-?j)Sz} zpV6KIC>8=pRv?~>Wmd5z-i~>rx6i7KWe*%yQen#2WyLUHt~pM(+K*P?xZ#FCjY?WoTOLj4-z7XQ+m?Z*7o z0j-Ku4Z~S10ojGP`iCz)2%Xo5bN!W9AIx_dkh<9(TejUJPxqTwa(B^pc_@ADRere{ zwu0T;aS!g{Cw{?I2(ngjd?elymdYR$;v;rlo+H?eg#1$ZFUK!oxI{Si*;H<1LJ}H; zTVvTjHGioX4A~^JsBW~wpP*rpSlX4bRxcRXU*q26Uc>}Rntz(YLP3;#sH`Mzx1;tU z0*uZeK-@5-ZPmID8v|4kYisnqrXph-Z**euwqKwW@x?zty%%S_(iNr41pyM`gT__p z5+=yf?VEOIgO$t0fe*hwLiJ&b0QsqCTXG`vVOv|AhEx@=tbXy z6#L;|hqTOg8YmosZ%ar)Jqq=G7PkZ+wTH+gUfbLFUx5;Zz9YCXO-15BX|cIK*X~qd z=wS~M_YZ0Lh9!gHqWthJvDAVpLr(Kp@Bhkw+d7Pu^D?@rxGr@D_rLNPfjR?AvOf{W!1d`bAaNKI3|dFOR6@Z8bn0k?DC1>olXL%5hl`U=Q(kRDKkV}6}uzc&oF{UB!YdJa4y z?ulV``z1{axdhL-R3(%OU?}HBJO3$8OuDLT7z9H)!+^aDQi8&J4Mr@$g@`C2%er6i zd;obgmS6TtO}uLd$ye@Q-vUZB+!ZIw1@8aG(L1Pk5R~tBYl8U0wYY045>T5TI-K5~ z=5dJ}9gdJrJ<%V>g25X^nrbvkbkX@rw4(=@NEt}6j3lp2IRs)}V+d&sJHBrQaVN-j zVZl#&I(yY}y-n@*YziU%22XzKz>bcPGOAe@mH4~m+Cidrsr3witLsa-o~iF+(bUBq z)oZ?iesI*)g!~hjIY@1(Mz8icCnvmpC~XC|B>voFH7y+6mq9?VG!3Gwr4G8fBLkv5 z2MGBBlaWux{GaJDCB+RC6P%Un|1vD>6EYgHKC=ybBt59gNk~!F9i>n28#y!XEjH3T zX2qbmk3n9t5=oJ@3aQ0%}Li0SeG(KbB+Z zvQm4(AE-PDKd(=3>K_&|9;T4V)ZlqE&y+7LCBCrz`r5}HZ5wg0ttxLJ5QKE|8Kkn5 zV4#Dlh>=HK3Z|Ly&U}4AM>_@b znCe=-0ZLwFPs1GDA{65govmtPt(~Xk7b@ozIWxR2qD@&eC28RxcfX+y$2;MoZF?9xI`eoHUoSXv9WuGn61g#Lqy4-PatzKzM{c*PWvF# z+vEehJcmJ=@s$4qNC&A|(tR12@szM!NTuM%;$~2HRckhF1&=Sc!m^lVgd~TcbB%Um zRYk>2!L>VWLd4O0k;~*?swH=xl_A6M#Kfjn`j>ZFP<)$_c(M$cMNq01wa`8MI8#Z` z2HR$g?<5QNCZFE>w;fkIZIH6$mR)q6T=$;j-=xJdPfeKFVD4XYcf$Oxz3A^Fj-Mq6%Ibwu#rBe)KBhhe6-2|RP}t`t9&{%EvQWIh}uME9i6NBisZoybwnaG5%QcWBy~6xnkC z>N`Nlq9ui{`*eHbsU<_EjvbNox3Wh!9j}}1i8`&krwTswXCmXloBROhH^E{}?RXFL zD`4-N;|;L(2an~5WRR_4P>MK>1@6;W>rr%hT|skzs}rhlV>5_w)4m^dwW-y~8No!I z*j)0sRc5pu$p7Ao&m_&^qo055UZDr$_jB|x!#dI#@e>jRHQb%s$`0yMzKhNHsxR>& zM}Mk4ty7a5C2oReEB5;%KMyrwI&ES(+D=n=$JqCcZ+#ZMO-epxeGxu&yk3+HwD$tn zS4biN-FL;Wyd6y%w5buTD%0X?pOz1&1E()&(|c~Gs?%Q7H}hV735OQRvbjQ5p!~&D zZbl(gM=l8edzdlVD07zL}fe`Bui^A)QcU=++@h$NC;~yr8KrWZUG49O< z9=i<6bvpPnKRQdOms^tlUm410O?#c1UCWjsk6;FP@l0+4>?t zZrsYxJ2MOmx1zSj&s zwHG~0vH}PjrbX?Z)uy_>{_QSDoX4}vpclA78IBXkIX(kDTvA=})rVm~nOUwHD$wd! zKu*{Q8BI6CNga@@mspcYqnb$Y%ft_Wqo^14x8#LXU|@axPBf9B#E+{tF>2%AxX^0J z2;b0NA+x%&$)VkW?60|YrMOzu^HER}J1n69ssmJl2Vv?b@>* zcKc+bU?OBBuhJx=g;@Puw{b=%A+4or^d&21%j7BRi3UtLnx81U5DIllYSYGmh|3cJ zcTu2z1Q8+f{!ToQ7l0B7-h9$q=BtkVgYv|b{QxDs8L`_n6W3SnJ`E>f+X+jhSH#)N zXqIyp>dE^R^Hk`Xx5SIH=`5vM1AcuZO6L2L4@`Nre`_R54OfWd)Md~Q|JPwdQrdz> zs4e}6w!JxH%U)>Rh|_BER1qjZWT{xCavK6zeqUHMdczu`Ur`=UL(H8L5)>S6l$CZ3 zpHrySO+tcEuK-p_K30QEBax3gV3B^c2G=FeV59=(@ejo>4jvtEmtxYahO;UoG52{G zc9_dgVP}V(Ch}Xp@(1!qD!gWrMYL4dS*#np#VouNq%1>#EKW;!Z*4xnKR`zXo0QB< z-a>@`OrRhe^Ry>c=yxVmJF8J?4MK2Ca>btdN>tTNw_MnbQVy#CslzWK$NjfqNY^|9 za2YMgN7R1FPm^LkiV75Mq1sTiJ1izJID&Vq?Rb>YI7HtYT=oeWD^m=LxdNmj@v&FB zCQ?64QNr`7gdTd?6WQMY2Bm8edP)xo%h+BJJp1P_s7oiO9FpP~2rj;6(6c#cir{3@71xQ{uV8+0yizck%0Hg0$xs@-MwX(Yg)q7JvKX&ddIcSo3bkr z*j{mEj$^wHxtAd-EuA;n2jH}%=K@+=Cortv&ICaCSmu`~bnj z>9ReYIJXO{?p6Sha1*BK{|GP@h!%HV`{AdA!>{5XGTVbq1vwrStzG4>Cwjgs=93rr zQ-fYgF%KqM{XJ`-x)uxWILhs$C*|Kn2UtC}$0UnP=vQ18slecg&z?&6JCAq$zE=3meA^IhuxD^VN^u0*^^JF?)r%vdYn=YZ`GbP_1 z+d)t8y6fT-Qe7!TYM-xfEbakalc@;ctb;<^v-m;*=?SyYqWYjfo|4$Atcv%=>+zAN zUFxV?pB`Kqx4pPdqAZfD<(+soM{1#w?|Q;$zfg#JVgd7&1c--&Z-gG02Jrm zZ#&!)ZlX*#y~jzSAG=EG26KY7uBrFCj1-H#73jj z+N{uiPOPXC&(-w)6EpI`duv+34$yh$GIFV*lXlRjy0T9iSxkHjq`f<9wHiy7cY8Vb zZA%4cJ4mgajx`xPW_!9S+@}8Go=NqWPWKBUwi;v^KJgpPP5#FQD&Gj?w_#c&UM;E1 z(ll_AgW@M#J2uzvoQ{;nmtg;AT$_KzvdT=SVQ=Hu=Bw&ZS9W`huCDH$9-dXD{FB~X zFsAh#qd~svdTs_c(_94@f*H_)a`Coy^&r|=8{0?hx0I=Ts`RFCg}1LSnR#ViK8Pf*q;D^ z613w4+MvihUD6pUoFlF#n}OhPi5?CLeW;?4{zUs!Pku zqg~tIfAhwqo+zoeFDGtf3r!ju;&N^OXD+F1H~G%j%iZLdku&e4Lo*pX|3XJTeT@KN zf(P&9I`ST9q_-~N1cG-^3)Pcj>Kg=b^qVQ(5S!*02-B0Q4K?7vFQrl(2UV%gH1go)egS=0j54B|5Ce3ys9R%1IzT2Z$!BZR6wP6 z6tJvz(Q3u9kD?~`J;?-%o9hB*CRSASsffv7q(PW(+3-Q@BD=#x)Jupu7Ulq>o4vv6 zNY}w|I&E9ga5C5Ei3g;TUvC6NNcv?P{p$M<#eTT_7S19zJrz7XB={@}T-lfb>7_7y z31rhQYGN!we6twnFz+pAJ>XZ52nsmC_Kb{NWrc1gm`E+s4S&oPd!K$d3>J;t-C9r;@n#|8b1oQImLABD$}o65*1nL(RLW01 zi#YPPoSQuy=yM1`sL~pKwbZ6u5t3+aW5(3nCxqhq=^p<&J>1$Gq-8)}Ud@#wey&O= zH>v}vCJ-c>1@#TzsW-~G_nylbI#Y_n9Q;me7nY6UT8s9~U?*5u<(6I9^Q``_4rksq zP3QFQIBfPYEM8MG);M+W+U(q>@8}r*QBhMC7}?gW%(QlO zB-HL4;DPGYnUjOY*BLkUTHhZVPT|`-RI?#qF%TKkP^Eaq4m^MoLM1H+^)6w%WH?sF zfq@^1GAzbo80SIljJ>2*Ua=V?WSi?EkSOsIn_@gh(U6q_0C?2T3nSAD)tTv;5De6d zewHpkyQqSm9WoHH2WFT(k}CESw`b6)Z(cr`R||pvp=ex`WZLP>#PQF#ru3r}e^ic4 zCkU+q$ly3l`ZXY>gbwP3nrH2LtmMOEu6PN<=sMmbZdf5jmyCNrmZD^@3fxR7m#hx- z_4A=rRVEB|F7v)5(bB~rv4;vXS_=GMYmO>!9TWM5IaA*&qMeH27%9OqhzYN8GPLg} z0`eSNX{Sz|NPam=JazomE#p}4Oj}ud-ta11?7xW+J{f=x4c$K9s9~HWaE=cSv+o42 zNQG`+4G!^V5}GKTxPF|=M2Ye!%P&6q3s(KBS2@mmXuHzWiDhwC3CJ|(U0JVGSegWT z+YH*a>{(K}S%X>EL{M8YlLxA$D|A-HvE20Z>8A0C#K&3J?JG4;(#34nEgEvcp(B1) zicG$7qa47l+KD-vI-Q0!(tWxfZSfDail_MtO3cZhT%crhNo~ja{r3J8@F5iwt7moW zA=jvZ5vJDkOsrg&Y0VL4@=yf*YAiQR!!u;+b7QZ}=<9j3-4SgQ3^XcUf>{HsS>jXK zRLo-^iR4s`)_m|Xa+Q?Y5#wRA_h@@9aa5~56~}IM?qnn%Cu zIgtPwWJ_U}luG9^6c}PpEm5YhK&C7T*hz?iVtlD&5mJ#<;QmhMu2YrZ|*V~ynNM^c?_m;7)8R76qqKUFMUjF3@y z5StDZp;1Kq+rv{8r%WCxu*wBg?jBq8rqHA##>HvcVg>XZhe70#$rQewO+FR_Hw3)| ztolm7JAD)rDY9<{)CZug3@{awO(IHl+rNr?_bm|iqi|&Tgn~c#F{fVE^Br8ThS~wP zL+zyg?&bAf;FP$|%_s`si*W4}D)lRgsM5-@DZhyDc5|<1tq-%>9F9P-k=U{a>n3wz zqXenqd#19vjw6T|PK2rWjZVaK{vdk8kmkT%P)=!D^Pi4hsSDbmJ>7>P#pr`_GJrP1@u)GF&v(++Tyi9u6AU z&C#|Fv*ob_7=FsTxC-WtrY+>uf|{xJv_(>p5cvK^L%~9PHB9T|^-PQr<-R)nVU%nk zQzVRdAx}k$NhsNR!XOZ15#JlA_i0Kj2se{xg{kiy8JlQp$`NMSaTQ;6Ru>FNvQx<$ zt2}|pKX=3d(rywetqt*--p(jWNI&NYw%m+9F|EnzSg2JX?0FmM*YnEWi`j(egX)~o zufQf2xDm)$-`H|s2^cr)H>!6~g&w}rNISxw?;RjxhUrol!pG6JN?%G2h<4E8x3VtJ zZFUw`6$U7t-Ey1nsoe#p?@0qtK5Ani4NRE!t!YX**FM3Te&j?JACJzY@N*n}xOpTr zOMIqN>+d7omzv z*sX;&FnS4j$u~qOC4=rzaC2GK6K(e3P@W>pt%W;|Pb^`p=plzx2~GpR)Z8(`hcUiM z3Yk4#P$oDj^?(Q4+5A^c@d?9)u7S&^HOH0136AJDwd~098`{cklzMOuEBTyjrYN(07{P9oE1E2G?~pAN zQGXIPls{)-iq!uh>z$%A4Yp|Q*tTukwz^|=Y&#t%U!0C@+qP}nwr!o9z4!m$oXa)p z9qX>%F{^5>XAJGNzkXbVFN*Gdm`(N+uk*=L(BZZQQ9{O z+EQe8-9J=*ALRcjyX+3+w+^RZSB+)$i5MubaBBYanv&iA{RRw(GRxEz~Afd6Yjto+a0p*z2q4x^s^1Vqbo1wpP-W zpZYpQr{JxpP|y~YJ3}*=G9z*}86_``3bo&~2w@67&B?>omixT?mo($!D zUe{H;(1VYNdUT@ED`;0a%!aTb)+M#mYS7v|7z}p;zD&6i8vcws0O3N=IZ7df^uNBq zCs!N3?9*mJQ9Q`l_ZruYA!6z=q(s_eRP~whU+>Wf%Dxv7Krzb>S~lNhXEVC(bRk?U zqka=sGx)ew?3#UvKH@_;kHJl1Mc5+;6Lsu0x7^ zV}1p@_dio(2kJOXDZ%6B!-=p~&L`IT56Z+NslVrSYQx4#m~_%QS{`YNM_RqCj3}CE zJue2QiDOM$2^#Dl9oSyqXbX058d3{07$t4DO!jq2Pp};`);r{- z{5$m8Z-bFZ-45G{D0Z9~ZK}SjDhVJsw_!p=t62q?27JF7K8{8=>m=#Ad2UZm+0La#ON`hNkO0!{cFe_^N?vW*qi<2NE!t)eep zP0rCfR%%!;*hi`-hyD{XM?WudNEaBMAEGG%`lqvLLO$Cu(7Ehw-yD3jU=OQhdGvRc zu%E$6`H1Yk7J<$)0AqXVHfMs zMDS=pDzgjMwLHMfZJ?<$e}-KxUHmsvZ3_W~_s9Jv3+13(23)-m3qh2k&D2yq^%{b0 zDdQS{EBA}x5Ks6SvlJi;34Y<^<%L;5Q$ zEtIw+llFb)n_bP7wFW|_XVIKYwf>%3jIAO7r2di-NgJlIG6}Qx{HSDj2uR5OCO1)2 z441{&_cZ*dLeEaTTz1)^ajLZ-CT!d!A&>iQCoYE zQ*(rv1-?CLJ#on~eK*`YR;=%A{ng_jJF?XF+@xdo0D)=c-3ct0Anj) z;E&}l<^){beCFcpOmhr4 zW=uscwyhKM2dqsG)r3r8Fm?~%Vw#x^*Z|YFTX9!MEkT&EIr)z`7;8?lS*4bqCl}`l`{LH2Y1p7eFW2Y^-5^py1T$Ev z)=KIb+XcG8*uU6QjJ0dO{xo4^r`96{4hh294j^35`#Htavdn4fnWqOpXF&8nTOc?3 zQ#dZriG5$p;yX}Zv4?6zfWBzRSpMZduMu03g+bkQ&vx;dj_JM#+Z}rp4~HQtEI8FD zLswBzUNJN2Kyi4N>QZ%t8))^9%e1sVImXU(1o;q{_8QwLTE5euC-?V;!; z^nd$RH{Y!f7^w+Z(Sdpa7gi2J?m0L&fy&iGwztjqLrDk*z12m=1ld7B%B7Dn{sx9= z5}yC^b(d!Z;(VK1A~A8yFT1YB;TzkFRxX2q?$LZ z5+anNoo}5+#z+{?x^(7F$)I)l4>VYI;i*>|i5t>TV}F--JDe{Ah%w}e?O|>yT_8H? zxel>0(fSB2-}xC>oM82}ZL|Ya)glMl4@H=Nr`8$mf>jA=QtGq3sF??V{B;+z`74$w z>72Gp4~T9fA z&yY7jsUeX5QoG#XNtwnHL>CFviMH4GkN`H^?|Km52Q98cjmJ-tl7bet>06zmhnVC> zk}qhsNPgB4o(=gYyy(*|rf2|iKs_p4Tv6PKLn3Tj61>D@B?U3_M0MqS)4cl=fn(fi zBufG)u#}Mk$Z?loS`w!3A*FR4B=tJR-sD!4r+W&k$TcItBTMqVgj6f`SX^2$U-W(8 zV?h0Z2M3`eV}I z&adK28GQG5dcMr!+3GcTQE1*|Vtzi=0>N<66gNmLB|NF?$|}j#jiXx>eaWAlizJq@BxV@{ zjtmGMAS={gf*sMv00eK1taMzfU=Nol_FaHj{LG2>TnnKFCKOm*07~RfRje8&pS!`! zH<1>Xk{xyw<;4I$deT4VE81BC^nI_w!jSb$(hq!oPX?~*Ur4gy zA>Dgvtsc9{MqM}@sGS7AksN9MAD=HeEK@1`Th6S26C!=_*tVx zJ)3dIbe;2_kfi5tPQ(rjp{7uV0`SN6#d27qOuuXeiz!=f*91-;QW&i5e6YpC`3UHv zG?e+MMh?Frg@Q+|#_piSOtyE{)?A<_6vBvXxG@Xrw{J@kq?kQPXMvQFX2x&EDlHxy zKw*=5ByKdK9&X2linu`^solB4UvfFzTkkBR9V`fIESf*DeAOjKYD|TR$LN%XN8B-# zbHmwBla8NQ659-0EvAMZU+!|V_cprOTGSH>3fUrFMl`>>4GJCH?ljp1sIjfT121pc zo@93WY6ixRsc}BGKoAd1c6ggRo8?ysI1*kAy@m?Ydb1};1KV?vJQrJDtw2Wmq>8pVJ)I`;5K1Y@a$TMnq&ErubxZKOGvp@!K3ER~A)8kYYpio+MKi)Ek6`ig_)S zue8&S{XpFa8-fFrZDxcNwbt$A)*X^A+X>g8Es?7MXp_mZPf;-tuu&P)3ph<*d?tm*lG1o#*!p|!v{6nxDT`L%g_^}7;1Xz{VnX%AwHl$ zYJsC#W=|PpI&3$l{l~cPp6? zOGKGpQg(-J`o5pRFY{pBQsjI*lS7!#m}bK3utZaqzVWwmq;Y{RCEyy?w@S4Srfbv2 zHu8QD6{fkJ1>U&&_c64X93HUf=1NS|V`*aiIW0PUo{kTA_A^**`n5i}Dy*NZJuEo` z=zs417>n$3b4{oPtqqXq&2FY}sTsuFup0MXd@Q)I?!Yl;rlOgy!>IaW9neNbGdoW4 zUIQ%_L*Kl$8?+l~igD7zFxH9-GW1s+?A+J$1gEP|XZ9{JO~pN1UVYZD60|*twtQx) zwtwhldG5W$yqqMZNN%+omH5hhNs~M+L%fOwbxW3pDd^eCUEC=r7YDgk@ zw+7tQi!a#Hk{E< zk5*)gsrn&p0%7_Lld4DkdnZWSZOLS2awo(l`?DVmQ7(wns1W|d2 z)}}0R)CFGLdi4H^iysxjxI=5ws|I|58y(_1c|3kJA6M6Occn*;eu7&GO)hBH&B5QW zQbRHkwS{>1+ZZ@Qcz0GblXH{LNfRBygs2CO`#oWG zVX7BaZj&!3_WARp_mDEW-=@~Q=SFzyK5HH zs1z&Q4bsSi9b&T=x`P_*((EgUWEkV6>mq!_LKHf|wkr8)ODnvYpwjVX)`e<^eHOrp zyVcWh5k{6&*?mEFQ05-}3{p>yV;zZ&&{4cS6(?^40LV|Kr^b%@;Xn9A^|Ek~O`ZMQ()U%qfLd>`Z@@d2fdK>S>N ze>o5jIplPhsGXZk5Nlds$%1N14`h?a=SYQhl>GQ*kiu<+3?=fo#C*^HcmbfzfNf$N z6JWZMJ{yNIPo5c~;o~)^N)vfl9iEetBR+MGq-00NrYQir~&? z^O^E1d|3;y>1y(t&g(GanZ?EX31x_&a3Zy$KBsnjzQWln$#h7p5+NoZ`zn74>;@>3 zDFAnhNr5rcgQ9Ou;=_qG&Ahz_tm5f8Y{uik;bEzqen$t8$1vZYn;Gyah(kIdbC)Lw zO0H0jU>btGGCj?}Q}Joc#I=7q=x=0hB~Vx6i!Q)*OGcP1AJ!gF2&dp}S2HIa(~KV! zA8@sQ;=gvA2*>OFM^GmJ4FN_4Ct~6FpT+hEIQRdNa>vLs!XSu+kjOF*cDSsPwGSKV zA}<0CK?im(SoVwtLe#Xr?oTB+xen;m9cg4H{oFWkK402!NjZr>4kU_XO5E}bP!!un z&&!|HNucAI)^E(3FZLLkX?|c%h=C-^>Fbg2UasV+Z&9PyQxD&*-EZg3Jb(=<`Z%`O z^iO0rCXt~r)>=VpANa4wyK}qe_3fu^y&x$JbKPsoy6BWIXdj=df-3>&#Tu@WQ0y&;6dkV+YU!}jn#I;wP^lOZpv^`hcfQQ7U?st3};bOi}$Qv}7PxDRQT6aUF?rr$`xaw#2A?w--W)vNLf&;e8cLuOBEWJGzy5Fw?=F#E zcWWVobPVP_y5fi;N(c15J)l{F%TAgSgQJppv7h{=q@w{nGpqlv{%~#%gB9o>Mul>{ zZNBfKVjuSh+LxVr)A=1Q<_m6<=Ie~H*SNzS%mbTcbncU3T!ak?lIk5+OWW!m53-=2 zFgAoC+65QN-iQD~L4Y+WbcjFx5Ix6rXwTe^pRbZgS|HR%-LFJeC)7MgR3(O|tvH2} z`1LBTDD=Myt1t9vJ{NAl;ZT3NO8Pr7y#%|5(%r5zptu^!7jsiqDMz5P1A2Yf zxYuW4&qk}U)D&d*q1Zb)w9$xL#}&=o6^uvnDnRix}`5IAYZ~{fHW% zu{!da6L`1~O=qa%wNuz9USuvamgKFIs{selRq|~1dsUe*GHLxOiDI)r(}R zqCC#fk+>P~ag73phFOxkSQaI#K&4net83G9FyOoLCV)ilP$?#aF8!4M+MYLIiIds1 ztu*QHn48QQAYN1)BJS?Ke7|*ZHA1J6ev<;3jfjP7m}hrM(XElc6e5KTBoO!;`0G_J zLuJ5`cz;M#uJWjyMt&aJ)x8=XOKB7qdG4CZ1%bJk)Z;%1hRVpptYCoFkwR0gga%Dj zlS36K{s2z&uZY(2bBtg@?!7LMuAy^)wn>ph8_@d8leAyAipjAG>E5$1-)nu)%7@|u zs2r78pHVMFd=GA;1>l51m=L=l@WF>EuQhLdrGiOD8=5!d-F;s>AX69*fRFk>rK)i5 zg`J4ifs)2tu>Pw0jz+Nwv&w(hJ+OQRAZsJ6O7II3eH%!QJt-6vd2C#g;9UlD#L$C*qh9ZOqLYUw)@pEe8?0A}31S%T`%07C{otnd7awja<09GNhQ#q6&qYpN zZpz&P9JcYuVcPqDw!V^Z6%ZG)fi1T96Jcu&SN$M5PVcGd)HGq3Xaz$Ck!vahA_0Sc zTH?OH<07ysa%^)kJcqsz{p59n+YS3Fq)<$ElZ|j2rpDs2E%3UW$Q@(J>+4YtdBR4? z(0AeiK!XWVN{vFb;sr6$1TgvqCQHbPtHu+I30@^;cPY+KPST4XHFT^ni%it~@qvd0kNnpS*IfuBd?rN1$d`-wJqt3L5!x zad1SPe=+!tj#F(gXC!qAc|AEnizv$%K(nBe%^mQ;XdY+LV(7H zmakVxK24D}t=5`UDs`WXnlFeteGNo5o_**$kdf#&l+{6M6}q{a2lvMBSwJT|pH@Uy z$5ywGdp~YXN!}6L`~9QrMuV$9HMe5x&J$)_L3wRvA+GTUn*qG;@=G(h#^Yr8a)Wt| z9W6nAp7|HZ501OA5R;L}cDN#Dko>tYJnxtv(w+(X=b1C16+h!WmCM{4#E!obD_5YY z*$Rn*ICJ4NkUxmCmmN032!NcrzzB}t`?6uashB`YN*GCtnKRfY3$3f4bHJER(O}6z z-H_8=D~bcwY3x3mOKWGBtL8j}PZPy$In;dK21q9? zTqh#>>U7P@d@y0A23Uh4CzRC@H}%+4R>aE~`ihdEhxb|;!Wm1g&6)KBl^#&@lilGv zrMOV3mkdr!gpixs{VQTup$8mL%J}JEd(n0ZKkYEv*qm8e8sBW(WgaD82I04IxZ;54 z44xvw9JIM&_FW4dev9+71P8JNDE+4WnKoiUpe?58(ZujhRaT}Jnc2Qu*dNPH z4fb01N)@daD`3$f;911SJawXR=Hqvm+G;17|5e}v2GB_%x$Y%|hCJ-kAX3iy>?bW2 zoXdH;pKjv1_z4A#E@Ek+pH2*dNftZgJJ4^oMgZrJ$^J%gN6F(7*!7@W@LKh=2~*RU zLKZ0W$tx5y?xSRYy!{qgJKocj+B&ivb!j0=UC+{D(B#*Zo8CA2$FRisr40@*f>Mo2 z*Dn+h26vu)aYwlF4U{LH2K!%udj0^63icyV+2VH-r~!?dn*aCC)91N*MvF}qLqwY_ zHl%W`as{_|OxmTwm@tlS21T+zC1j%CX!+~)5_BhbHzNMg`qWP-e>O7z)*D{RmgHqL zQZ7-!pFWylxtQ6TX}2 zb+gS%0I*{VLN_k#^xk?(uV6e5Z8DR!1Tte1?a$oVD>k2;cigv8YNT_VhZkY6k)=XeZ8~ay1H1l&NpMjap&`&f2lNGtD!Ym% z0$exN>eIHG+!N8+Tg6|g&o#0ZhjV5SZ7$n48-5M$3A}K_*}&3*OnKm}+uGRgWU$>u ze{ywdseHegR2qa(BcG8vQg`^HLtbL`c1@mHT;$ThO6ni3+!Pr9_N!XIKdf$#)F?=wq97lyC#nC!2 zRwAoKzk}Wiw?p@Q_=suI|Gbb70fu7#WeC@NK0cc}1#(~|&oFFm#F{qFqP5KyZFB%X zP)O;v%`=5^CE`NS#ro4buEBkVDOZZ8trXvu5hv`w^qo(JDdUD>Y3xg9^o`MIH?=Bn zT%jw(mNIc5r28uB;}iX)y=>2bmq^UT11BM1SvS7|qi;D>TqFdOo->@10{Zi5L5U5R zICgw{UPnM+BvhzBTrsK+DKR<=_v1yU>w1HEug)SR;^ARVjx{*o&MtT(NO%d5KaOi{ zB=bPg%R8y9+;S8o?g}tO2+Ju|50+vza=UyH&;nT_kHYl9c_WS!+IFEEShl*_oP~O% zkJNsX(yNnT$JWfCOza*g1JF@`TKSuMjm8LsUDOi_?Zoe%M2bAv#K=yZpieFAlT=4S zWxL#oEQE+0^r4dM4Ygz34jk6~F)_o@#EDbPQzUJL1p2SFAn+>+NW0Nct@ESggB12B zzlO7`l;q4yJU$J-?;jB>z+0Eb+^Y3H?)KhbFl-nJC@BvE!Ic)%v)50b1?ef4o!|26GhJ}GOayoYmEG1OsU4Re9^KoT2f&KejehJ>j=3a z`y_8l>*@koiuq%#6V5ftQP5!pFZyPw@^*U~VzFINe1<~qu{n8U-%t`n?7oTW9EB4Y zgI-@XDi3GT`wzh!+Yo&eE}P`DZW)P zc%(3!+n=P-CYW;w+5F7=j}V>#K!H=Q+es29$lAXvh0S^Vv8B`Bg9ve{!>3b1eftj( zj^=ILUeJ(OQ1cpo(l%l^HBGVI7m z;u*gU$2KueF>>eogKeqvb6-sYDiDRcii{@6{ z>vs7D$5@O#-)R*&fnN}mV4KP#L#&bHV&GkijR=B!XUbo3<^xdb4TeSj4f?S-)eW!+ zA_D9TSM6dO0IQ*oOQ<|uQbeL0ABkd_IQGRmkAap4FEq<=g~vZ0zvI|sb6$Je^DO@1 zxQNzcgkDJf!}f{`PY<2a7El{#FoYct~DKH0fhPnzv>bh-hCiB)SQiS1EGfT z;L$ix<37A<8iI)p4*o)~VgT#p!EYTR#6X**%l!bk5#HrUPf8|t--IjHTACj3=o3Rg zY9##%1s#b)1<_<-fm{u_gU+-J*3L}3Bt#OAXw&fAi32XTE$~MBi(WJux@sPDwSeiD z9cGYB46vR_0@3p7(+A(Kd;uSeHvIe{vD2VfCHbTyx_c^fxRyslBW=yM5ykWlUG(EH zBCCavLKcc=Ed?#bP*b&3lgDa~mu2ee3M(=pDwQO;d!O00PMpBfpi58|RZQiOibW~h zn$k+q)5^e44F+O03uA5Si>t z1Q3hgP%*W!kBVl;yJxB!L=~dj`GfXjFe`sH01usUBUIapSS9NtNA6_Qiv2T^03?Y| zYtd**g^qCb*`7)ufao3U?3}kVRP{A*xc8o|_dQT(wDl;S8HrgNf=#7HA|2K#FS{Kk&@rC7z}27@g;a!G`^3 z{_I=(()cT+on9cY7UcKmI;P!IN7h1Lgn#4&71e!2A0tFUtCdB+l^+R?W~hN6EmR^T z)?m-j*n@*eXvIv;jErS$-xUD4MiN1agj~zvUjgL3n+dUx;WJy74V(%^a+ZU;8epSn zhTbC&x;W{sAZT4PqB^sY3;EW$F|S53YeJt8^ILU}tFa8n2UtA{aelDVb=+`OJZ!Wm=@kVk!FFuijlkYYC&5YH_WTGyqX<1jIo$J~S{I&mDzG!2O2&%jtCN+;@B+K$=d9 zIP36U{*f&p9`KmF%Ze<`qtu^Xao0c2@QpRP5lFlKb{oJz(R%>DBFGYCR~PIYvjkqzg&GW-@%mv}RoIFnfcBCSU-oUom8 zW?#-Qdx%nCYJfExa2UlDO#>y14qHf{*~a#GB#O&rDd?lnRaP=FDF~^nRO;a5KGpK2)<- zX*T~I3LR^aRBGMSll0Fwg>AnLY)8nFkM7db9i6MXh@$*O2gC?VNDiPZBs6s|JM7*Y z==ES#i>EzQ1Ye>j0j)>{r}b_gOli4}+@ybHLRfS%t0Tqr;Gx&Bsd{??xW;Ml+d&Hopa z&%w-^EZhi+)*K!J>_whDYygIkEY}DM)r?;WJShpv!OZ#JW#C8PTKu1q`~Q}x%_Arv zonoLIEX@C1%}@tfr1~lU*Xy4zNGJPG$^PGuwVFWwsQ;AzYc&B6icS4f{-+gs^Ct)B z7A`0U>;GEaDS?KOfUy3j6MA#}U(hF95Z3=RLPO@@{Qp7zEJU1KtSre)mB47AT-dnX;XIUzpIrup!6n{1l zB^aOlWzX&BuJ7+Z+s&qxsoU;P_0OxG*=E_P>~ar0Z`yTDOja={aA7-p`$y=IBUWeW zcefyJ?%*x%?yn9hDR4bdP$zbHA)`N?wR|Z!h2SB;dmT~^3>da-^*tfM9J9L#fh#zy zfdrVDk*Bt=fHciwa91Fpz|BFB5s1tVqBw-0ga}PRA&5J^(fuDoK|yv&ruz3!Pe{UHMoEH2+&0QQ4{3zU2gE|f1A zkv=XGT9mUx$eRBK(8wn6TNw_J4JOh9W8IJ**zca~BcPX-*DvVj;dgZ+riW(MnIVXy z3y?sMfZQh_UM4WG8MV0`2{&RcV4;#rMiL|tKnU@tAAt}Pn0X}G$8UTng}6m1N(wz3%0-=$ZWo^($e1 z2Hphh&iEiB#10DT5%|sREi|Z@SRl|B;5#ZG6zqF+zuQYS*M-S0YLr| zbje}G+WQRgQ>o$w;Ra7*I-nE!PPT-5vvOt^Zqj!>{%$NI@g1><30fmXpB$tFj7-wL zU?K$5`gIC5@#qIe_Uo0s!g7AF+aWQb6@Uf=05+MV7Pbi4j#28fCy}<;QX9{iL4$VS9&+> zeef%Q1brb-!S4foFOa(B0J%~Cq^JuptgApi{1AY>fUW^hH2chDH&8)$Bkl~u10z5{ z*H7?9kLCsUC!jmP_zP$I+g>~-rJQ#K6be{4cJDRh9rhW7bl#VwN6P&p@}AU4D2876 z?E^CmEVEa^^#x!q)r0R3)J}x^C@&=pgotMR{VozB>U;A|1J|?fKY!tG=t}jHeD`hrT>Z7)(%wp}g6_~lott)@W-Qh@GpO#Slv@zzDZkvI+H8|sxZHx~ z6$)*z>sKpkm4EJey{#tzDU&{8%}0cc-!lG$OEEdAXAEvLf_&BON-{+%y65-f)A>5W z%an4hw=ZkXi+_wjBm^>T`f_dbTVFz!13t6$TLe?o$|PO?rqH7O9>ya$*OgKgeel_u z=R6&Q)64#FZ^l=bv){bP;}*_tnLP@P5+VCrM{EHwP2B)qW3xQ~&6GPA_O)&S45EzW z*#1bBjW%}inlVIMgM8-%M-73O3`XTO15gt|`f^qWFBCtF|kl`^4zyr7B@d-+>~_!^e1g zZ_{qpKUA!JWcpQS1%GMJrLXtSf#xz1Ou~N=E9gPUf6wk~e#I56REB|yNyA;j(>aCoV(|Ev`48Pxf)2ELH}mNaPqEXpSKVvdc2<{GJa?hXUpvJZ? zdbRTQ1IfnC9Z%m~8g>tRuXEe+O>xFnMti}8%A95(tTcGB%dCGj2woXTEFTEl@yeb> zCCltjg1C1`z&q^n2Hbi^hxEU=sW`&|bWo=- z#Qu1PcGz!6+zz&z=e^;YB*%wug%C>rIU~3$fqz*6Xy>7W(}wyy&me2O4jARp^PSMc zo>%aCbaflqUdZGlRjfuJThO$qIbwW8mCUgDxHQa&N#2U>CjsW;x?hD=ov`J|7coPk z`>fK!oXw*P>nAhEs?md8|2h~|JpNN-lx03_cwwsN_JMf(;B^`^g*b?vlfN?q#t|#= zK=4`wILo*%>4le5d~lNxx!tKB`R?T%5~x+ogb=vY*B~LZLyzH2e)dZ~ekCKFhHzYz z$HGafVgP$2t{)vv?P>)Q_K@eE9j=MaJ3N#j8g5N=ympDc{)#@CLSZ(di(Tb@V>jy2 z34V0o_V;cGG(Pg%9mmt_p0wL`{`3RMPLv@An3v+4<>{UzvZYiPBTLf^)fMuq;-Wl0 z$V!yQySm-c_ZJXL)Vw{IL??9wkez0|B}k%BbN`JarB%wv{Y)oE$DhWcsY+L>rz(0t zxb{35EoMN=JwBn~+ELh@9?@_-f{%$lKx*JcjvxNBF6?5gTJ21yFn-R;k6AAjW}_Mc zQ1Qypuweif>FA=I5k~~aKS9JjD_L59EsIW$>@DrLj~t>wk zoY$XYh*_Ohfrgf+@Tn{7M=5@?H0JIhbx5kuuJ!Bftr1gx>+sR@;9m{$W>6Ub1!Rdh zYK9BVr%haq27q=*V*Uu4y%MkrZgA(>DND$Eh2edZs}O!tttL zKR^D8%bKQ*t7mQ3+5RUDO9CE}cu%Pn)Rmfw>sU(a>GZ{^^BL}L@577n%6h#R`Dfhk zEY%X*372Q~fQPE_AFV9Z;uHVl2%-P)wK%j8S9BJqVBdl{bf{xV{FJ?L)i4`Mzyf0G z#S4i>mAR?x0~Ta5r`<4S*92ccBZSrjYnMsXtCx%AZ!#F^$ZZc;J5vPs%OmNPl^Xr= zJkFO70i=Jf!EL(k4P2VtzX=wqrWJ^gzsVm%+QGwo(4s14EQ)F}$YvdLA)UGB^-;@d zSi5ZVB8jcf>LLa*AdMnud$u_p0c8Lw3IIjq{$M{H(^uy@3+_$}th2d$y}{9Zg&lX6 znkLg&Ehy?oo7XcLqVF7EchzGWpWC@p;q2Vfi;xs$%@1+%Bm9AmlHqK(lsyk9E@M}S zD&s|&cP@Xp4ouX}wLzCom{8xZ$VsaD+%a@BQ^;xcg2HQciC$6Vo9$8-VD_Cf43e3G zTE(e|=bkSU*BlPk*f;Y*L9`ls7Q<+!^dZK$&~azf)EHgY!3L^aLwI5oOZl1H*1){G z5=+9L$r!P2mp%w+0_fvfMk{-D+!T%^71ljFQNpV`pReu`|wI5gYtC$3$g9wouJ2vrC}UO@!I|$Wy(SE33@*sQj7kYE}n&j-03w`xtPp0())Ttd!Qi zjvN!!=8(7!!ip9^#Q4%;N^Fzz8_ly{7O8};Yo)3Mba37RK*wZ#U;>GE z)e~=GM7G@?b3tL&iVXgWj3VledmZ`m`u?0QuI}^n#3lwEcVXT9fUhRA>-XUw7E4A! zxkR@lw~m>`xWDof$3Ev~0aCAvKv~cKj_E_av3H+k^&2iiP7RqqZBgsX7-96I*n>mP zFv)bI!09(u02x?D>Uc|DjZSu5drxJiqG)VUf3x$T!E`uI?S&1FZHh1HS6h88w z*jJcWz_`%hY!_dZT75Gwk%weX`-2Ilcrx&?xOSLz0}#~vsm@+sccux|r`!sVcDS;* zQBQ$6MQc?>tWr%Hue5IZfzIfYtHQgu+6?66+)&D5IGd%V_}2PQYb~3q*AZdiVGtC< z7_DCy?qM@&Xe9FS1tC<>3}=Phy{X0$%Pp1(TO~Rpw;_|T&#zcs1E&WYSj~b%C#nMxLoG5YVkwDga|t* z(j@9pVGXKtxtWl^mugAbXOhUL1e(f(twMM(WS3}@2H@lz6LxYQ$*m3}drZ@{<9^j$ zSE`zA!?Gw|3+r%L8s2Jpd2rCkwhsiu^S7nA0TcwSW|Bk}(o<_Hn7e|jn*d&IR=Ge7 zo$Ry%$UoFe9rxjF(eFg(MW($WMqKhs=Ap(I#gneh1cQgH^4~GvhtU>-quI=wBipR} zB5OW8omhH1nudC^Cwm{w0+jXpGmb0mBM^>DjxMrq+-f(l$fJnh;l_aU!CyST%Pr*e z0B%xPJ)PtEDl($13w(WADZw-2ns%WQho){P(0zY%ip~a>p}nBj(wtnl`IW+b*+&+Z zYzn|X8~9e^GZB!eR%gi9FB9@vc>q&xBt;r4B3V0Bq4#~-I10Dq%ggsZk>q|cJtnbp zv7d5go%&VUN6D2JpsRB*dyg*5oT!wf0I=w#Hnn0fT;}{!5?x9;f?L(|6-A$(D!%=U z`Rj2^fo<#d@0-H)bCmjl=uHB(Z2NVb^R8i~3|9y@9U9iJiDGE_we+1QMsorHQ`%iq zf}u&Cs0AJCU{MnTEwe^kUDYN<7DsmtG78~6iDVRXq{hX9z8E=p|=hb zGwrrvqUZwMz7ZshhTrP1JR}QSs!2%{#Kj(a=c~8DIN~zqoO~b z;D5YDGX-%&o)+9LXh`>vj9W_DW-YbdtRCJ}pM)K~d~Nz?x8Nx$+L<$*9Z`51RJw2h z+N%%)4IYHFsk-l~NlW9!%KPHV``vOrT|8DFJog55V zFg9ukh5761Jk{)!gjc9yY%<9lxg4H!2%d>H50OM#j*RQCTue~g9z7_rV#ZfHhZ<54 z;No?-6N^P=X|GL;!{WK}-bvc<835=2u8QGTruRQQX_piIkPdvCEFck)va#$J#~p8D zeX>617RY9H2p-{XLd@9vPGbS2Iz!jegURen2CiG$j;U>33T}s=sm={U;pU?)nZdPz zT{00*`%0<0l}A+B$Je|PbQWF&lp4TF>iAHno3;b~ zm6^OK`dl{nqDv+WDY7BKs*LlWB+0MAAk=JBH2iiW9=M8ZgVnfTtIqa! zcX80H=ZkR(kdV!{pqCb4XliNUWRk@K0y|NP)!7*<%Ba+YU_VNPvN4;8TawX54- zHvFc0?EX_FR%aW1G*Rvgc<}PFN;@ihZE9sq0;P-_E(m1ki+m@;Mt&AP#3MBjPfCp) za6GT9ko;*pSq?F-&$>QLmyEIr1d zK5Jvn>rjU6XGT+se;3r-ZLbI+)Ma43e|&X#Q<%o@Tf_hNsdoTI^dt@(7%B@L#U?g( z#z_?xzNM<_{F#de2&-lfgfLZ^PI~mb_QLbYi8PPJC#HW8UY+#mN8IPlo1rlB67hg- z(6jb3GG$r1Ux*ny)Pl)(KlOA2W5rCz%UM>(r9>np%&`2`QG*gRQxmd!w14E?R$4HW zXs`wowh%Yz})!zmFaCxOR8;t zEA8$am9-1G$vU8RePcQ9TzV=nUakL&U&6JtK;XoV{xv8IZ@xj8xA(^+bmgB!L-Yd4 z!WWK~in+Kppba9fKGrwSwv^XqKIVFa{@P0Qp-f0iglc5;WvHyQwi4HQpfH&9U=qoi z(H$O0V;ZGDp8Z~PJpVU?*0AvlPuDMlKvbI}qu*yW=1P$03CdbbjPRmyZ75Z=u$9v5 zmx}TZ16|q5_Odo-c6~K*_(^YEuQ!vx$rr-0Y(;sttcus?^{PH?a*M>Zu0wLhF z%#LOU0ZZgrD=qC+o@mLQA9BUjLvwGOGf)VK2%ck>lVlEOL}d%mS7VLFy3FQe<={0} zdxYWG7(5I@eWp1I?Dy)nvX9vozbWJ3?x8<%j_njbCv0iRj8l&Ep3nt2+#kD6wSOVa zD&IB~_0;inv@XZw6&k#MP#D0I1N28Ct;C4e0EGCgQ;DgS+15a0Bzq#wE-4?Wl)scn zPqCm)isO#6uDNcKBwJgGY8sG zl1W7cy|YTF=b=38dU%<(c{C~;>ARRQ140aZaA1X^Oyp(R3tb|0DtaIvqxl5g+$O`@ z*U!k}+xZ`xNxY5p$78j>#?p;Sw756CfE4#1!$eo^os?D%qIe8S{JFB&$W&Ydb=WF8 zBTN42)F~YOiCNX{x&OrED>~QVf&tb5ijlnVgS)hfIZY3d`FQ5Bv-h&$!|u@P1Yqsz zeEhNad?9+z)@Y7-7MY_V<_fKGST$Fr*i@B!>IrY6mmXplyz6SIQ$ChBNM4XJoQ!Un zaM_+V>3_to+y+oSl>dQUc`44~xlL|Brwo{3_8e@<+(@B}c(Kb2A2inT?b^8NYI=q( zGKF={ASB+2AN(|1iHX#;(AeR&1qjoK+OAVate&AtHm^$4ZMUr5PByJMvVR166!F4= z>IUdN)!~VrJsD(qj;+d(*)(pRZ8skoGf$)l*H_8Wd6A@_GbwkP=1@*;U*oMtIM`D= z&kcA}2N2GK7+!f$#-;?$1X%7hYkX-6v>zL)v#4ZG#c`g|9)(2S5kevD)o(qOgax zO*7NZ`Jc>W;b){AgnOoDW)Kj6nG;FKKx0B-Q>w+LPx{W5Ebq-P1V?-*T^9zw5FJA# zU_o*@M``H>n{E4KNTU8-20XoP^;zFvP0W%N^(DRzuZ{Kffl7SzW*>Ax+W$}nne-1X z?u_Dlz5QK>l7C}|8L7+09WIIquJ0)&a{(CnSft7g-& zp@5T^-#$=}C-d9WD>Io{)Z}Lv@r#9!XK&8F6+gz&ziyW~aq)}xDDsh3UWLJ`IaQJ# zogcw>KT>sc(U02qt|E-AO{Fds6-l3a0faSlyodU2fUpBTjCJS(G{DtmC-(rf3+4mgwxzf8{2rIP1YEiE(l zNPoE(i@CG@1HG}x)!gy>+^CzQLoV8e^P7l6O4hAey60^_Ewt5vfAbFHxbpb|3EeRX zWa!tw;W_P14AmpE<`B)N96?fHkrhhpcq0QmM*C4zb60B@6wFnCS`A%arSx}5L zGa8uz4>efVkw z?iX@Ui5**1mqPelV}xK* zZuN{DlmRDXnEp^3*+~-PqvqIh{OlPpumpy%+BT#gw?{xg|GEcS(A}{eGN8|VeMnPe>qbE4G}5qf1=3%oreMmYnFz` z1}$Y+9se{FqSZRyYZNyWv#dN$y9sq7lryK=-U4{OObUOctCR|(^AuIWZ8J(zyzu$H zK{k2M-+~w;&^=LHeq5g`y45nd^TA1nFRGg%!)wrOpeb!>+5Xg);AVSp%Xp?5=SR$! z_HnLM1_}Gev=Zkg-0lyyV@SrsIU$=>rNk3js&J z;oQ*Or40&u1M+z>g`NuNv0cJkB+}qTcv3tjF1;YKl{ZgK&NPxdncUfP>ZX{4BnkqU zQTaI4bGfV8k3)*}{&ESUKT&tC9%<>Irkeh@NDGL{i=y=WVH*{yQiKTV{0 z#FxcHXH+PrSLktpeoQvB?z#1Ecp z(@M|&Yizs+N9Uy})V!m323+|=k8xT(>%SfmeQR~9R3bMzEk`ZW$}-9ov z|5Cp{I}~N$b4}FqijW#Z5((h%Cr~zz8#*L8PE4j;#x3{Prb=TNLw(kJ@r_WPhOeTd z+CLaps%;_>Q=|7JY4GP=6!gXOaC5NebD1?V`X zjtqw?y0Cg(@)C2ye$X(nUImbs{_-~VoS2&5FYmM+D^)oPN7f<&1-UN8SPlgo`MAdY z)n>wHR+5@H@iu4PQ@oGNYt=G~6kh37E^5EN^UkWBe?{=9kN|BZvTzZ#QIF~ctM=o4 zz8o4eaWM^jQ7%)&n9L4Wye?Qy#cdMb?V67UzC8GtbzD>>PWKSp=m+rO?iv^t3AZg_ zzPMrlWOsY4_nj_4jifIRq%e)rmh$o);R> z0(?7wBoRMmWSocJ#acn`Frgsv6A3g&3T(;^;j5jqo>{*B(ud@qE|y$Wz^Yi6vy83U z{ZvP8bgt)m-cmM42eg`X=_QcPGk2WFe;dT(0)OpV4byd)XaVe-o4$1Bq=7WTWy7xT z&hG1G2*P@K7nAdsn)8c-MW2ErD4hR+0~(of(6T-~5aOk_jmyDQmHXPq7A$o4%VfaW zz(A3LC0~|25Ib>?LwKLE;wkbI)RtSr{65Cf+?l$9oXsN51Lz-4(%YDBE5_yeIoOdA zdRZ1xw!Cd*n*eumNKk#FIFuMlY)&5>md>P>>zVhOghNPpl zEszY|Vc3CW)zm=|U>l+_i(AC0eL3)8^tAvU7cb#pnMx$w1sPPtV}W!)dG|(L`=_%= zI~zJ6pUdu!Zre*a9;~4ntgyjQN*r4hgSLX+Y2qjC@Q3OGTSt|wG8&s1p45=tO3fKD zHJWpbHv=mro@~Vw5P7zF*jRLAzllG-*!czc#bT>r`hP(MF|)V);C%9bpf~?T>7cf} z&VXz(0kixUq=V9O4gyN_qx~Pa4>21kDs5S{h`&gYUbu&Eaz^r^r;s<&{_g@+{QBAHz$n#7i%hH8f1S@yU; za@%c?(A&j-($}mg5+BM7{Am*7+vIx{gLJtgBnJSPEXBP$vS?$$0<Y_2>g(RB@r5)Q!jJ zzZnqo?pb8-n)khEA`@BE9+c}&vP+S}Qdbxy3)+jIS=;rYjR?RsGwZ;|Nq*NBAkQ*z z`wao|(zv5Fqf$sVbIbZV%??6Y#Z^-OhT858_$tXLHYmo$h8a^!{Gv%vt{ut^ zGLweH6{peovoM=p6%uZS#YUy+sHhB-LJ&=VY@h-81@HYyeXp{bQGN@7O1yMK!cLyrdNEJ^Di5n{NgW3wm@XUCPRe8s#OL+!O2 zLb3!CbR@c|jN=;`ifB7|Ai~Bg0yO7_am%wO|i*bD?1rwQv@LvM3LL zI>b<}2ew(=R?iPEqPAtB-@Kk&4nBWGAWUhc+ep=1><}$1tTNdGoE5p~ZlUP@#z5Q( z13ydlAnHBH?Rz(rgM`*ojm#mZ*SPz;?7jw2Mb=mks?>r)rT5$6Sqo}ZUP@gjqZTnH zh8gCDoe%0%;GdKWye5?$44^&5A`PjIn72q z0i?Wm9==LDVV51@A=s-#u@?-Z}B0@1c=QRC&Tl!1LW@l(a^jl)945YAYuc?}n!tX-?W zVvo4k7ff;DO=R9ld?Ce){BN6hKyPgD+lW1irQpWIwuaMka?b;99VG+$BihD$4jg!VvI+E z2L~d=-sKk^Uw5Vq8OA;U+FlbNH$@jSzICHLQnB3s^*q13Q~DcjP!z!Na;Foq|8=9D z>3Z&0+LaY?TT1>_N*-XrgWexuu`pPBX!b!}=O`XsNlD9D8`iuqf;yTQmk-2gh4n)_ z%Gp-xVK~QorDinS-bzEhxJYG^9)#KvkAGkMVlb8g)g_&XLP3y%9u^32lR~wlct@OO z$P~Z8g$Q!v;=w^bi0qD}j~Pb0S8bd7)|^-dmMe1FxLc41t@(~)Rec&45h!3@zu1$|yy=xjD13>o7`x<2)qeOcz-`AgIw z?qnG!CmW+*rCS$#b8bk#Pvt7^CQ_qqEUq9AtI3uqZXQ15UL|+N%_xczR&^)y7-`QR zqC*i!V@Q1#fK-tR;Y(sKv5d?k zeAxohpTS9&L7GkAaPdQSexut+TXBOmWR2v*pNHTA|>#XZX0>M8kHfI+3&oI}Dy z6%*?^Pg8Fh{$1ZXmlb2(Q=m*r41Ibq)%{K^YvwRP`)dIP_UG-r40me>Z&}9kIWvDx zwRechOw0K)nVl=c)Lolpjz{QE2FlZ;n1&WQfji5aKs{>t`l3GVK zazxpkTHKMBK=w&A%VE3=_O16-lUR7)GfNB@j@33TwX15DmEbH1VqLmw9?I4JXm#e= z?kE!F0oaI_;m2EO>+6r9)ggs7jUsECK$|4>YAQ9OrOo?;+}p-i!oUN)??Paa4(=Z{IcH0wI8=s()LU6Bi+v)!K47%h1UN zvQRxBJ+MhM=@RUj1~#KbEWQt6g+w8>w1JA;q8Ue%SA+O53>{vdH~`xvct^i;DNmG% z4>C9p>}u|hdU-QX^N_`e;^H{LlLSI**Pi5pD-8TZAbw;SDV&eCZTxw zU<7qD7WfN^QL*7mR8Jo9+!1F$bPA8uQdpmd{}A6;ZRGJGQ7FOc4n$b6-Z1riYf|sr zd?i*|Cpe#A8>!y?&g9tuUl3!IKY%2m7AN8)(3{*Bfo3`w7IN2MCUlpY-cK#8T|U<3 z=kNeroxUd+4IC{d>NHWbtCLrEarkdIoauUsvkz>1Cta)mp)(%34woH}p8j;YV%4d@ z%4~9`ouj1T(@Yz_1lh1QNv&OCC1zC>$!R9M%5cb9IG_ck8x@j$_bL#(0xo(7E$_&q z@M@_2SMT9q8S1EqFd8a$lEQa~J+>GR&YyAti6|qjMLI=2SeJeAG;Ks%%?n8GGnH6f zV^1)T{OgEjd5K;VqcIu)LoFk<1pS;bd-S|iSEW%sUG}1rEMLt|S_w+rv(D+>!kllp z3+pqig~1D=aqi<}8Oiq7($)+cvX7j>-lZCq=bgnof+v=lDBn?^>xD>f}*V3Y143gi6ynfp8KKnUADzvv|*F=!wr? zFohZ{+%CbRtNQ2M+9bELCdY=dKrSxz}X1v8LdT?|&sv!_kx2-Es7Iw53H zd!Y%N-Z6SR$B?vJcwz^WvJ1`=R@uQBlOCPo$!27k*82@`vXb9Lu1xfK^1WCwajRnk z^f74Q-fL<_O|2zc_+b*bX*OBJB_~6 zpB|*k&cS@{^M-UD&dWM%*iefHiF=RoTkJ)<1Cf3(+l|x*rDm{;12JT;%8lH|mShI| zDFgsQH9g(FC!(DHJJV^{bg$t>8&END-1WlKffJ@=Pd3%1m#G4x5J8_$KQ$--J7^~# z24k-fb+#M!;-L}s>N-%9(l6ocuyL)HL*5XAl$?>{fR>N1wrw_G?@dZ(=)hJ^#rSGO zK{v>L8`6rrJ=i_iT`_B?z;A={iPNCtc-(H2x~ek9gf zyH%{7iH4&+#9UOJx-DI=AV{owdq~XbQbysyUv3ajhRYsKDMSjrFT~S(Xx~1DOE^1A z9}SifxwENw`}5?YmR6|{MJ;)0%c$X)Fv{l(VN$n)QHo|b%GS=^F#Wt2Y6WOm z@8$>y!c$A2n|P?S!8$c%8ktvEB-JA7uN37a$^SO$zt}(X;Ah@aKb;Vwx42EZuyJAw zynHk`{_DCaRnI#C+-_8%m1wgj(gxAp>C^TcS%hs{%O340xs_litWCK=>+k{M;y6-m z6UdS@ZblYT0hZmg7I@sky?))?911x8RN*r@T*&?VhLvrqdo(W@og-iCISF)C3T0Tv)&4AySvOHY^U z2D~gxoH?aI=E7p>?`5OE@MLCp;*XE@>*DCW+nP7qDzATS5(3hZ$7rf{(qRELzLxG^ zW#rHF!w2J*C7JG?+^?C-X=(1i)4_rs|{G5PwmRQ$8iUXTRyd7b*8gaLyeB2FEC4b3(^~ zRu{}RF6qNv3OJtMGcMJ{Aw1ar~IdKleKKjzGgzGD^NbdoR<g*%e!BC{#4_w3 zHV6Wj0?4lSGc(tBvFm=w z-~!j`vWmx;2)=3vK;8S@FFGz-)qRw2KD9SC>BF7buX6lxNpn|6+{5-=0_mma;IoPB!_;kEcGN=a-8I^=`mh|+TrGE9BUdBs>0x~|KfcO4_5F*g zJXGs{@>~cG#s=2^8QL0JK(n#1C-{Ps!?CkN(@PuMm^zseFmWU>7E%F9@`GrkKoW$Cv>L<Gam~*f{Xqb;YvoB{ zK-{lWbrH=-mQE!SK~n%^z)f;iz_ruO2TJnr33tV6{EeTLDJsA&nBUCnM!H|5o2Y+- zMvLNPisNes_gh4IzI_2}+BX|9Vh{o@9w%(inUk$7oCOTvj@f| z8m#Xi%Nbm|zE|M4N^Qy&Fa{W=h*X5L*5qE!N6T91_55LHPKD(n)tgn zXpcDIcHp}tFsHSTW&Sx5zh~Mp4|KS6>pZ$5*!8Ix256mmITqE6gD+^3$9a~5!!tTH z#Unc69^rUJ^y7**JcamP(3$ZTKbv)D&aM*ZJQ(?@*99<|7wc~%`Y)E}JSi0daHF$l z{6Y9II9S6rDyDxFjV9K) z25_@)7^1gwyFWY3P~+#}^7)XhP`UQ0?Rs_desy+qo_rJIYphxEarbU;e>fg=HdTG^ z);^eKtOh)7>wa~;-Ul-$cueO2y1-sC_Fd_x^cY@l^VrsRw1b)Ls;Hf;CqQHN?JZv# zFiPZNavpE${A`X+5<%O_XFe}y_4qm6?r#VSx-Fn=H*hwK2Uo5MKJQJZw|TkFX0^uN zuPN}Mx!A+C3UWGlyzX{8#1Y^gs`Bp}QQO&C!~p=;uaEwWWr64Q9hZs|ZCaj14YihG zAHd7o%P3(jHmPZgPupiN zJLre!D*qNO(vq`GYP~@l+nBGX!`{)55?&9fmsXgKLFjk*MW2VS+1vH!NbfBxN1OJ& z4giI#4iq3{CMFx7*E6!f$7d^Wn~tk##r!Jvxjz9R1cIE`OW3_d#fg%}Nkh`L^mQ0Q zjvuGvYxm`1*S_LxLv=BT>qS22sW!^T<97JXr0M(Q4BN)!{7N+VQ5?sA+TVhTkvsPjBkH#e6obK``;nngD?M) zaoTm`&4b71_7`up4Ul`&Z?$vOgR{axi#6S<1o{eV&&ScX8kuj^Rdws)MaD}*JjJGz zQ4kg>R^)L_)-PJlmb>?|9kxSHzu(;49gre;ON0^tR0N}!WwU`E)X z9rKK0Yu*9*N>(h$mW;S~GnAB9tk`q_ z^I@@yi;%PvUg`C-JnxbY5*}?TFEwFQZeDg|It0L^!NJ&lNA4 zscOC&XR#ocwPk!In){4*X|0J4!5DR#%--@Ld6XNw16WC#k(1qM^W9WQX}Y$!a$?A- z6#I{5b&@@IMzg=VWn1#@*?RgDb%B{uUe`-t)^j$Kt?9lho9JpV;P3%TR zv1-)|-K@$$t@z67WGsnQZkr;7$;Wg)h z-9{H5ca?-T_^lOug!O>5P1FHXoGFL~s=p3@B^gD002RZ8D3zhPP^JK=Q|6O``8k(o7myS z4xR&hXI}S_p9{j>j|w+LY`ARmp#)=80;VY+V$sR7K-dd&7P|xH{O87>OZmRfPx+Du z*vZLa9vU&^?Cw9p;y;1~JXE-81ZYo05C&+tSz92h13gqQi^5C*ca{WXADM&LV`Tkc9M%K$^PumbW&r#}d9bM-kpy z9?JLIUJ1#XdGCmDjm#v$%mHedMTMMcH@SE%EOMSgQmTMrSB#v{P7uYLW!H!7&CH)C zKZb0C__u-3K(Y6_#D7eRatHu1NQMI0AJa0=e7ZJE&$cg9ulP0;uiF9O``hPoIn`g! zk9+5DzaLX1d^%r@HuVCBIlw{4+ReA=1@!OM_BcL#EqmNZacNR7jIMwGND_7Z`ULew zJ+FcCuRWBb0kZ!(NSiDw;^$usmbckMDwwOFA5&P4$l@*V6gq%u*(CJ^S`8chJo{aG z(5Yuq?G6JpqaD4I?u@`YRB?0er`0wp`F~bX->wM6^sXrvw5Nw z+)j1MSXMSzqPgkojLp>d>*tUy@AdEJfDikTCtz?mVuX2@dWmakpl1h70OU??I$%w?wDA7!XWby_nV^)R%x`(V`IeIR8v)MQii-U_CS$yU{`+jb zuT+mQpEdH7PBrsoMwWc6+ht=<-A5z^Gefm5U+DmVTwVF;*@NEx^~o_=HS)fq5#BWY znOy82b+aXiBg)*BhemDDJ3UGu#_cL9o=2-9qlW?TFbTq8qn(fT>~l|JMRmF_EI5qD z7#R(%mb&lAEjPo1q>4BqENJ^+GHN0BN`c(I{%#Yr+Kiwi_T|w{2K)7}5w*WyMBV!O zwBBeCgN)I-H~i04?ipVLM0M!@``*mX!1Vtj%aM1m{fp*oWb8mdVfWWW*;t*Hk&cCd zj){RX#o8K_BW1`MlpK_ak&z+g)*2N5e@3V_pz4@R3Ezp8?4OFBeDB($Q9aOqs0aPTRJXMF7Zd z%5?KyEW22%4KaRRA8l;MMS%u@OE$+>?zs{q!nnmdQAG97eu zbf+ZD(|$W!YK|rFQ~7Am2Ae_o0beRd8pX|skqT({wu5n|ZE~0bB%=4>{c_On)4=kP z_0h-3USQEOk?nA1Eaa#4?Ey^P_EYf$)1S?g2^t*O)T)P@+Odhe)`RY`p}EjfN@>S) zlVDKIr8!7xoE7fna}EEF$B-V!j1jQ8F%F}wOdQ?W^JWWD;|Qlju~s!@?gBf~KPzp1 zaw3vjNuR$=kMpGg)00O>^g?J?hx?H(bYmmN+{uCa`^qs5IY?K=atZTLiM%rQX}-Co zt1+N_Hnx-~-f`g%mXu3jkA@0Xn9Chp8y%mswqH-mr^4HpOSICn1%FEn>yTg!i@2JN z9{kN9tejA+P>U&~4{3y94U5>w(6B#!v-&`_qWNL_m?5VD1Um5zzXH_ilt?c2kE*p= z?89k!0&WrmPy!w(BrhS2E`FXG3jn8$mrUJWDHN)jBoI*Bq~THYZ_ZQjxAJtITRdb6 zIfeHUg(RmPURYi+8mYr6wX!eal|^~;^p0VRSaT|_`Ji2%NPRmWuh%PSZ=c268~9h{ zZ`g3ZswhK*)Kmc8C)X|S7k+z`CD7aJchoiCUagmBdL=gM(&oc9-*k>U*gNu9{AG%3 zmjAb~Von*e1tkEmGqC7D(<_;K7!&*qq3P8Kv=|AP2^fDS6>V*uekK`z;!hEpUc$!2 z_DB0qH2y~uA<*LCU=U^#Vf~@q5E5Z!WnvO!WaVIB5Mg5#WoKt#5M<@#Bl!O{f#du6 z(Eqt!GqU~ndQF-lC9R@{8vNY$3{2H0s*G1Jcnjo@Ag>^g=TA}UhAIjwf@n8Gwp1uF zkC{Nmnw42>4#8^9Wo9aqUTnIO#bGxyl$XUK1JQnJH0O8uVdQ$n?%t=+!n5EXa4PcHtGrTJWnn zKMJ=_r#&73Jmkd62cbXmQsgf zE4bY|g~;Yl)`-?< z&}UK$DA=K)%!fm2{uQGtgiT@vW^IOMRs!kjuVaQiD~sG5uxYkF3l910+i8YZm1Bp0 zBETK!;$Mq$A&xE1ExQ8NHrv?3*9Wpgu!g+G*eAThutv3}(1$k@(H!g&C_iFKYu4Qv zsF{l(^S)^Gwog9OoQq4g5P^`}ly@p;F<&wRaLLCdS%AP6;`S|GUL3{~JQqACQ_dHg zjV>^+ki5fUdDZ97gLOcc;hQmy|3@}MHp>M`-s|3I&f>iXvqQ8-w5HofX$K{)%HMG zq)Yx_UbM^nz(~9vQNncqA}(;KBW@8MaCSMWwK2YR#n8uxq$diCVQ)B)k8~|2jce~& z2uze5gL`zAV@WwiYWN!JYLxTuay*@*7oq|TO#kN>_So--DK|rt_GAAw8}PDCR7NU8 z73u&N9JO^Tgw7IM(-~g>F^Wv(lKoYQGYvBF+t%MbHua}9A{ZSA&F>PRp~&T50GG&R zAhJmI9-Nvy5re{3eH$}{ik&8kcbbfh)I^tzWJ3JPw2Z417l=t95HC2u+D1bA99saD zqoolvyRS^{Zz~|yqbr}9RZV$tXMJij(8nWPKyglKv;`(B!yIr`?&wAa|2g@jKvX+e zsdaOj=R51<6{yAJr2w2ef;{#X0Aj(5+i&e0NB~8;ZGxA|9PLq^brCOD)dO@!Ai>D@ zDG7)Dc{aquKRWFJJRpiZWG+-E@^*|U9_rL-%gH|H{u_mx3v=!U-E+l6V-q8V$ zhFtG)nfb-=#|)T+FZM;iCxHEVUy0UN5Lg2K@+kY8(wZH6!K0A-g7sOviL6$jhb0dt z_H@SlsvF~aZqkUwTaFF{FbWegQLZ~j6AhV4s2`j=kID?1mB+>Mm>MlfgZM`olZl#t z7FeAi_A__h7WQNU8-nX}#AoKU7$j-{0@vs-|H%ze_inw0Q4V@%ANP4g!l%F#$j^?U zKU68xg!c1IU?7-4upXd9MW-&2R;#5~b$+sU!BsslOJC+eZUJt_g;e;YnN}{NeAa!aeJUdA5d zC45%TXIq&EAUZ~X59G|(XXxrXHVZfOP!Cqm;ZzRTaS!MY^8PePbyIeGb`Efg0I(A$ z;iwOn-m6}f*W$%(#B!%e^H;eTv#J^H%t;&CQaNjSQy(|L+^HX0&%0_i6DPBFo@9(o<=Om1_SfXZIF1 z#?+0ce*ufA#9M59q}EFn#Lgvny@AVjxWR*Z^PBY>+cvWO8|c^2RBg(b3n(58Gbbx6 MG%2a5oEY@~16uk|DF6Tf delta 113218 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 z%0D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

J!}0Sz1#?F|lHNfb^M)U=IRIAiz; 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 diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 35f3deba78..1a9262eea9 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -48,8 +48,8 @@ retzero: %endmacro %macro extcodesize - %stack (address) -> (address, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, %%after) - %jump(load_code) + %stack (address) -> (address, %%after) + %jump(extcodesize) %%after: %endmacro @@ -76,53 +76,54 @@ global sys_extcodesize: global extcodesize: // stack: address, retdest - %extcodesize - // stack: extcodesize(address), retdest - SWAP1 JUMP + %next_context_id + // stack: codesize_ctx, address, retdest + SWAP1 + // stack: address, codesize_ctx, retdest + %jump(load_code) -// Loads the code at `address` into memory, at the given context and segment, starting at offset 0. +// Loads the code at `address` into memory, in the code segment of the given context, starting at offset 0. // Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. -// Pre stack: address, ctx, segment, retdest +// Pre stack: address, ctx, retdest // Post stack: code_size global load_code: - %stack (address, ctx, segment, retdest) -> (extcodehash, address, load_code_ctd, ctx, segment, retdest) + %stack (address, ctx, retdest) -> (extcodehash, address, load_code_ctd, ctx, retdest) JUMP load_code_ctd: - // stack: codehash, ctx, segment, retdest + // stack: codehash, ctx, retdest DUP1 ISZERO %jumpi(load_code_non_existent_account) - PROVER_INPUT(account_code::length) - // stack: code_size, codehash, ctx, segment, retdest - PUSH 0 - -// Loop non-deterministically querying `code[i]` and storing it in `SEGMENT_KERNEL_ACCOUNT_CODE` -// at offset `i`, until `i==code_size`. -load_code_loop: - // stack: i, code_size, codehash, ctx, segment, retdest - DUP2 DUP2 EQ - // stack: i == code_size, i, code_size, codehash, ctx, segment, retdest - %jumpi(load_code_check) - DUP1 - // stack: i, i, code_size, codehash, ctx, segment, retdest - DUP6 // segment - DUP6 // context - PROVER_INPUT(account_code::get) - // stack: opcode, context, segment, i, i, code_size, codehash, ctx, segment, retdest - MSTORE_GENERAL - // stack: i, code_size, codehash, ctx, segment, retdest - %increment - // stack: i+1, code_size, codehash, ctx, segment, retdest - %jump(load_code_loop) - -// Check that the hash of the loaded code equals `codehash`. -load_code_check: - // stack: i, code_size, codehash, ctx, segment, retdest - %stack (i, code_size, codehash, ctx, segment, retdest) - -> (ctx, segment, 0, code_size, codehash, retdest, code_size) + // Load the code non-deterministically in memory and return the length. + PROVER_INPUT(account_code) + %stack (code_size, codehash, ctx, retdest) -> (ctx, @SEGMENT_CODE, 0, code_size, codehash, retdest, code_size) + // Check that the hash of the loaded code equals `codehash`. KECCAK_GENERAL // stack: shouldbecodehash, codehash, retdest, code_size %assert_eq + // stack: retdest, code_size JUMP load_code_non_existent_account: - %stack (codehash, ctx, segment, retdest) -> (retdest, 0) + // Write 0 at address 0 for soundness. + // stack: codehash, ctx, retdest + %stack (codehash, ctx, retdest) -> (0, ctx, @SEGMENT_CODE, 0, retdest, 0) + MSTORE_GENERAL + // stack: retdest, 0 + JUMP + +// Identical to load_code, but adds 33 zeros after code_size for soundness reasons. +// If the code ends with an incomplete PUSH, we must make sure that every subsequent read is 0, +// accordingly to the Ethereum specs. +// Pre stack: address, ctx, retdest +// Post stack: code_size +global load_code_padded: + %stack (address, ctx, retdest) -> (address, ctx, load_code_padded_ctd, ctx, retdest) + %jump(load_code) + +load_code_padded_ctd: + %stack (code_size, ctx, retdest) -> (ctx, @SEGMENT_CODE, code_size, 0, ctx, retdest, code_size) + MSTORE_32BYTES_32 + // stack: last_offset, ctx, retdest, code_size + %stack (last_offset, ctx) -> (0, ctx, @SEGMENT_CODE, last_offset) + MSTORE_GENERAL + // stack: retdest, code_size JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index ba6c775e12..2e7d1d7345 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -352,8 +352,8 @@ call_too_deep: %endmacro %macro set_new_ctx_code - %stack (address, new_ctx) -> (address, new_ctx, @SEGMENT_CODE, %%after, new_ctx) - %jump(load_code) + %stack (address, new_ctx) -> (address, new_ctx, %%after, new_ctx) + %jump(load_code_padded) %%after: %set_new_ctx_code_size // stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index bb794b651f..df39b9d814 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -248,11 +248,10 @@ global process_message_txn: %create_context // stack: new_ctx, retdest PUSH process_message_txn_code_loaded - PUSH @SEGMENT_CODE - DUP3 // new_ctx + DUP2 // new_ctx %mload_txn_field(@TXN_FIELD_TO) - // stack: address, new_ctx, segment, process_message_txn_code_loaded, new_ctx, retdest - %jump(load_code) + // stack: address, new_ctx, process_message_txn_code_loaded, new_ctx, retdest + %jump(load_code_padded) global process_message_txn_insufficient_balance: // stack: retdest diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index 638cd1e362..9798f42474 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -197,8 +197,10 @@ global sys_extcodecopy: DUP1 %ensure_reasonable_offset %update_mem_bytes - %stack (kexit_info, address, dest_offset, offset, size) -> - (address, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, extcodecopy_contd, 0, kexit_info, dest_offset, offset, size) + %next_context_id + + %stack (ctx, kexit_info, address, dest_offset, offset, size) -> + (address, ctx, extcodecopy_contd, ctx, kexit_info, dest_offset, offset, size) %jump(load_code) sys_extcodecopy_empty: @@ -207,8 +209,8 @@ sys_extcodecopy_empty: EXIT_KERNEL extcodecopy_contd: - // stack: code_size, src_ctx, kexit_info, dest_offset, offset, size - %codecopy_after_checks(@SEGMENT_KERNEL_ACCOUNT_CODE) + // stack: code_size, ctx, kexit_info, dest_offset, offset, size + %codecopy_after_checks(@SEGMENT_CODE) // The internal logic is similar to wcopy, but handles range overflow differently. diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 6d40193f98..b2a8f0cea0 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; -use itertools::Itertools; +use itertools::{enumerate, Itertools}; use num_bigint::BigUint; use plonky2::field::types::Field; use serde::{Deserialize, Serialize}; @@ -19,6 +19,7 @@ use crate::memory::segments::Segment::BnPairing; use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; use crate::witness::errors::ProgramError; use crate::witness::errors::ProverInputError::*; +use crate::witness::memory::MemoryAddress; use crate::witness::util::{current_context_peek, stack_peek}; /// Prover input function represented as a scoped function name. @@ -42,7 +43,7 @@ impl GenerationState { "ffe" => self.run_ffe(input_fn), "rlp" => self.run_rlp(), "current_hash" => self.run_current_hash(), - "account_code" => self.run_account_code(input_fn), + "account_code" => self.run_account_code(), "bignum_modmul" => self.run_bignum_modmul(), "withdrawal" => self.run_withdrawal(), "num_bits" => self.run_num_bits(), @@ -130,35 +131,26 @@ impl GenerationState { Ok(U256::from_big_endian(&self.inputs.block_hashes.cur_hash.0)) } - /// Account code. - fn run_account_code(&mut self, input_fn: &ProverInputFn) -> Result { - match input_fn.0[1].as_str() { - "length" => { - // Return length of code. - // stack: codehash, ... - let codehash = stack_peek(self, 0)?; - Ok(self - .inputs - .contract_code - .get(&H256::from_uint(&codehash)) - .ok_or(ProgramError::ProverInputError(CodeHashNotFound))? - .len() - .into()) - } - "get" => { - // Return `code[i]`. - // stack: context, segment, i, i, code_size, codehash, ... - let i = stack_peek(self, 2).map(u256_to_usize)??; - let codehash = stack_peek(self, 5)?; - Ok(self - .inputs - .contract_code - .get(&H256::from_uint(&codehash)) - .ok_or(ProgramError::ProverInputError(CodeHashNotFound))?[i] - .into()) - } - _ => Err(ProgramError::ProverInputError(InvalidInput)), + /// Account code loading. + /// Initializes the code segment of the given context with the code corresponding + /// to the provided hash. + /// Returns the length of the code. + fn run_account_code(&mut self) -> Result { + // stack: codehash, ctx, ... + let codehash = stack_peek(self, 0)?; + let context = stack_peek(self, 1)?; + let context = u256_to_usize(context)?; + let mut address = MemoryAddress::new(context, Segment::Code, 0); + let code = self + .inputs + .contract_code + .get(&H256::from_uint(&codehash)) + .ok_or(ProgramError::ProverInputError(CodeHashNotFound))?; + for &byte in code { + self.memory.set(address, byte.into()); + address.increment(); } + Ok(code.len().into()) } // Bignum modular multiplication. From edfc86c39353e4870b36849fc36e0a70d09a832b Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Thu, 7 Dec 2023 13:07:06 -0500 Subject: [PATCH 006/175] Remove is_keccak_sponge (#1410) * Remove is_keccak_sponge * Apply comment --- evm/spec/tables/cpu.tex | 1 - evm/spec/zkevm.pdf | Bin 295898 -> 295781 bytes evm/src/cpu/columns/mod.rs | 3 --- evm/src/cpu/cpu_stark.rs | 9 ++++++++- evm/src/witness/operation.rs | 1 - 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/evm/spec/tables/cpu.tex b/evm/spec/tables/cpu.tex index 401081d37d..7bca5a9f5e 100644 --- a/evm/spec/tables/cpu.tex +++ b/evm/spec/tables/cpu.tex @@ -31,7 +31,6 @@ \subsubsection{CPU columns} \item \texttt{is\_kernel\_mode}: Boolean indicating whether we are in kernel (i.e. privileged) mode. This means we are executing kernel code, and we have access to privileged instructions. \item \texttt{gas}: The current amount of gas used in the current context. It is eventually checked to be below the current gas limit. Must fit in 32 bits. - \item \texttt{is\_keccak\_sponge}: Boolean indicating whether we are executing a Keccak hash. Only used as a filter for CTLs. \item \texttt{clock}: Monotonic counter which starts at 0 and is incremented by 1 at each row. Used to enforce correct ordering of memory accesses. \item \texttt{opcode\_bits}: 8 boolean columns, which are the bit decomposition of the opcode being read at the current PC. \end{itemize} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 1f72b69df723e87dfef470e86e6e6bd75cb7c2c8..455491be7a3eb9fd96457e2d7645e161afcc5525 100644 GIT binary patch delta 5834 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�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 K3MC~)Peuws { /// If CPU cycle: the opcode, broken up into bits in little-endian order. pub opcode_bits: [T; 8], - /// Filter. 1 iff a Keccak sponge lookup is performed on this row. - pub is_keccak_sponge: T, - /// Columns shared by various operations. pub(crate) general: CpuGeneralColumnsView, diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 1b89d5af1a..5eaa0c3fda 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -48,8 +48,15 @@ pub(crate) fn ctl_data_keccak_sponge() -> Vec> { } /// CTL filter for a call to the Keccak sponge. +// KECCAK_GENERAL is differentiated from JUMPDEST by its second bit set to 0. pub(crate) fn ctl_filter_keccak_sponge() -> Filter { - Filter::new_simple(Column::single(COL_MAP.is_keccak_sponge)) + Filter::new( + vec![( + Column::single(COL_MAP.op.jumpdest_keccak_general), + Column::linear_combination_with_constant([(COL_MAP.opcode_bits[1], -F::ONE)], F::ONE), + )], + vec![], + ) } /// Creates the vector of `Columns` corresponding to the two inputs and diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 3d09210194..848dae8532 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -129,7 +129,6 @@ pub(crate) fn generate_keccak_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - row.is_keccak_sponge = F::ONE; let [(context, _), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; let len = u256_to_usize(len)?; From 7efd147e0888c5c6754a4d7ee2691a2ff5c82072 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:48:37 -0500 Subject: [PATCH 007/175] Use mstore_32bytes to optimize decode_int_given_len (#1413) --- evm/src/cpu/kernel/asm/rlp/decode.asm | 44 ++++++++------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/evm/src/cpu/kernel/asm/rlp/decode.asm b/evm/src/cpu/kernel/asm/rlp/decode.asm index 327edcf1c5..dd990bbd37 100644 --- a/evm/src/cpu/kernel/asm/rlp/decode.asm +++ b/evm/src/cpu/kernel/asm/rlp/decode.asm @@ -131,37 +131,19 @@ decode_rlp_list_len_big: // Pre stack: pos, len, retdest // Post stack: pos', int global decode_int_given_len: - %stack (pos, len, retdest) -> (pos, len, pos, retdest) + DUP2 ISZERO %jumpi(empty_int) + %stack (pos, len, retdest) -> (pos, len, pos, len, retdest) ADD - // stack: end_pos, pos, retdest - SWAP1 - // stack: pos, end_pos, retdest - PUSH 0 // initial accumulator state - // stack: acc, pos, end_pos, retdest - -decode_int_given_len_loop: - // stack: acc, pos, end_pos, retdest - DUP3 - DUP3 - EQ - // stack: pos == end_pos, acc, pos, end_pos, retdest - %jumpi(decode_int_given_len_finish) - // stack: acc, pos, end_pos, retdest - %shl_const(8) - // stack: acc << 8, pos, end_pos, retdest - DUP2 - // stack: pos, acc << 8, pos, end_pos, retdest - %mload_kernel(@SEGMENT_RLP_RAW) - // stack: byte, acc << 8, pos, end_pos, retdest - ADD - // stack: acc', pos, end_pos, retdest - // Increment pos. - SWAP1 - %increment - SWAP1 - // stack: acc', pos', end_pos, retdest - %jump(decode_int_given_len_loop) + %stack(pos_two, pos, len, retdest) -> (pos, len, pos_two, retdest) + PUSH @SEGMENT_RLP_RAW + PUSH 0 //context + MLOAD_32BYTES + // stack: int, pos', retdest + %stack(int, pos, retdest) -> (retdest, pos, int) + JUMP -decode_int_given_len_finish: - %stack (acc, pos, end_pos, retdest) -> (retdest, pos, acc) +empty_int: + // stack: pos, len, retdest + %stack(pos, len, retdest) -> (retdest, pos, 0) JUMP + From 43ecf1dff34ab8dc0b076ee261ed29b692754e0f Mon Sep 17 00:00:00 2001 From: yanziseeker <153156292+AdventureSeeker987@users.noreply.github.com> Date: Fri, 8 Dec 2023 10:17:07 +0000 Subject: [PATCH 008/175] chore: fix some comment typos --- evm/src/cpu/kernel/tests/bn254.rs | 4 ++-- evm/src/cpu/kernel/tests/hash.rs | 2 +- evm/src/stark.rs | 2 +- starky/src/stark.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs index 5ed60e7a32..8a90ff2479 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -117,8 +117,8 @@ fn run_bn_frob_fp12(f: Fp12, n: usize) -> Fp12 { segment: BnPairing, memory: vec![(ptr, f.to_stack().to_vec())], }; - let interpeter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpeter.extract_kernel_memory(BnPairing, ptr..ptr + 12); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); Fp12::::from_stack(&output) } diff --git a/evm/src/cpu/kernel/tests/hash.rs b/evm/src/cpu/kernel/tests/hash.rs index 9b96de91be..6371f0a8a3 100644 --- a/evm/src/cpu/kernel/tests/hash.rs +++ b/evm/src/cpu/kernel/tests/hash.rs @@ -65,7 +65,7 @@ fn prepare_test( // Load the message into the kernel. let interpreter_setup = make_interpreter_setup(message, hash_fn_label, hash_input_virt); - // Run the interpeter + // Run the interpreter let result = run_interpreter_with_memory(interpreter_setup).unwrap(); Ok((expected, result.stack().to_vec())) diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 10f48eae47..51a3ec1b14 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -66,7 +66,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a vector of points from the degree `D` extension field. This is like /// `eval_ext`, except in the context of a recursive circuit. - /// Note: constraints must be added through`yeld_constr.constraint(builder, constraint)` in the + /// Note: constraints must be added through`yield_constr.constraint(builder, constraint)` in the /// same order as they are given in `eval_packed_generic`. fn eval_ext_circuit( &self, diff --git a/starky/src/stark.rs b/starky/src/stark.rs index aec37c59df..1e7b07117b 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -66,7 +66,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a vector of points from the degree `D` extension field. This is like /// `eval_ext`, except in the context of a recursive circuit. - /// Note: constraints must be added through`yeld_constr.constraint(builder, constraint)` in the + /// Note: constraints must be added through`yield_constr.constraint(builder, constraint)` in the /// same order as they are given in `eval_packed_generic`. fn eval_ext_circuit( &self, From 3195c205df35fc9bb4d1a86413a0c570e9113ce7 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Fri, 8 Dec 2023 17:57:45 -0500 Subject: [PATCH 009/175] Merge MSTORE_32BYTES and MLOAD_32BYTES columns (#1414) * Merge MSTORE_32BYTES and MLOAD_32BYTES columns * Fix circuit functions * Apply comments --- evm/src/cpu/byte_unpacking.rs | 9 +++- evm/src/cpu/columns/ops.rs | 6 +-- evm/src/cpu/contextops.rs | 3 +- evm/src/cpu/cpu_stark.rs | 18 +++++++- evm/src/cpu/decode.rs | 83 ++++++++++++++++++++++++++--------- evm/src/cpu/gas.rs | 3 +- evm/src/cpu/stack.rs | 10 +---- evm/src/witness/transition.rs | 9 ++-- 8 files changed, 94 insertions(+), 47 deletions(-) diff --git a/evm/src/cpu/byte_unpacking.rs b/evm/src/cpu/byte_unpacking.rs index be3ed59da4..aed3bc4e77 100644 --- a/evm/src/cpu/byte_unpacking.rs +++ b/evm/src/cpu/byte_unpacking.rs @@ -13,7 +13,9 @@ pub(crate) fn eval_packed( nv: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, ) { - let filter = lv.op.mstore_32bytes; + // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES + // by the 5th bit set to 0. + let filter = lv.op.m_op_32bytes * (lv.opcode_bits[5] - P::ONES); let new_offset = nv.mem_channels[0].value[0]; let virt = lv.mem_channels[2].value[0]; // Read len from opcode bits and constrain the pushed new offset. @@ -32,7 +34,10 @@ pub(crate) fn eval_ext_circuit, const D: usize>( nv: &CpuColumnsView>, yield_constr: &mut RecursiveConstraintConsumer, ) { - let filter = lv.op.mstore_32bytes; + // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES + // by the 5th bit set to 0. + let filter = + builder.mul_sub_extension(lv.op.m_op_32bytes, lv.opcode_bits[5], lv.op.m_op_32bytes); let new_offset = nv.mem_channels[0].value[0]; let virt = lv.mem_channels[2].value[0]; // Read len from opcode bits and constrain the pushed new offset. diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index 73f501d734..250c67af9b 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -34,10 +34,8 @@ pub(crate) struct OpsColumnsView { pub dup_swap: T, /// Combines GET_CONTEXT and SET_CONTEXT flags. pub context_op: T, - /// Flag for MSTORE_32BYTES. - pub mstore_32bytes: T, - /// Flag for MLOAD_32BYTES. - pub mload_32bytes: T, + /// Combines MSTORE_32BYTES and MLOAD_32BYTES. + pub m_op_32bytes: T, /// Flag for EXIT_KERNEL. pub exit_kernel: T, /// Combines MSTORE_GENERAL and MLOAD_GENERAL flags. diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 9f62ade0c7..77eb3be90e 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -30,8 +30,7 @@ const KEEPS_CONTEXT: OpsColumnsView = OpsColumnsView { push: true, dup_swap: true, context_op: false, - mstore_32bytes: true, - mload_32bytes: true, + m_op_32bytes: true, exit_kernel: true, m_op_general: true, syscall: true, diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 5eaa0c3fda..b045b7e022 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -125,8 +125,15 @@ pub(crate) fn ctl_data_byte_packing() -> Vec> { } /// CTL filter for the `MLOAD_32BYTES` operation. +/// MLOAD_32 BYTES is differentiated from MSTORE_32BYTES by its fifth bit set to 1. pub(crate) fn ctl_filter_byte_packing() -> Filter { - Filter::new_simple(Column::single(COL_MAP.op.mload_32bytes)) + Filter::new( + vec![( + Column::single(COL_MAP.op.m_op_32bytes), + Column::single(COL_MAP.opcode_bits[5]), + )], + vec![], + ) } /// Creates the vector of `Columns` corresponding to the contents of General Purpose channels when calling byte unpacking. @@ -159,8 +166,15 @@ pub(crate) fn ctl_data_byte_unpacking() -> Vec> { } /// CTL filter for the `MSTORE_32BYTES` operation. +/// MSTORE_32BYTES is differentiated from MLOAD_32BYTES by its fifth bit set to 0. pub(crate) fn ctl_filter_byte_unpacking() -> Filter { - Filter::new_simple(Column::single(COL_MAP.op.mstore_32bytes)) + Filter::new( + vec![( + Column::single(COL_MAP.op.m_op_32bytes), + Column::linear_combination_with_constant([(COL_MAP.opcode_bits[5], -F::ONE)], F::ONE), + )], + vec![], + ) } /// Creates the vector of `Columns` corresponding to the contents of the CPU registers when performing a `PUSH`. diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs index 90e49ec05c..4e6dbfbd68 100644 --- a/evm/src/cpu/decode.rs +++ b/evm/src/cpu/decode.rs @@ -23,7 +23,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP}; /// behavior. /// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to /// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid. -const OPCODES: [(u8, usize, bool, usize); 9] = [ +const OPCODES: [(u8, usize, bool, usize); 7] = [ // (start index of block, number of top bits to check (log2), kernel-only, flag column) // ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly through the Arithmetic table CTL. // ADDMOD, MULMOD and SUBMOD flags are handled partly manually here, and partly through the Arithmetic table CTL. @@ -34,12 +34,10 @@ const OPCODES: [(u8, usize, bool, usize); 9] = [ // SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL. // JUMPDEST and KECCAK_GENERAL are handled manually here. (0x49, 0, true, COL_MAP.op.prover_input), - (0x56, 1, false, COL_MAP.op.jumps), // 0x56-0x57 - (0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f - (0x80, 5, false, COL_MAP.op.dup_swap), // 0x80-0x9f - (0xc0, 5, true, COL_MAP.op.mstore_32bytes), //0xc0-0xdf - (0xf6, 1, true, COL_MAP.op.context_op), //0xf6-0xf7 - (0xf8, 0, true, COL_MAP.op.mload_32bytes), + (0x56, 1, false, COL_MAP.op.jumps), // 0x56-0x57 + (0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f + (0x80, 5, false, COL_MAP.op.dup_swap), // 0x80-0x9f + (0xf6, 1, true, COL_MAP.op.context_op), //0xf6-0xf7 (0xf9, 0, true, COL_MAP.op.exit_kernel), // MLOAD_GENERAL and MSTORE_GENERAL flags are handled manually here. ]; @@ -47,7 +45,7 @@ const OPCODES: [(u8, usize, bool, usize); 9] = [ /// List of combined opcodes requiring a special handling. /// Each index in the list corresponds to an arbitrary combination /// of opcodes defined in evm/src/cpu/columns/ops.rs. -const COMBINED_OPCODES: [usize; 9] = [ +const COMBINED_OPCODES: [usize; 10] = [ COL_MAP.op.logic_op, COL_MAP.op.fp254_op, COL_MAP.op.binary_op, @@ -57,6 +55,7 @@ const COMBINED_OPCODES: [usize; 9] = [ COL_MAP.op.jumpdest_keccak_general, COL_MAP.op.not_pop, COL_MAP.op.pc_push0, + COL_MAP.op.m_op_32bytes, ]; /// Break up an opcode (which is 8 bits long) into its eight bits. @@ -133,13 +132,18 @@ pub(crate) fn eval_packed_generic( yield_constr.constraint(lv[col] * (unavailable + opcode_mismatch)); } + let opcode_high_bits = |num_high_bits| -> P { + lv.opcode_bits + .into_iter() + .enumerate() + .rev() + .take(num_high_bits) + .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) + .sum() + }; + // Manually check lv.op.m_op_constr - let opcode: P = lv - .opcode_bits - .into_iter() - .enumerate() - .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum(); + let opcode = opcode_high_bits(8); yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_general); let m_op_constr = (opcode - P::Scalar::from_canonical_usize(0xfb_usize)) @@ -177,6 +181,32 @@ pub(crate) fn eval_packed_generic( * (opcode - P::Scalar::from_canonical_usize(0x50_usize)) * lv.op.not_pop; yield_constr.constraint(not_pop_op); + + // Manually check lv.op.m_op_32bytes. + // Both are kernel-only. + yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_32bytes); + + // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. + let opcode_high_three = opcode_high_bits(3); + let op_32bytes = (opcode_high_three - P::Scalar::from_canonical_usize(0xc0_usize)) + * (opcode - P::Scalar::from_canonical_usize(0xf8_usize)) + * lv.op.m_op_32bytes; + yield_constr.constraint(op_32bytes); +} + +fn opcode_high_bits_circuit, const D: usize>( + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + lv: &CpuColumnsView>, + num_high_bits: usize, +) -> ExtensionTarget { + lv.opcode_bits + .into_iter() + .enumerate() + .rev() + .take(num_high_bits) + .fold(builder.zero_extension(), |cumul, (i, bit)| { + builder.mul_const_add_extension(F::from_canonical_usize(1 << i), bit, cumul) + }) } /// Circuit version of `eval_packed_generic`. @@ -259,13 +289,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( } // Manually check lv.op.m_op_constr - let opcode = lv - .opcode_bits - .into_iter() - .rev() - .fold(builder.zero_extension(), |cumul, bit| { - builder.mul_const_add_extension(F::TWO, cumul, bit) - }); + let opcode = opcode_high_bits_circuit(builder, lv, 8); let mload_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfb_usize)); let mstore_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfc_usize)); @@ -332,4 +356,21 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let mut not_pop_constr = builder.mul_extension(not_constr, pop_constr); not_pop_constr = builder.mul_extension(lv.op.not_pop, not_pop_constr); yield_constr.constraint(builder, not_pop_constr); + + // Manually check lv.op.m_op_32bytes. + // Both are kernel-only. + let constr = builder.mul_extension(is_not_kernel_mode, lv.op.m_op_32bytes); + yield_constr.constraint(builder, constr); + + // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. + let opcode_high_three = opcode_high_bits_circuit(builder, lv, 3); + let mstore_32bytes_opcode = + builder.constant_extension(F::Extension::from_canonical_usize(0xc0_usize)); + let mload_32bytes_opcode = + builder.constant_extension(F::Extension::from_canonical_usize(0xf8_usize)); + let mstore_32bytes_constr = builder.sub_extension(opcode_high_three, mstore_32bytes_opcode); + let mload_32bytes_constr = builder.sub_extension(opcode, mload_32bytes_opcode); + let constr = builder.mul_extension(mstore_32bytes_constr, mload_32bytes_constr); + let constr = builder.mul_extension(constr, lv.op.m_op_32bytes); + yield_constr.constraint(builder, constr); } diff --git a/evm/src/cpu/gas.rs b/evm/src/cpu/gas.rs index 33ef9364f2..af86fb4766 100644 --- a/evm/src/cpu/gas.rs +++ b/evm/src/cpu/gas.rs @@ -33,8 +33,7 @@ const SIMPLE_OPCODES: OpsColumnsView> = OpsColumnsView { push: G_VERYLOW, dup_swap: G_VERYLOW, context_op: KERNEL_ONLY_INSTR, - mstore_32bytes: KERNEL_ONLY_INSTR, - mload_32bytes: KERNEL_ONLY_INSTR, + m_op_32bytes: KERNEL_ONLY_INSTR, exit_kernel: None, m_op_general: KERNEL_ONLY_INSTR, syscall: None, diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 62c0dd309d..b834482eb4 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -34,8 +34,7 @@ pub(crate) const MIGHT_OVERFLOW: OpsColumnsView = OpsColumnsView { push: true, dup_swap: true, context_op: false, - mload_32bytes: false, - mstore_32bytes: false, + m_op_32bytes: false, exit_kernel: true, // Doesn't directly push, but the syscall it's returning from might. m_op_general: false, syscall: false, @@ -138,12 +137,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol }), dup_swap: None, context_op: None, - mload_32bytes: Some(StackBehavior { - num_pops: 4, - pushes: true, - disable_other_channels: false, - }), - mstore_32bytes: Some(StackBehavior { + m_op_32bytes: Some(StackBehavior { num_pops: 4, pushes: true, disable_other_channels: false, diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 6cdc5f6a67..6f720a3b2e 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -179,8 +179,7 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { Operation::Jump | Operation::Jumpi => &mut flags.jumps, Operation::Pc | Operation::Push(0) => &mut flags.pc_push0, Operation::GetContext | Operation::SetContext => &mut flags.context_op, - Operation::Mload32Bytes => &mut flags.mload_32bytes, - Operation::Mstore32Bytes(_) => &mut flags.mstore_32bytes, + Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => &mut flags.m_op_32bytes, Operation::ExitKernel => &mut flags.exit_kernel, Operation::MloadGeneral | Operation::MstoreGeneral => &mut flags.m_op_general, } = F::ONE; @@ -211,8 +210,7 @@ const fn get_op_special_length(op: Operation) -> Option { Operation::Jump => JUMP_OP, Operation::Jumpi => JUMPI_OP, Operation::GetContext | Operation::SetContext => None, - Operation::Mload32Bytes => STACK_BEHAVIORS.mload_32bytes, - Operation::Mstore32Bytes(_) => STACK_BEHAVIORS.mstore_32bytes, + Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => STACK_BEHAVIORS.m_op_32bytes, Operation::ExitKernel => STACK_BEHAVIORS.exit_kernel, Operation::MloadGeneral | Operation::MstoreGeneral => STACK_BEHAVIORS.m_op_general, }; @@ -251,8 +249,7 @@ const fn might_overflow_op(op: Operation) -> bool { Operation::Jump | Operation::Jumpi => MIGHT_OVERFLOW.jumps, Operation::Pc | Operation::Push(0) => MIGHT_OVERFLOW.pc_push0, Operation::GetContext | Operation::SetContext => MIGHT_OVERFLOW.context_op, - Operation::Mload32Bytes => MIGHT_OVERFLOW.mload_32bytes, - Operation::Mstore32Bytes(_) => MIGHT_OVERFLOW.mstore_32bytes, + Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => MIGHT_OVERFLOW.m_op_32bytes, Operation::ExitKernel => MIGHT_OVERFLOW.exit_kernel, Operation::MloadGeneral | Operation::MstoreGeneral => MIGHT_OVERFLOW.m_op_general, } From 5607faf36b15a81507514bdb367e9501ed886495 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:33:10 -0500 Subject: [PATCH 010/175] Check that limbs after the length are 0 (#1419) * Check that limbs after the length are 0 * Update comments * Update comments --- evm/src/byte_packing/byte_packing_stark.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index 1e1a667dc5..c3cfea71e5 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -312,6 +312,14 @@ impl, const D: usize> Stark for BytePackingSt // Only padding rows have their filter turned off. let next_filter = next_values[LEN_INDICES_COLS].iter().copied().sum::

(); yield_constr.constraint_transition(next_filter * (next_filter - current_filter)); + + // Check that all limbs after final length are 0. + for i in 0..NUM_BYTES - 1 { + // If the length is i+1, then value_bytes(i+1),...,value_bytes(NUM_BYTES-1) must be 0. + for j in i + 1..NUM_BYTES { + yield_constr.constraint(local_values[index_len(i)] * local_values[value_bytes(j)]); + } + } } fn eval_ext_circuit( @@ -367,6 +375,16 @@ impl, const D: usize> Stark for BytePackingSt let constraint = builder.sub_extension(next_filter, current_filter); let constraint = builder.mul_extension(next_filter, constraint); yield_constr.constraint_transition(builder, constraint); + + // Check that all limbs after final length are 0. + for i in 0..NUM_BYTES - 1 { + // If the length is i+1, then value_bytes(i+1),...,value_bytes(NUM_BYTES-1) must be 0. + for j in i + 1..NUM_BYTES { + let constr = + builder.mul_extension(local_values[index_len(i)], local_values[value_bytes(j)]); + yield_constr.constraint(builder, constr); + } + } } fn constraint_degree(&self) -> usize { From bfcfcdb498d253ff33121dba03a130be89fcc96c Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sat, 9 Dec 2023 06:26:55 +0100 Subject: [PATCH 011/175] Add `Checkpoint` heights (#1418) --- evm/src/fixed_recursive_verifier.rs | 143 +++++++++++++++++++++------- evm/src/generation/mod.rs | 9 +- evm/src/get_challenges.rs | 4 +- evm/src/proof.rs | 32 +++---- evm/src/recursive_verifier.rs | 8 +- evm/tests/add11_yml.rs | 2 +- evm/tests/basic_smart_contract.rs | 2 +- evm/tests/empty_txn_list.rs | 2 +- evm/tests/erc20.rs | 2 +- evm/tests/log_opcode.rs | 106 +++++++++++++++++---- evm/tests/self_balance_gas_cost.rs | 2 +- evm/tests/selfdestruct.rs | 2 +- evm/tests/simple_transfer.rs | 2 +- evm/tests/withdrawals.rs | 2 +- 14 files changed, 233 insertions(+), 85 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 53de19f805..87f076c1b5 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -37,7 +37,7 @@ use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; use crate::proof::{ BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, - PublicValuesTarget, StarkProofWithMetadata, TrieRootsTarget, + PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, }; use crate::prover::prove; use crate::recursive_verifier::{ @@ -715,18 +715,18 @@ where lhs: &ExtraBlockDataTarget, rhs: &ExtraBlockDataTarget, ) { - // Connect genesis state root values. + // Connect checkpoint state root values. for (&limb0, &limb1) in pvs - .genesis_state_trie_root + .checkpoint_state_trie_root .iter() - .zip(&rhs.genesis_state_trie_root) + .zip(&rhs.checkpoint_state_trie_root) { builder.connect(limb0, limb1); } for (&limb0, &limb1) in pvs - .genesis_state_trie_root + .checkpoint_state_trie_root .iter() - .zip(&lhs.genesis_state_trie_root) + .zip(&lhs.checkpoint_state_trie_root) { builder.connect(limb0, limb1); } @@ -790,6 +790,34 @@ where let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); + // Connect block `trie_roots_before` with parent_pv `trie_roots_before`. + TrieRootsTarget::connect( + &mut builder, + public_values.trie_roots_before, + parent_pv.trie_roots_before, + ); + // Connect the rest of block `public_values` with agg_pv. + TrieRootsTarget::connect( + &mut builder, + public_values.trie_roots_after, + agg_pv.trie_roots_after, + ); + BlockMetadataTarget::connect( + &mut builder, + public_values.block_metadata, + agg_pv.block_metadata, + ); + BlockHashesTarget::connect( + &mut builder, + public_values.block_hashes, + agg_pv.block_hashes, + ); + ExtraBlockDataTarget::connect( + &mut builder, + public_values.extra_block_data, + agg_pv.extra_block_data, + ); + // Make connections between block proofs, and check initial and final block values. Self::connect_block_proof(&mut builder, has_parent_block, &parent_pv, &agg_pv); @@ -855,12 +883,12 @@ where builder.connect(limb0, limb1); } - // Between blocks, the genesis state trie remains unchanged. + // Between blocks, the checkpoint state trie remains unchanged. for (&limb0, limb1) in lhs .extra_block_data - .genesis_state_trie_root + .checkpoint_state_trie_root .iter() - .zip(rhs.extra_block_data.genesis_state_trie_root) + .zip(rhs.extra_block_data.checkpoint_state_trie_root) { builder.connect(limb0, limb1); } @@ -878,15 +906,11 @@ where let has_not_parent_block = builder.sub(one, has_parent_block.target); - // Check that the genesis block number is 0. - let gen_block_constr = builder.mul(has_not_parent_block, lhs.block_metadata.block_number); - builder.assert_zero(gen_block_constr); - - // Check that the genesis block has the predetermined state trie root in `ExtraBlockData`. - Self::connect_genesis_block(builder, rhs, has_not_parent_block); + // Check that the checkpoint block has the predetermined state trie root in `ExtraBlockData`. + Self::connect_checkpoint_block(builder, rhs, has_not_parent_block); } - fn connect_genesis_block( + fn connect_checkpoint_block( builder: &mut CircuitBuilder, x: &PublicValuesTarget, has_not_parent_block: Target, @@ -897,7 +921,7 @@ where .trie_roots_before .state_root .iter() - .zip(x.extra_block_data.genesis_state_trie_root) + .zip(x.extra_block_data.checkpoint_state_trie_root) { let mut constr = builder.sub(limb0, limb1); constr = builder.mul(has_not_parent_block, constr); @@ -1026,7 +1050,9 @@ where trie_roots_before: lhs_public_values.trie_roots_before, trie_roots_after: rhs_public_values.trie_roots_after, extra_block_data: ExtraBlockData { - genesis_state_trie_root: lhs_public_values.extra_block_data.genesis_state_trie_root, + checkpoint_state_trie_root: lhs_public_values + .extra_block_data + .checkpoint_state_trie_root, txn_number_before: lhs_public_values.extra_block_data.txn_number_before, txn_number_after: rhs_public_values.extra_block_data.txn_number_after, gas_used_before: lhs_public_values.extra_block_data.gas_used_before, @@ -1077,42 +1103,66 @@ where block_inputs .set_proof_with_pis_target(&self.block.parent_block_proof, parent_block_proof); } else { - // Initialize genesis_state_trie, state_root_after, and the previous block hashes for correct connection between blocks. - // Block number does not need to be initialized as genesis block is constrained to have number 0. - if public_values.trie_roots_before.state_root - != public_values.extra_block_data.genesis_state_trie_root + != public_values.extra_block_data.checkpoint_state_trie_root { return Err(anyhow::Error::msg(format!( - "Inconsistent pre-state for first block {:?} with genesis state {:?}.", + "Inconsistent pre-state for first block {:?} with checkpoint state {:?}.", public_values.trie_roots_before.state_root, - public_values.extra_block_data.genesis_state_trie_root, + public_values.extra_block_data.checkpoint_state_trie_root, ))); } - // Initialize `state_root_after`. + + // Initialize some public inputs for correct connection between the checkpoint block and the current one. + let mut nonzero_pis = HashMap::new(); + + // Initialize the checkpoint block roots before, and state root after. + let state_trie_root_before_keys = 0..TrieRootsTarget::HASH_SIZE; + for (key, &value) in state_trie_root_before_keys + .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) + { + nonzero_pis.insert(key, value); + } + let txn_trie_root_before_keys = + TrieRootsTarget::HASH_SIZE..TrieRootsTarget::HASH_SIZE * 2; + for (key, &value) in txn_trie_root_before_keys.clone().zip_eq(&h256_limbs::( + public_values.trie_roots_before.transactions_root, + )) { + nonzero_pis.insert(key, value); + } + let receipts_trie_root_before_keys = + TrieRootsTarget::HASH_SIZE * 2..TrieRootsTarget::HASH_SIZE * 3; + for (key, &value) in receipts_trie_root_before_keys + .clone() + .zip_eq(&h256_limbs::( + public_values.trie_roots_before.receipts_root, + )) + { + nonzero_pis.insert(key, value); + } let state_trie_root_after_keys = TrieRootsTarget::SIZE..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE; - let mut nonzero_pis = HashMap::new(); for (key, &value) in state_trie_root_after_keys .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) { nonzero_pis.insert(key, value); } - // Initialize the genesis state trie digest. - let genesis_state_trie_keys = + // Initialize the checkpoint state root extra data. + let checkpoint_state_trie_keys = TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE + 8; - for (key, &value) in genesis_state_trie_keys.zip_eq(&h256_limbs::( - public_values.extra_block_data.genesis_state_trie_root, + for (key, &value) in checkpoint_state_trie_keys.zip_eq(&h256_limbs::( + public_values.extra_block_data.checkpoint_state_trie_root, )) { nonzero_pis.insert(key, value); } - // Initialize block hashes. + // Initialize checkpoint block hashes. + // These will be all zeros the initial genesis checkpoint. let block_hashes_keys = TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - 8; @@ -1130,6 +1180,14 @@ where nonzero_pis.insert(block_hashes_current_start + i, cur_targets[i]); } + // Initialize the checkpoint block number. + // Subtraction would result in an invalid proof for genesis, but we shouldn't try proving this block anyway. + let block_number_key = TrieRootsTarget::SIZE * 2 + 6; + nonzero_pis.insert( + block_number_key, + F::from_canonical_u64(public_values.block_metadata.block_number.low_u64() - 1), + ); + block_inputs.set_proof_with_pis_target( &self.block.parent_block_proof, &cyclic_base_proof( @@ -1145,13 +1203,26 @@ where block_inputs .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); - set_public_value_targets(&mut block_inputs, &self.block.public_values, &public_values) - .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; + // This is basically identical to this block public values, apart from the `trie_roots_before` + // that may come from the previous proof, if any. + let block_public_values = PublicValues { + trie_roots_before: opt_parent_block_proof + .map(|p| TrieRoots::from_public_inputs(&p.public_inputs[0..TrieRootsTarget::SIZE])) + .unwrap_or(public_values.trie_roots_before), + ..public_values + }; + + set_public_value_targets( + &mut block_inputs, + &self.block.public_values, + &block_public_values, + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let block_proof = self.block.circuit.prove(block_inputs)?; - Ok((block_proof, public_values)) + Ok((block_proof, block_public_values)) } pub fn verify_block(&self, block_proof: &ProofWithPublicInputs) -> anyhow::Result<()> { diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index db4a40476b..77b6fd36d7 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -50,8 +50,11 @@ pub struct GenerationInputs { pub tries: TrieInputs, /// Expected trie roots after the transactions are executed. pub trie_roots_after: TrieRoots, - /// State trie root of the genesis block. - pub genesis_state_trie_root: H256, + + /// State trie root of the checkpoint block. + /// This could always be the genesis block of the chain, but it allows a prover to continue proving blocks + /// from certain checkpoint heights without requiring proofs for blocks past this checkpoint. + pub checkpoint_state_trie_root: H256, /// Mapping between smart contract code hashes and the contract byte code. /// All account smart contracts that are invoked will have an entry present. @@ -218,7 +221,7 @@ pub fn generate_traces, const D: usize>( let trie_root_ptrs = state.trie_root_ptrs; let extra_block_data = ExtraBlockData { - genesis_state_trie_root: inputs.genesis_state_trie_root, + checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, txn_number_before: inputs.txn_number_before, txn_number_after, gas_used_before: inputs.gas_used_before, diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index ae23679900..756b0650da 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -104,7 +104,7 @@ fn observe_extra_block_data< challenger: &mut Challenger, extra_data: &ExtraBlockData, ) -> Result<(), ProgramError> { - challenger.observe_elements(&h256_limbs(extra_data.genesis_state_trie_root)); + challenger.observe_elements(&h256_limbs(extra_data.checkpoint_state_trie_root)); challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?); challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?); challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?); @@ -123,7 +123,7 @@ fn observe_extra_block_data_target< ) where C::Hasher: AlgebraicHasher, { - challenger.observe_elements(&extra_data.genesis_state_trie_root); + challenger.observe_elements(&extra_data.checkpoint_state_trie_root); challenger.observe_element(extra_data.txn_number_before); challenger.observe_element(extra_data.txn_number_after); challenger.observe_element(extra_data.gas_used_before); diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 6901f2c876..a5bf57566e 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -232,8 +232,8 @@ impl BlockMetadata { /// unlike `BlockMetadata`. #[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct ExtraBlockData { - /// The state trie digest of the genesis block. - pub genesis_state_trie_root: H256, + /// The state trie digest of the checkpoint block. + pub checkpoint_state_trie_root: H256, /// The transaction count prior execution of the local state transition, starting /// at 0 for the initial transaction of a block. pub txn_number_before: U256, @@ -251,14 +251,14 @@ impl ExtraBlockData { pub fn from_public_inputs(pis: &[F]) -> Self { assert!(pis.len() == ExtraBlockDataTarget::SIZE); - let genesis_state_trie_root = get_h256(&pis[0..8]); + let checkpoint_state_trie_root = get_h256(&pis[0..8]); let txn_number_before = pis[8].to_canonical_u64().into(); let txn_number_after = pis[9].to_canonical_u64().into(); let gas_used_before = pis[10].to_canonical_u64().into(); let gas_used_after = pis[11].to_canonical_u64().into(); Self { - genesis_state_trie_root, + checkpoint_state_trie_root, txn_number_before, txn_number_after, gas_used_before, @@ -338,13 +338,13 @@ impl PublicValuesTarget { buffer.write_target_array(&cur_hash)?; let ExtraBlockDataTarget { - genesis_state_trie_root: genesis_state_root, + checkpoint_state_trie_root, txn_number_before, txn_number_after, gas_used_before, gas_used_after, } = self.extra_block_data; - buffer.write_target_array(&genesis_state_root)?; + buffer.write_target_array(&checkpoint_state_trie_root)?; buffer.write_target(txn_number_before)?; buffer.write_target(txn_number_after)?; buffer.write_target(gas_used_before)?; @@ -386,7 +386,7 @@ impl PublicValuesTarget { }; let extra_block_data = ExtraBlockDataTarget { - genesis_state_trie_root: buffer.read_target_array()?, + checkpoint_state_trie_root: buffer.read_target_array()?, txn_number_before: buffer.read_target()?, txn_number_after: buffer.read_target()?, gas_used_before: buffer.read_target()?, @@ -740,8 +740,8 @@ impl BlockHashesTarget { /// unlike `BlockMetadata`. #[derive(Eq, PartialEq, Debug, Copy, Clone)] pub(crate) struct ExtraBlockDataTarget { - /// `Target`s for the state trie digest of the genesis block. - pub genesis_state_trie_root: [Target; 8], + /// `Target`s for the state trie digest of the checkpoint block. + pub checkpoint_state_trie_root: [Target; 8], /// `Target` for the transaction count prior execution of the local state transition, starting /// at 0 for the initial trnasaction of a block. pub txn_number_before: Target, @@ -762,14 +762,14 @@ impl ExtraBlockDataTarget { /// Extracts the extra block data `Target`s from the public input `Target`s. /// The provided `pis` should start with the extra vblock data. pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - let genesis_state_trie_root = pis[0..8].try_into().unwrap(); + let checkpoint_state_trie_root = pis[0..8].try_into().unwrap(); let txn_number_before = pis[8]; let txn_number_after = pis[9]; let gas_used_before = pis[10]; let gas_used_after = pis[11]; Self { - genesis_state_trie_root, + checkpoint_state_trie_root, txn_number_before, txn_number_after, gas_used_before, @@ -786,11 +786,11 @@ impl ExtraBlockDataTarget { ed1: Self, ) -> Self { Self { - genesis_state_trie_root: core::array::from_fn(|i| { + checkpoint_state_trie_root: core::array::from_fn(|i| { builder.select( condition, - ed0.genesis_state_trie_root[i], - ed1.genesis_state_trie_root[i], + ed0.checkpoint_state_trie_root[i], + ed1.checkpoint_state_trie_root[i], ) }), txn_number_before: builder.select( @@ -812,8 +812,8 @@ impl ExtraBlockDataTarget { ) { for i in 0..8 { builder.connect( - ed0.genesis_state_trie_root[i], - ed1.genesis_state_trie_root[i], + ed0.checkpoint_state_trie_root[i], + ed1.checkpoint_state_trie_root[i], ); } builder.connect(ed0.txn_number_before, ed1.txn_number_before); diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 9732ba639a..3103dd49b9 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -712,13 +712,13 @@ pub(crate) fn add_virtual_block_hashes, const D: us pub(crate) fn add_virtual_extra_block_data, const D: usize>( builder: &mut CircuitBuilder, ) -> ExtraBlockDataTarget { - let genesis_state_trie_root = builder.add_virtual_public_input_arr(); + let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); let txn_number_before = builder.add_virtual_public_input(); let txn_number_after = builder.add_virtual_public_input(); let gas_used_before = builder.add_virtual_public_input(); let gas_used_after = builder.add_virtual_public_input(); ExtraBlockDataTarget { - genesis_state_trie_root, + checkpoint_state_trie_root, txn_number_before, txn_number_after, gas_used_before, @@ -979,8 +979,8 @@ where W: Witness, { witness.set_target_arr( - &ed_target.genesis_state_trie_root, - &h256_limbs::(ed.genesis_state_trie_root), + &ed_target.checkpoint_state_trie_root, + &h256_limbs::(ed.checkpoint_state_trie_root), ); witness.set_target( ed_target.txn_number_before, diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index ab08fd0295..040750fa58 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -157,7 +157,7 @@ fn add11_yml() -> anyhow::Result<()> { trie_roots_after, contract_code, block_metadata, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: 0xa868u64.into(), diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index c520793f15..28db58ed54 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -188,7 +188,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 17e9b7bfd0..684a8f3625 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -63,7 +63,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { }, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/erc20.rs b/evm/tests/erc20.rs index a71049f74a..9c4bfa8353 100644 --- a/evm/tests/erc20.rs +++ b/evm/tests/erc20.rs @@ -164,7 +164,7 @@ fn test_erc20() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index bc50c4cd9d..b00fe36f9e 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -221,7 +221,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), @@ -324,7 +324,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { to_second_nibbles, rlp::encode(&to_account_second_before).to_vec(), ); - let genesis_state_trie_root = state_trie_before.hash(); + let checkpoint_state_trie_root = state_trie_before.hash(); let tries_before = TrieInputs { state_trie: state_trie_before, @@ -335,7 +335,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { let txn = hex!("f85f800a82520894095e7baea6a6c7c4c2dfeb977efac326af552d870a8026a0122f370ed4023a6c253350c6bfb87d7d7eb2cd86447befee99e0a26b70baec20a07100ab1b3977f2b4571202b9f4b68850858caf5469222794600b5ce1cfb348ad"); - let block_metadata = BlockMetadata { + let block_1_metadata = BlockMetadata { block_beneficiary: Address::from(beneficiary), block_timestamp: 0x03e8.into(), block_number: 1.into(), @@ -420,27 +420,31 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { receipts_root: receipts_trie.clone().hash(), }; + let block_1_hash = + H256::from_str("0x0101010101010101010101010101010101010101010101010101010101010101")?; + let mut block_hashes = vec![H256::default(); 256]; + let inputs_first = GenerationInputs { signed_txn: Some(txn.to_vec()), withdrawals: vec![], tries: tries_before, trie_roots_after: tries_after, contract_code, - genesis_state_trie_root, - block_metadata: block_metadata.clone(), + checkpoint_state_trie_root, + block_metadata: block_1_metadata.clone(), txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: 21000u64.into(), block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), + prev_hashes: block_hashes.clone(), + cur_hash: block_1_hash, }, }; // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 14..16, 16..18, 14..15, 9..10, 12..13, 19..20], + &[16..17, 13..16, 15..18, 14..15, 9..10, 12..13, 17..20], &config, ); @@ -539,8 +543,10 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { transactions_trie.insert(Nibbles::from_str("0x01").unwrap(), txn_2.to_vec()); + let block_1_state_root = expected_state_trie_after.hash(); + let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: block_1_state_root, transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -549,16 +555,16 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { signed_txn: Some(txn_2.to_vec()), withdrawals: vec![], tries: tries_before, - trie_roots_after, + trie_roots_after: trie_roots_after.clone(), contract_code, - genesis_state_trie_root, - block_metadata, + checkpoint_state_trie_root, + block_metadata: block_1_metadata, txn_number_before: 1.into(), gas_used_before: gas_used_second, gas_used_after: receipt.cum_gas_used, block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), + prev_hashes: block_hashes.clone(), + cur_hash: block_1_hash, }, }; @@ -578,9 +584,77 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { public_values_second, )?; all_circuits.verify_aggregation(&agg_proof)?; - let (block_proof, _block_public_values) = + let (first_block_proof, _block_public_values) = all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?; - all_circuits.verify_block(&block_proof) + all_circuits.verify_block(&first_block_proof)?; + + // Prove the next, empty block. + + let block_2_hash = + H256::from_str("0x0123456789101112131415161718192021222324252627282930313233343536")?; + block_hashes[255] = block_1_hash; + + let block_2_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 2.into(), + block_difficulty: 0x020000.into(), + block_gaslimit: 0x445566u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + ..Default::default() + }; + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + + let inputs = GenerationInputs { + signed_txn: None, + withdrawals: vec![], + tries: TrieInputs { + state_trie: expected_state_trie_after, + transactions_trie: Node::Empty.into(), + receipts_trie: Node::Empty.into(), + storage_tries: vec![], + }, + trie_roots_after: TrieRoots { + state_root: trie_roots_after.state_root, + transactions_root: HashedPartialTrie::from(Node::Empty).hash(), + receipts_root: HashedPartialTrie::from(Node::Empty).hash(), + }, + contract_code, + checkpoint_state_trie_root: block_1_state_root, // We use block 1 as new checkpoint. + block_metadata: block_2_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0.into(), + block_hashes: BlockHashes { + prev_hashes: block_hashes, + cur_hash: block_2_hash, + }, + }; + + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + all_circuits.verify_root(root_proof.clone())?; + + // We can just duplicate the initial proof as the state didn't change. + let (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( + false, + &root_proof, + public_values.clone(), + false, + &root_proof, + public_values, + )?; + all_circuits.verify_aggregation(&agg_proof)?; + + let (second_block_proof, _block_public_values) = all_circuits.prove_block( + None, // We don't specify a previous proof, considering block 1 as the new checkpoint. + &agg_proof, + updated_agg_public_values, + )?; + all_circuits.verify_block(&second_block_proof) } /// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000 diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index 2ddb01030e..7346dc2487 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -175,7 +175,7 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/selfdestruct.rs b/evm/tests/selfdestruct.rs index 03879de12f..fb33b18fb2 100644 --- a/evm/tests/selfdestruct.rs +++ b/evm/tests/selfdestruct.rs @@ -127,7 +127,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 7ef1d61a7e..ede18bf809 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -143,7 +143,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { tries: tries_before, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm/tests/withdrawals.rs b/evm/tests/withdrawals.rs index 50d490f6e6..29c958170a 100644 --- a/evm/tests/withdrawals.rs +++ b/evm/tests/withdrawals.rs @@ -73,7 +73,7 @@ fn test_withdrawals() -> anyhow::Result<()> { }, trie_roots_after, contract_code, - genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), From 942e43abc019e7eb8a3dd31f0b0195a9d78017e3 Mon Sep 17 00:00:00 2001 From: Pioua <136521243+dzizazda@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:49:04 +0000 Subject: [PATCH 012/175] typo fix --- evm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/README.md b/evm/README.md index b15bd776aa..5293f8ba8a 100644 --- a/evm/README.md +++ b/evm/README.md @@ -18,7 +18,7 @@ Audits for the ZK-EVM will begin on November 27th, 2023. See the [Audit RC1 Mile ## Documentation / Specification -The current specification is located in the [/spec](/spec) directory, with the most currently up-to-date PDF [availabe here](https://github.com/0xPolygonZero/plonky2/blob/main/evm/spec/zkevm.pdf). Further documentation will be made over the coming months. +The current specification is located in the [/spec](/spec) directory, with the most currently up-to-date PDF [available here](https://github.com/0xPolygonZero/plonky2/blob/main/evm/spec/zkevm.pdf). Further documentation will be made over the coming months. --- Copyright (C) 2023 PT Services DMCC From 724437d0537cfb3531b5608f41ac06dd7e33750c Mon Sep 17 00:00:00 2001 From: Pioua <136521243+dzizazda@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:53:57 +0000 Subject: [PATCH 013/175] typo fix --- evm/spec/cpulogic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex index ff38096645..0f62b9fe67 100644 --- a/evm/spec/cpulogic.tex +++ b/evm/spec/cpulogic.tex @@ -98,7 +98,7 @@ \subsection{Privileged instructions} empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. - The popped element, \textit{kexit\_info}, contains several informations like the current program counter, current gas used, and if we are in kernel (i.e. privileged) mode. + The popped element, \textit{kexit\_info}, contains several information like the current program counter, current gas used, and if we are in kernel (i.e. privileged) mode. \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 3 elements (successively the context, segment, and offset portions of a Memory address), and pushes the value stored at this memory address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. From 00ed16fc8e174edfe016fa589e2b900c11c08193 Mon Sep 17 00:00:00 2001 From: Pioua <136521243+dzizazda@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:54:20 +0000 Subject: [PATCH 014/175] minor typo fix --- evm/spec/mpts.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/spec/mpts.tex b/evm/spec/mpts.tex index bb0547cd80..91da59ce5c 100644 --- a/evm/spec/mpts.tex +++ b/evm/spec/mpts.tex @@ -59,7 +59,7 @@ \subsection{Prover input format} \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest. \end{enumerate} Nodes are thus given in depth-first order, enabling natural recursive methods for encoding and decoding this format. -The payload of state and receipt tries is given in the natural sequential way. The transaction an receipt payloads contain variable size data, thus the input is slightly different. The prover input for for the transactions is the transaction RLP encoding preceeded by its lenght. For the receipts is in the natural sequential way, except that topics and data are preceeded by their lengths, respectively. +The payload of state and receipt tries is given in the natural sequential way. The transaction an receipt payloads contain variable size data, thus the input is slightly different. The prover input for for the transactions is the transaction RLP encoding preceeded by its length. For the receipts is in the natural sequential way, except that topics and data are preceeded by their lengths, respectively. \subsection{Encoding and Hashing} From 4e4e61c2a88e37856ed317ea964ef976dd36f152 Mon Sep 17 00:00:00 2001 From: Pioua <136521243+dzizazda@users.noreply.github.com> Date: Tue, 12 Dec 2023 05:16:12 +0000 Subject: [PATCH 015/175] typo fix --- evm/spec/cpulogic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex index 0f62b9fe67..152cde1a2d 100644 --- a/evm/spec/cpulogic.tex +++ b/evm/spec/cpulogic.tex @@ -98,7 +98,7 @@ \subsection{Privileged instructions} empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. - The popped element, \textit{kexit\_info}, contains several information like the current program counter, current gas used, and if we are in kernel (i.e. privileged) mode. + The popped element, \textit{kexit\_info}, contains several information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 3 elements (successively the context, segment, and offset portions of a Memory address), and pushes the value stored at this memory address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. From 837434cf8134be9f49cc4b6158719f4e849d16a2 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 12 Dec 2023 07:03:29 +0000 Subject: [PATCH 016/175] Fix a minor typo in evm/spec/cpulogic.tex --- evm/spec/cpulogic.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex index 152cde1a2d..df866daf1a 100644 --- a/evm/spec/cpulogic.tex +++ b/evm/spec/cpulogic.tex @@ -98,7 +98,7 @@ \subsection{Privileged instructions} empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. - The popped element, \textit{kexit\_info}, contains several information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. + The popped element, \textit{kexit\_info}, contains several pieces of information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 3 elements (successively the context, segment, and offset portions of a Memory address), and pushes the value stored at this memory address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. From bc1a3c48512e57d882d393cd545a22838e2a8edf Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:21:24 +0100 Subject: [PATCH 017/175] Merge `push` and `prover_input` flags (#1417) * Merge PUSH and PROVER_INPUT flags * Apply comment --- evm/src/cpu/columns/ops.rs | 6 ++---- evm/src/cpu/contextops.rs | 3 +-- evm/src/cpu/control_flow.rs | 22 ++++++++++++++++++-- evm/src/cpu/cpu_stark.rs | 30 ++++++++++++++++++--------- evm/src/cpu/decode.rs | 38 +++++++++++++++++++++++++++++++---- evm/src/cpu/gas.rs | 28 +++++++++++++++++++++++--- evm/src/cpu/stack.rs | 10 ++------- evm/src/witness/transition.rs | 9 +++------ 8 files changed, 107 insertions(+), 39 deletions(-) diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index 250c67af9b..0c5be6ef52 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -24,12 +24,10 @@ pub(crate) struct OpsColumnsView { pub shift: T, /// Combines JUMPDEST and KECCAK_GENERAL flags. pub jumpdest_keccak_general: T, - /// Flag for PROVER_INPUT. - pub prover_input: T, /// Combines JUMP and JUMPI flags. pub jumps: T, - /// Flag for PUSH. - pub push: T, + /// Combines PUSH and PROVER_INPUT flags. + pub push_prover_input: T, /// Combines DUP and SWAP flags. pub dup_swap: T, /// Combines GET_CONTEXT and SET_CONTEXT flags. diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 77eb3be90e..2f0c72d433 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -24,10 +24,9 @@ const KEEPS_CONTEXT: OpsColumnsView = OpsColumnsView { not_pop: true, shift: true, jumpdest_keccak_general: true, - prover_input: true, + push_prover_input: true, jumps: true, pc_push0: true, - push: true, dup_swap: true, context_op: false, m_op_32bytes: true, diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs index 3e6f3f5b52..bde5930572 100644 --- a/evm/src/cpu/control_flow.rs +++ b/evm/src/cpu/control_flow.rs @@ -8,7 +8,7 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::kernel::aggregator::KERNEL; -const NATIVE_INSTRUCTIONS: [usize; 13] = [ +const NATIVE_INSTRUCTIONS: [usize; 12] = [ COL_MAP.op.binary_op, COL_MAP.op.ternary_op, COL_MAP.op.fp254_op, @@ -17,7 +17,7 @@ const NATIVE_INSTRUCTIONS: [usize; 13] = [ COL_MAP.op.not_pop, COL_MAP.op.shift, COL_MAP.op.jumpdest_keccak_general, - COL_MAP.op.prover_input, + // Not PROVER_INPUT: it is dealt with manually below. // not JUMPS (possible need to jump) COL_MAP.op.pc_push0, // not PUSH (need to increment by more than 1) @@ -70,6 +70,13 @@ pub(crate) fn eval_packed_generic( yield_constr .constraint_transition(is_native_instruction * (lv.is_kernel_mode - nv.is_kernel_mode)); + // Apply the same checks as before, for PROVER_INPUT. + let is_prover_input: P = lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES); + yield_constr.constraint_transition( + is_prover_input * (lv.program_counter - nv.program_counter + P::ONES), + ); + yield_constr.constraint_transition(is_prover_input * (lv.is_kernel_mode - nv.is_kernel_mode)); + // If a non-CPU cycle row is followed by a CPU cycle row, then: // - the `program_counter` of the CPU cycle row is `main` (the entry point of our kernel), // - execution is in kernel mode, and @@ -117,6 +124,17 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let kernel_diff = builder.sub_extension(lv.is_kernel_mode, nv.is_kernel_mode); let kernel_constr = builder.mul_extension(filter, kernel_diff); yield_constr.constraint_transition(builder, kernel_constr); + + // Same constraints as before, for PROVER_INPUT. + let is_prover_input = builder.mul_sub_extension( + lv.op.push_prover_input, + lv.opcode_bits[5], + lv.op.push_prover_input, + ); + let pc_constr = builder.mul_add_extension(is_prover_input, pc_diff, is_prover_input); + yield_constr.constraint_transition(builder, pc_constr); + let kernel_constr = builder.mul_extension(is_prover_input, kernel_diff); + yield_constr.constraint_transition(builder, kernel_constr); } // If a non-CPU cycle row is followed by a CPU cycle row, then: diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index b045b7e022..d4c663196f 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -103,18 +103,24 @@ pub(crate) fn ctl_arithmetic_base_rows() -> TableWithColumns { // (also `ops` is used as the operation filter). The list of // operations includes binary operations which will simply ignore // the third input. + let col_bit = Column::linear_combination_with_constant( + vec![(COL_MAP.opcode_bits[5], F::NEG_ONE)], + F::ONE, + ); TableWithColumns::new( Table::Cpu, columns, - Some(Filter::new_simple(Column::sum([ - COL_MAP.op.binary_op, - COL_MAP.op.fp254_op, - COL_MAP.op.ternary_op, - COL_MAP.op.shift, - COL_MAP.op.prover_input, - COL_MAP.op.syscall, - COL_MAP.op.exception, - ]))), + Some(Filter::new( + vec![(Column::single(COL_MAP.op.push_prover_input), col_bit)], + vec![Column::sum([ + COL_MAP.op.binary_op, + COL_MAP.op.fp254_op, + COL_MAP.op.ternary_op, + COL_MAP.op.shift, + COL_MAP.op.syscall, + COL_MAP.op.exception, + ])], + )), ) } @@ -201,7 +207,11 @@ pub(crate) fn ctl_data_byte_packing_push() -> Vec> { /// CTL filter for the `PUSH` operation. pub(crate) fn ctl_filter_byte_packing_push() -> Filter { - Filter::new_simple(Column::single(COL_MAP.op.push)) + let bit_col = Column::single(COL_MAP.opcode_bits[5]); + Filter::new( + vec![(Column::single(COL_MAP.op.push_prover_input), bit_col)], + vec![], + ) } /// Index of the memory channel storing code. diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs index 4e6dbfbd68..831f95ffaf 100644 --- a/evm/src/cpu/decode.rs +++ b/evm/src/cpu/decode.rs @@ -3,6 +3,7 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP}; @@ -23,7 +24,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP}; /// behavior. /// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to /// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid. -const OPCODES: [(u8, usize, bool, usize); 7] = [ +const OPCODES: [(u8, usize, bool, usize); 5] = [ // (start index of block, number of top bits to check (log2), kernel-only, flag column) // ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly through the Arithmetic table CTL. // ADDMOD, MULMOD and SUBMOD flags are handled partly manually here, and partly through the Arithmetic table CTL. @@ -33,9 +34,7 @@ const OPCODES: [(u8, usize, bool, usize); 7] = [ // NOT and POP are handled manually here. // SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL. // JUMPDEST and KECCAK_GENERAL are handled manually here. - (0x49, 0, true, COL_MAP.op.prover_input), (0x56, 1, false, COL_MAP.op.jumps), // 0x56-0x57 - (0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f (0x80, 5, false, COL_MAP.op.dup_swap), // 0x80-0x9f (0xf6, 1, true, COL_MAP.op.context_op), //0xf6-0xf7 (0xf9, 0, true, COL_MAP.op.exit_kernel), @@ -45,7 +44,7 @@ const OPCODES: [(u8, usize, bool, usize); 7] = [ /// List of combined opcodes requiring a special handling. /// Each index in the list corresponds to an arbitrary combination /// of opcodes defined in evm/src/cpu/columns/ops.rs. -const COMBINED_OPCODES: [usize; 10] = [ +const COMBINED_OPCODES: [usize; 11] = [ COL_MAP.op.logic_op, COL_MAP.op.fp254_op, COL_MAP.op.binary_op, @@ -56,6 +55,7 @@ const COMBINED_OPCODES: [usize; 10] = [ COL_MAP.op.not_pop, COL_MAP.op.pc_push0, COL_MAP.op.m_op_32bytes, + COL_MAP.op.push_prover_input, ]; /// Break up an opcode (which is 8 bits long) into its eight bits. @@ -192,6 +192,16 @@ pub(crate) fn eval_packed_generic( * (opcode - P::Scalar::from_canonical_usize(0xf8_usize)) * lv.op.m_op_32bytes; yield_constr.constraint(op_32bytes); + + // Manually check PUSH and PROVER_INPUT. + // PROVER_INPUT is a kernel-only instruction, but not PUSH. + let push_prover_input_constr = (opcode - P::Scalar::from_canonical_usize(0x49_usize)) + * (opcode_high_three - P::Scalar::from_canonical_usize(0x60_usize)) + * lv.op.push_prover_input; + yield_constr.constraint(push_prover_input_constr); + let prover_input_constr = + lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES) * (P::ONES - kernel_mode); + yield_constr.constraint(prover_input_constr); } fn opcode_high_bits_circuit, const D: usize>( @@ -373,4 +383,24 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let constr = builder.mul_extension(mstore_32bytes_constr, mload_32bytes_constr); let constr = builder.mul_extension(constr, lv.op.m_op_32bytes); yield_constr.constraint(builder, constr); + + // Manually check PUSH and PROVER_INPUT. + // PROVER_INPUT is a kernel-only instruction, but not PUSH. + let prover_input_opcode = + builder.constant_extension(F::Extension::from_canonical_usize(0x49usize)); + let push_opcodes = builder.constant_extension(F::Extension::from_canonical_usize(0x60usize)); + + let push_constr = builder.sub_extension(opcode_high_three, push_opcodes); + let prover_input_constr = builder.sub_extension(opcode, prover_input_opcode); + + let push_prover_input_constr = + builder.mul_many_extension([lv.op.push_prover_input, prover_input_constr, push_constr]); + yield_constr.constraint(builder, push_prover_input_constr); + let prover_input_filter = builder.mul_sub_extension( + lv.op.push_prover_input, + lv.opcode_bits[5], + lv.op.push_prover_input, + ); + let constr = builder.mul_extension(prover_input_filter, is_not_kernel_mode); + yield_constr.constraint(builder, constr); } diff --git a/evm/src/cpu/gas.rs b/evm/src/cpu/gas.rs index af86fb4766..be033c3c43 100644 --- a/evm/src/cpu/gas.rs +++ b/evm/src/cpu/gas.rs @@ -27,10 +27,9 @@ const SIMPLE_OPCODES: OpsColumnsView> = OpsColumnsView { not_pop: None, // This is handled manually below shift: G_VERYLOW, jumpdest_keccak_general: None, // This is handled manually below. - prover_input: KERNEL_ONLY_INSTR, - jumps: None, // Combined flag handled separately. + push_prover_input: None, // This is handled manually below. + jumps: None, // Combined flag handled separately. pc_push0: G_BASE, - push: G_VERYLOW, dup_swap: G_VERYLOW, context_op: KERNEL_ONLY_INSTR, m_op_32bytes: KERNEL_ONLY_INSTR, @@ -112,6 +111,14 @@ fn eval_packed_accumulate( yield_constr.constraint_transition( lv.op.jumpdest_keccak_general * (gas_diff - jumpdest_keccak_general_gas_cost), ); + + // For PROVER_INPUT and PUSH operations. + // PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1. + let push_prover_input_gas_cost = lv.opcode_bits[5] + * P::Scalar::from_canonical_u32(G_VERYLOW.unwrap()) + + (P::ONES - lv.opcode_bits[5]) * P::Scalar::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()); + yield_constr + .constraint_transition(lv.op.push_prover_input * (gas_diff - push_prover_input_gas_cost)); } fn eval_packed_init( @@ -270,6 +277,21 @@ fn eval_ext_circuit_accumulate, const D: usize>( let constr = builder.mul_extension(filter, gas_diff); yield_constr.constraint_transition(builder, constr); + + // For PROVER_INPUT and PUSH operations. + // PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1. + let push_prover_input_gas_cost = builder.arithmetic_extension( + F::from_canonical_u32(G_VERYLOW.unwrap()) + - F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), + F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), + lv.opcode_bits[5], + one, + one, + ); + let gas_diff = builder.sub_extension(nv_lv_diff, push_prover_input_gas_cost); + let constr = builder.mul_extension(lv.op.push_prover_input, gas_diff); + + yield_constr.constraint_transition(builder, constr); } fn eval_ext_circuit_init, const D: usize>( diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index b834482eb4..71dc7a2414 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -28,10 +28,9 @@ pub(crate) const MIGHT_OVERFLOW: OpsColumnsView = OpsColumnsView { not_pop: false, shift: false, jumpdest_keccak_general: false, - prover_input: false, + push_prover_input: true, // PROVER_INPUT doesn't require the check, but PUSH does. jumps: false, pc_push0: true, - push: true, dup_swap: true, context_op: false, m_op_32bytes: false, @@ -119,7 +118,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol disable_other_channels: false, }), jumpdest_keccak_general: None, - prover_input: Some(StackBehavior { + push_prover_input: Some(StackBehavior { num_pops: 0, pushes: true, disable_other_channels: true, @@ -130,11 +129,6 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol pushes: true, disable_other_channels: true, }), - push: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: true, - }), dup_swap: None, context_op: None, m_op_32bytes: Some(StackBehavior { diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 6f720a3b2e..cf2e3bbeba 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -161,7 +161,6 @@ pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result(op: Operation, row: &mut CpuColumnsView) { let flags = &mut row.op; *match op { - Operation::Push(1..) => &mut flags.push, Operation::Dup(_) | Operation::Swap(_) => &mut flags.dup_swap, Operation::Iszero | Operation::Eq => &mut flags.eq_iszero, Operation::Not | Operation::Pop => &mut flags.not_pop, @@ -175,7 +174,7 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { Operation::BinaryArithmetic(_) => &mut flags.binary_op, Operation::TernaryArithmetic(_) => &mut flags.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => &mut flags.jumpdest_keccak_general, - Operation::ProverInput => &mut flags.prover_input, + Operation::ProverInput | Operation::Push(1..) => &mut flags.push_prover_input, Operation::Jump | Operation::Jumpi => &mut flags.jumps, Operation::Pc | Operation::Push(0) => &mut flags.pc_push0, Operation::GetContext | Operation::SetContext => &mut flags.context_op, @@ -189,7 +188,7 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { const fn get_op_special_length(op: Operation) -> Option { let behavior_opt = match op { Operation::Push(0) | Operation::Pc => STACK_BEHAVIORS.pc_push0, - Operation::Push(1..) => STACK_BEHAVIORS.push, + Operation::Push(1..) | Operation::ProverInput => STACK_BEHAVIORS.push_prover_input, Operation::Dup(_) | Operation::Swap(_) => STACK_BEHAVIORS.dup_swap, Operation::Iszero => IS_ZERO_STACK_BEHAVIOR, Operation::Not | Operation::Pop => STACK_BEHAVIORS.not_pop, @@ -206,7 +205,6 @@ const fn get_op_special_length(op: Operation) -> Option { Operation::BinaryArithmetic(_) => STACK_BEHAVIORS.binary_op, Operation::TernaryArithmetic(_) => STACK_BEHAVIORS.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => STACK_BEHAVIORS.jumpdest_keccak_general, - Operation::ProverInput => STACK_BEHAVIORS.prover_input, Operation::Jump => JUMP_OP, Operation::Jumpi => JUMPI_OP, Operation::GetContext | Operation::SetContext => None, @@ -229,7 +227,7 @@ const fn get_op_special_length(op: Operation) -> Option { // Kernel-only pushing instructions aren't considered; they can't overflow. const fn might_overflow_op(op: Operation) -> bool { match op { - Operation::Push(1..) => MIGHT_OVERFLOW.push, + Operation::Push(1..) | Operation::ProverInput => MIGHT_OVERFLOW.push_prover_input, Operation::Dup(_) | Operation::Swap(_) => MIGHT_OVERFLOW.dup_swap, Operation::Iszero | Operation::Eq => MIGHT_OVERFLOW.eq_iszero, Operation::Not | Operation::Pop => MIGHT_OVERFLOW.not_pop, @@ -245,7 +243,6 @@ const fn might_overflow_op(op: Operation) -> bool { Operation::BinaryArithmetic(_) => MIGHT_OVERFLOW.binary_op, Operation::TernaryArithmetic(_) => MIGHT_OVERFLOW.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => MIGHT_OVERFLOW.jumpdest_keccak_general, - Operation::ProverInput => MIGHT_OVERFLOW.prover_input, Operation::Jump | Operation::Jumpi => MIGHT_OVERFLOW.jumps, Operation::Pc | Operation::Push(0) => MIGHT_OVERFLOW.pc_push0, Operation::GetContext | Operation::SetContext => MIGHT_OVERFLOW.context_op, From 3e8ad0868845cec242a4c2085add7c1462cf5baa Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 17:33:53 +0100 Subject: [PATCH 018/175] Rebase to main --- evm/src/cpu/kernel/asm/core/call.asm | 9 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 182 ++++++++++++++++++ evm/src/generation/mod.rs | 67 ++++++- evm/src/generation/prover_input.rs | 153 ++++++++++++++- evm/src/generation/state.rs | 26 +++ evm/src/prover.rs | 4 +- evm/src/util.rs | 5 + evm/src/witness/errors.rs | 2 + evm/src/witness/transition.rs | 2 +- 9 files changed, 435 insertions(+), 15 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 2e7d1d7345..5a2a14c45f 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -367,11 +367,12 @@ call_too_deep: %checkpoint // Checkpoint %increment_call_depth // Perform jumpdest analyis - PUSH %%after - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - GET_CONTEXT + // PUSH %%after + // %mload_context_metadata(@CTX_METADATA_CODE_SIZE) + // GET_CONTEXT // stack: ctx, code_size, retdest - %jump(jumpdest_analysis) + // %jump(jumpdest_analysis) + %validate_jumpdest_table %%after: PUSH 0 // jump dest EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e63..09bb35fad4 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -1,3 +1,48 @@ +// Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], +// for the given context's code. Panics if we never hit final_pos +// Pre stack: init_pos, ctx, final_pos, retdest +// Post stack: (empty) +global verify_path: +loop_new: + // stack: i, ctx, final_pos, retdest + // Ideally we would break if i >= final_pos, but checking i > final_pos is + // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is + // a no-op. + DUP3 DUP2 EQ // i == final_pos + %jumpi(return_new) + DUP3 DUP2 GT // i > final_pos + %jumpi(panic) + + // stack: i, ctx, final_pos, retdest + %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) + MLOAD_GENERAL + // stack: opcode, i, ctx, final_pos, retdest + + DUP1 + // Slightly more efficient than `%eq_const(0x5b) ISZERO` + PUSH 0x5b + SUB + // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest + %jumpi(continue_new) + + // stack: JUMPDEST, i, ctx, code_len, retdest + %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + MSTORE_GENERAL + +continue_new: + // stack: opcode, i, ctx, code_len, retdest + %add_const(code_bytes_to_skip) + %mload_kernel_code + // stack: bytes_to_skip, i, ctx, code_len, retdest + ADD + // stack: i, ctx, code_len, retdest + %jump(loop_new) + +return_new: + // stack: i, ctx, code_len, retdest + %pop3 + JUMP + // Populates @SEGMENT_JUMPDEST_BITS for the given context's code. // Pre stack: ctx, code_len, retdest // Post stack: (empty) @@ -89,3 +134,140 @@ code_bytes_to_skip: %rep 128 BYTES 1 // 0x80-0xff %endrep + + +// A proof attesting that jumpdest is a valid jump destinations is +// either 0 or an index 0 < i <= jumpdest - 32. +// A proof is valid if: +// - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b +// - i > 0 and: +// - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, +// - we can go from opcode i+32 to jumpdest, +// - code[jumpdest] = 0x5b. +// stack: proof_prefix_addr, jumpdest, retdest +// stack: (empty) abort if jumpdest is not a valid destination +global is_jumpdest: + GET_CONTEXT + // stack: ctx, proof_prefix_addr, jumpdest, retdest + %stack + (ctx, proof_prefix_addr, jumpdest) -> + (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) + MLOAD_GENERAL + // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest + + // Slightly more efficient than `%eq_const(0x5b) ISZERO` + PUSH 0x5b + SUB + %jumpi(panic) + + //stack: jumpdest, ctx, proof_prefix_addr, retdest + SWAP2 DUP1 + // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest + %eq_const(0) + %jumpi(verify_path) + //stack: proof_prefix_addr, ctx, jumpdest, retdest + // If we are here we need to check that the next 32 bytes are less + // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, + // or larger than 127 + %check_and_step(127) %check_and_step(126) %check_and_step(125) %check_and_step(124) + %check_and_step(123) %check_and_step(122) %check_and_step(121) %check_and_step(120) + %check_and_step(119) %check_and_step(118) %check_and_step(117) %check_and_step(116) + %check_and_step(115) %check_and_step(114) %check_and_step(113) %check_and_step(112) + %check_and_step(111) %check_and_step(110) %check_and_step(109) %check_and_step(108) + %check_and_step(107) %check_and_step(106) %check_and_step(105) %check_and_step(104) + %check_and_step(103) %check_and_step(102) %check_and_step(101) %check_and_step(100) + %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) + + // check the remaining path + %jump(verify_path) + +return_is_jumpdest: + //stack: proof_prefix_addr, jumpdest, retdest + %pop2 + JUMP + + +// Chek if the opcode pointed by proof_prefix address is +// less than max and increment proof_prefix_addr +%macro check_and_step(max) + %stack + (proof_prefix_addr, ctx, jumpdest) -> + (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) + MLOAD_GENERAL + // stack: opcode, proof_prefix_addr, ctx, jumpdest + DUP1 + %gt_const(127) + %jumpi(%%ok) + %assert_lt_const($max) + // stack: proof_prefix_addr, ctx, jumpdest + PUSH 0 // We need something to pop +%%ok: + POP + %increment +%endmacro + +%macro is_jumpdest + %stack (proof, addr) -> (proof, addr, %%after) + %jump(is_jumpdest) +%%after: +%endmacro + +// Check if the jumpdest table is correct. This is done by +// non-deterministically guessing the sequence of jumpdest +// addresses used during program execution within the current context. +// For each jumpdest address we also non-deterministically guess +// a proof, which is another address in the code, such that +// is_jumpdest don't abort when the proof is on the top of the stack +// an the jumpdest address below. If that's the case we set the +// corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. +// +// stack: retdest +// stack: (empty) +global validate_jumpdest_table: + // If address > 0 it is interpreted as address' = address - 1 + // and the next prover input should contain a proof for address'. + PROVER_INPUT(jumpdest_table::next_address) + DUP1 %jumpi(check_proof) + // If proof == 0 there are no more jump destionations to check + POP +global validate_jumpdest_table_end: + JUMP + // were set to 0 + //%mload_context_metadata(@CTX_METADATA_CODE_SIZE) + // get the code length in bytes + //%add_const(31) + //%div_const(32) + //GET_CONTEXT + //SWAP2 +//verify_chunk: + // stack: i (= proof), code_len, ctx = 0 + //%stack (i, code_len, ctx) -> (code_len, i, ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx) + //GT + //%jumpi(valid_table) + //%mload_packing + // stack: packed_bits, code_len, i, ctx + //%assert_eq_const(0) + //%increment + //%jump(verify_chunk) + +check_proof: + %sub_const(1) + DUP1 + // We read the proof + PROVER_INPUT(jumpdest_table::next_proof) + // stack: proof, address + %is_jumpdest + GET_CONTEXT + %stack (ctx, address) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address) + MSTORE_GENERAL + %jump(validate_jumpdest_table) +valid_table: + // stack: ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx, retdest + %pop7 + JUMP + +%macro validate_jumpdest_table + PUSH %%after + %jump(validate_jumpdest_table) +%%after: +%endmacro diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 77b6fd36d7..94c0e43258 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use anyhow::anyhow; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; @@ -6,6 +6,7 @@ use ethereum_types::{Address, BigEndianHash, H256, U256}; use itertools::enumerate; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -19,11 +20,13 @@ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::opcodes::get_opcode; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; -use crate::util::h2u; +use crate::util::{h2u, u256_to_u8, u256_to_usize}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -34,7 +37,7 @@ pub(crate) mod state; mod trie_extractor; use self::mpt::{load_all_mpts, TrieRootPtrs}; -use crate::witness::util::mem_write_log; +use crate::witness::util::{mem_write_log, stack_peek}; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] @@ -244,9 +247,7 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn simulate_cpu, const D: usize>( - state: &mut GenerationState, -) -> anyhow::Result<()> { +fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { let halt_pc = KERNEL.global_labels["halt"]; loop { @@ -281,3 +282,57 @@ fn simulate_cpu, const D: usize>( transition(state)?; } } + +fn simulate_cpu_between_labels_and_get_user_jumps( + initial_label: &str, + final_label: &str, + state: &mut GenerationState, +) -> anyhow::Result> { + let halt_pc = KERNEL.global_labels[final_label]; + let mut jumpdest_addresses = HashSet::new(); + state.registers.program_counter = KERNEL.global_labels[initial_label]; + let context = state.registers.context; + + loop { + if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { + state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] + } + let pc = state.registers.program_counter; + let halt = state.registers.is_kernel && pc == halt_pc && state.registers.context == context; + let opcode = u256_to_u8(state.memory.get(MemoryAddress { + context: state.registers.context, + segment: Segment::Code as usize, + virt: state.registers.program_counter, + })) + .map_err(|_| anyhow::Error::msg("Invalid opcode."))?; + let cond = if let Ok(cond) = stack_peek(state, 1) { + cond != U256::zero() + } else { + false + }; + if !state.registers.is_kernel + && (opcode == get_opcode("JUMP") || (opcode == get_opcode("JUMPI") && cond)) + { + // TODO: hotfix for avoiding deeper calls to abort + let jumpdest = u256_to_usize(state.registers.stack_top) + .map_err(|_| anyhow::Error::msg("Not a valid jump destination"))?; + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: jumpdest, + }, + U256::one(), + ); + if (state.registers.context == context) { + jumpdest_addresses.insert(jumpdest); + } + } + if halt { + log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + return Ok(jumpdest_addresses.into_iter().collect()); + } + + transition(state)?; + } +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b2a8f0cea0..852cd77da5 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,24 +1,34 @@ +use std::cmp::min; +use std::collections::HashSet; use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; +use hashbrown::HashMap; use itertools::{enumerate, Itertools}; use num_bigint::BigUint; +use plonky2::field::extension::Extendable; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; +use crate::generation::simulate_cpu_between_labels_and_get_user_jumps; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::memory::segments::Segment::BnPairing; -use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; -use crate::witness::errors::ProgramError; +use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_u8, u256_to_usize}; use crate::witness::errors::ProverInputError::*; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::MemoryAddress; use crate::witness::util::{current_context_peek, stack_peek}; @@ -47,6 +57,7 @@ impl GenerationState { "bignum_modmul" => self.run_bignum_modmul(), "withdrawal" => self.run_withdrawal(), "num_bits" => self.run_num_bits(), + "jumpdest_table" => self.run_jumpdest_table(input_fn), _ => Err(ProgramError::ProverInputError(InvalidFunction)), } } @@ -229,6 +240,144 @@ impl GenerationState { Ok(num_bits.into()) } } + + fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { + match input_fn.0[1].as_str() { + "next_address" => self.run_next_jumpdest_table_address(), + "next_proof" => self.run_next_jumpdest_table_proof(), + _ => Err(ProgramError::ProverInputError(InvalidInput)), + } + } + /// Return the next used jump addres + fn run_next_jumpdest_table_address(&mut self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + + if self.jumpdest_addresses.is_none() { + let mut state: GenerationState = self.soft_clone(); + + let mut jumpdest_addresses = vec![]; + // Generate the jumpdest table + let code = (0..code_len) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i, + })) + }) + .collect::, _>>()?; + let mut i = 0; + while i < code_len { + if code[i] == get_opcode("JUMPDEST") { + jumpdest_addresses.push(i); + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: i, + }, + U256::one(), + ); + log::debug!("jumpdest at {i}"); + } + i += if code[i] >= get_push_opcode(1) && code[i] <= get_push_opcode(32) { + (code[i] - get_push_opcode(1) + 2).into() + } else { + 1 + } + } + + // We need to skip the validate table call + self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + &mut state, + ) + .ok(); + log::debug!("code len = {code_len}"); + log::debug!("all jumpdest addresses = {:?}", jumpdest_addresses); + log::debug!("user's jumdest addresses = {:?}", self.jumpdest_addresses); + // self.jumpdest_addresses = Some(jumpdest_addresses); + } + + let Some(jumpdest_table) = &mut self.jumpdest_addresses else { + // TODO: Add another error + return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); + }; + + if let Some(next_jumpdest_address) = jumpdest_table.pop() { + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) + } else { + self.jumpdest_addresses = None; + Ok(U256::zero()) + } + } + + /// Return the proof for the last jump adddress + fn run_next_jumpdest_table_proof(&mut self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + + let mut address = MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: 0, + }; + let mut proof = 0; + let mut prefix_size = 0; + + // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem + // a problem because is done natively. + + // Search the closest address to last_jumpdest_address for which none of + // the previous 32 bytes in the code (including opcodes and pushed bytes) + // are PUSHXX and the address is in its range + while address.virt < self.last_jumpdest_address { + let opcode = u256_to_u8(self.memory.get(address))?; + let is_push = + opcode >= get_push_opcode(1).into() && opcode <= get_push_opcode(32).into(); + + address.virt += if is_push { + (opcode - get_push_opcode(1) + 2).into() + } else { + 1 + }; + // Check if the new address has a prefix of size >= 32 + let mut has_prefix = true; + for i in address.virt as i32 - 32..address.virt as i32 { + let opcode = u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i as usize, + }))?; + if i < 0 + || (opcode >= get_push_opcode(1) + && opcode <= get_push_opcode(32) + && i + (opcode - get_push_opcode(1)) as i32 + 1 >= address.virt as i32) + { + has_prefix = false; + break; + } + } + if has_prefix { + proof = address.virt - 32; + } + } + if address.virt > self.last_jumpdest_address { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpDestination, + )); + } + Ok(proof.into()) + } } enum EvmField { diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 89ff0c5af9..79dd94fb2f 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -50,6 +50,9 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, + + pub(crate) last_jumpdest_address: usize, + pub(crate) jumpdest_addresses: Option>, } impl GenerationState { @@ -91,6 +94,8 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, + last_jumpdest_address: 0, + jumpdest_addresses: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -167,6 +172,27 @@ impl GenerationState { .map(|i| stack_peek(self, i).unwrap()) .collect() } + + /// Clone everything but the traces + pub(crate) fn soft_clone(&self) -> GenerationState { + Self { + inputs: self.inputs.clone(), + registers: self.registers.clone(), + memory: self.memory.clone(), + traces: Traces::default(), + rlp_prover_inputs: self.rlp_prover_inputs.clone(), + state_key_to_address: self.state_key_to_address.clone(), + bignum_modmul_result_limbs: self.bignum_modmul_result_limbs.clone(), + withdrawal_prover_inputs: self.withdrawal_prover_inputs.clone(), + trie_root_ptrs: TrieRootPtrs { + state_root_ptr: 0, + txn_root_ptr: 0, + receipt_root_ptr: 0, + }, + last_jumpdest_address: 0, + jumpdest_addresses: None, + } + } } /// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. diff --git a/evm/src/prover.rs b/evm/src/prover.rs index ab33a66103..51fea9a4e8 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -377,7 +377,7 @@ where let alphas = challenger.get_n_challenges(config.num_challenges); - #[cfg(test)] + // #[cfg(test)] { check_constraints( stark, @@ -636,7 +636,7 @@ where .collect() } -#[cfg(test)] +// #[cfg(test)] /// Check that all constraints evaluate to zero on `H`. /// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. fn check_constraints<'a, F, C, S, const D: usize>( diff --git a/evm/src/util.rs b/evm/src/util.rs index 9cac52c66b..7a635c0e1d 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,6 +70,11 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } +/// Safe alternative to `U256::as_u8()`, which errors in case of overflow instead of panicking. +pub(crate) fn u256_to_u8(u256: U256) -> Result { + u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) +} + /// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 5a0fcbfb32..1b266aefde 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -36,4 +36,6 @@ pub enum ProverInputError { InvalidInput, InvalidFunction, NumBitsError, + InvalidJumpDestination, + InvalidJumpdestSimulation, } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..0fa14321f2 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,7 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?}", op); + log::debug!("User instruction: {:?} stack = {:?}", op, state.stack()); } fill_op_flag(op, &mut row); From f76ab777417e757ec6d0e4d93d7524efdf9f5c4d Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 14:11:43 +0100 Subject: [PATCH 019/175] Refactor run_next_jumpdest_table_proof --- evm/src/cpu/kernel/asm/core/call.asm | 7 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 114 +++------- evm/src/cpu/kernel/interpreter.rs | 9 + .../kernel/tests/core/jumpdest_analysis.rs | 70 +++--- evm/src/generation/mod.rs | 6 +- evm/src/generation/prover_input.rs | 207 +++++++++++------- evm/src/prover.rs | 4 +- evm/src/witness/transition.rs | 7 +- 8 files changed, 208 insertions(+), 216 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 5a2a14c45f..46765954c4 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -367,13 +367,10 @@ call_too_deep: %checkpoint // Checkpoint %increment_call_depth // Perform jumpdest analyis - // PUSH %%after - // %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - // GET_CONTEXT + GET_CONTEXT // stack: ctx, code_size, retdest - // %jump(jumpdest_analysis) %validate_jumpdest_table -%%after: + PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 09bb35fad4..76c25fa07d 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -3,13 +3,13 @@ // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) global verify_path: -loop_new: +loop: // stack: i, ctx, final_pos, retdest // Ideally we would break if i >= final_pos, but checking i > final_pos is // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is // a no-op. DUP3 DUP2 EQ // i == final_pos - %jumpi(return_new) + %jumpi(return) DUP3 DUP2 GT // i > final_pos %jumpi(panic) @@ -18,51 +18,6 @@ loop_new: MLOAD_GENERAL // stack: opcode, i, ctx, final_pos, retdest - DUP1 - // Slightly more efficient than `%eq_const(0x5b) ISZERO` - PUSH 0x5b - SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest - %jumpi(continue_new) - - // stack: JUMPDEST, i, ctx, code_len, retdest - %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) - MSTORE_GENERAL - -continue_new: - // stack: opcode, i, ctx, code_len, retdest - %add_const(code_bytes_to_skip) - %mload_kernel_code - // stack: bytes_to_skip, i, ctx, code_len, retdest - ADD - // stack: i, ctx, code_len, retdest - %jump(loop_new) - -return_new: - // stack: i, ctx, code_len, retdest - %pop3 - JUMP - -// Populates @SEGMENT_JUMPDEST_BITS for the given context's code. -// Pre stack: ctx, code_len, retdest -// Post stack: (empty) -global jumpdest_analysis: - // stack: ctx, code_len, retdest - PUSH 0 // i = 0 - -loop: - // stack: i, ctx, code_len, retdest - // Ideally we would break if i >= code_len, but checking i > code_len is - // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is - // a no-op. - DUP3 DUP2 GT // i > code_len - %jumpi(return) - - // stack: i, ctx, code_len, retdest - %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) - MLOAD_GENERAL - // stack: opcode, i, ctx, code_len, retdest - DUP1 // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b @@ -144,13 +99,17 @@ code_bytes_to_skip: // - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, // - we can go from opcode i+32 to jumpdest, // - code[jumpdest] = 0x5b. -// stack: proof_prefix_addr, jumpdest, retdest +// stack: proof_prefix_addr, jumpdest, ctx, retdest // stack: (empty) abort if jumpdest is not a valid destination global is_jumpdest: - GET_CONTEXT - // stack: ctx, proof_prefix_addr, jumpdest, retdest + // stack: proof_prefix_addr, jumpdest, ctx, retdest + //%stack + // (proof_prefix_addr, jumpdest, ctx) -> + // (ctx, @SEGMENT_JUMPDEST_BITS, jumpdest, proof_prefix_addr, jumpdest, ctx) + //MLOAD_GENERAL + //%jumpi(return_is_jumpdest) %stack - (ctx, proof_prefix_addr, jumpdest) -> + (proof_prefix_addr, jumpdest, ctx) -> (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest @@ -182,8 +141,8 @@ global is_jumpdest: %jump(verify_path) return_is_jumpdest: - //stack: proof_prefix_addr, jumpdest, retdest - %pop2 + //stack: proof_prefix_addr, jumpdest, ctx, retdest + %pop3 JUMP @@ -194,7 +153,7 @@ return_is_jumpdest: (proof_prefix_addr, ctx, jumpdest) -> (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) MLOAD_GENERAL - // stack: opcode, proof_prefix_addr, ctx, jumpdest + // stack: opcode, ctx, proof_prefix_addr, jumpdest DUP1 %gt_const(127) %jumpi(%%ok) @@ -207,7 +166,7 @@ return_is_jumpdest: %endmacro %macro is_jumpdest - %stack (proof, addr) -> (proof, addr, %%after) + %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) %jump(is_jumpdest) %%after: %endmacro @@ -216,58 +175,41 @@ return_is_jumpdest: // non-deterministically guessing the sequence of jumpdest // addresses used during program execution within the current context. // For each jumpdest address we also non-deterministically guess -// a proof, which is another address in the code, such that -// is_jumpdest don't abort when the proof is on the top of the stack +// a proof, which is another address in the code such that +// is_jumpdest don't abort, when the proof is at the top of the stack // an the jumpdest address below. If that's the case we set the // corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. // -// stack: retdest +// stack: ctx, retdest // stack: (empty) global validate_jumpdest_table: - // If address > 0 it is interpreted as address' = address - 1 + // If address > 0 then address is interpreted as address' + 1 // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) DUP1 %jumpi(check_proof) // If proof == 0 there are no more jump destionations to check POP +// This is just a hook used for avoiding verification of the jumpdest +// table in another contexts. It is useful during proof generation, +// allowing the avoidance of table verification when simulating user code. global validate_jumpdest_table_end: + POP JUMP - // were set to 0 - //%mload_context_metadata(@CTX_METADATA_CODE_SIZE) - // get the code length in bytes - //%add_const(31) - //%div_const(32) - //GET_CONTEXT - //SWAP2 -//verify_chunk: - // stack: i (= proof), code_len, ctx = 0 - //%stack (i, code_len, ctx) -> (code_len, i, ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx) - //GT - //%jumpi(valid_table) - //%mload_packing - // stack: packed_bits, code_len, i, ctx - //%assert_eq_const(0) - //%increment - //%jump(verify_chunk) - check_proof: %sub_const(1) - DUP1 + DUP2 DUP2 + // stack: address, ctx, address, ctx // We read the proof PROVER_INPUT(jumpdest_table::next_proof) - // stack: proof, address + // stack: proof, address, ctx, address, ctx %is_jumpdest - GET_CONTEXT - %stack (ctx, address) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address) + %stack (address, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address, ctx) MSTORE_GENERAL + %jump(validate_jumpdest_table) -valid_table: - // stack: ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx, retdest - %pop7 - JUMP %macro validate_jumpdest_table - PUSH %%after + %stack (ctx) -> (ctx, %%after) %jump(validate_jumpdest_table) %%after: %endmacro diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index f187a4804a..78691632a9 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -10,6 +10,7 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use super::assembler::BYTES_PER_OFFSET; +use super::utils::u256_from_bool; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -289,6 +290,14 @@ impl<'a> Interpreter<'a> { .collect() } + pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] + .content = jumpdest_bits + .into_iter() + .map(|x| u256_from_bool(x)) + .collect(); + } + fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 022a18d729..d3edc17bfc 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -4,39 +4,37 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -#[test] -fn test_jumpdest_analysis() -> Result<()> { - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; - const CONTEXT: usize = 3; // arbitrary - - let add = get_opcode("ADD"); - let push2 = get_push_opcode(2); - let jumpdest = get_opcode("JUMPDEST"); - - #[rustfmt::skip] - let code: Vec = vec![ - add, - jumpdest, - push2, - jumpdest, // part of PUSH2 - jumpdest, // part of PUSH2 - jumpdest, - add, - jumpdest, - ]; - - let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true]; - - // Contract creation transaction. - let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()]; - let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); - interpreter.set_code(CONTEXT, code); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![]); - assert_eq!( - interpreter.get_jumpdest_bits(CONTEXT), - expected_jumpdest_bits - ); - - Ok(()) -} +// #[test] +// fn test_jumpdest_analysis() -> Result<()> { +// let jumpdest_analysis = KERNEL.global_labels["validate_jumpdest_table"]; +// const CONTEXT: usize = 3; // arbitrary + +// let add = get_opcode("ADD"); +// let push2 = get_push_opcode(2); +// let jumpdest = get_opcode("JUMPDEST"); + +// #[rustfmt::skip] +// let code: Vec = vec![ +// add, +// jumpdest, +// push2, +// jumpdest, // part of PUSH2 +// jumpdest, // part of PUSH2 +// jumpdest, +// add, +// jumpdest, +// ]; + +// let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + +// // Contract creation transaction. +// let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; +// let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); +// interpreter.set_code(CONTEXT, code); +// interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + +// interpreter.run()?; +// assert_eq!(interpreter.stack(), vec![]); + +// Ok(()) +// } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 94c0e43258..71176da518 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -293,6 +293,8 @@ fn simulate_cpu_between_labels_and_get_user_jumps( state.registers.program_counter = KERNEL.global_labels[initial_label]; let context = state.registers.context; + log::debug!("Simulating CPU for jumpdest analysis "); + loop { if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] @@ -330,7 +332,9 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } if halt { log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - return Ok(jumpdest_addresses.into_iter().collect()); + let mut jumpdest_addresses: Vec = jumpdest_addresses.into_iter().collect(); + jumpdest_addresses.sort(); + return Ok(jumpdest_addresses); } transition(state)?; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 852cd77da5..2435f4ebf7 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -257,51 +257,7 @@ impl GenerationState { }))?; if self.jumpdest_addresses.is_none() { - let mut state: GenerationState = self.soft_clone(); - - let mut jumpdest_addresses = vec![]; - // Generate the jumpdest table - let code = (0..code_len) - .map(|i| { - u256_to_u8(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: i, - })) - }) - .collect::, _>>()?; - let mut i = 0; - while i < code_len { - if code[i] == get_opcode("JUMPDEST") { - jumpdest_addresses.push(i); - state.memory.set( - MemoryAddress { - context: state.registers.context, - segment: Segment::JumpdestBits as usize, - virt: i, - }, - U256::one(), - ); - log::debug!("jumpdest at {i}"); - } - i += if code[i] >= get_push_opcode(1) && code[i] <= get_push_opcode(32) { - (code[i] - get_push_opcode(1) + 2).into() - } else { - 1 - } - } - - // We need to skip the validate table call - self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", - "terminate_common", - &mut state, - ) - .ok(); - log::debug!("code len = {code_len}"); - log::debug!("all jumpdest addresses = {:?}", jumpdest_addresses); - log::debug!("user's jumdest addresses = {:?}", self.jumpdest_addresses); - // self.jumpdest_addresses = Some(jumpdest_addresses); + self.generate_jumpdest_table()?; } let Some(jumpdest_table) = &mut self.jumpdest_addresses else { @@ -326,57 +282,138 @@ impl GenerationState { virt: ContextMetadata::CodeSize as usize, }))?; - let mut address = MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: 0, - }; - let mut proof = 0; - let mut prefix_size = 0; + let code = (0..self.last_jumpdest_address) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i, + })) + }) + .collect::, _>>()?; // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem - // a problem because is done natively. + // a problem as is done natively. // Search the closest address to last_jumpdest_address for which none of // the previous 32 bytes in the code (including opcodes and pushed bytes) // are PUSHXX and the address is in its range - while address.virt < self.last_jumpdest_address { - let opcode = u256_to_u8(self.memory.get(address))?; - let is_push = - opcode >= get_push_opcode(1).into() && opcode <= get_push_opcode(32).into(); - - address.virt += if is_push { - (opcode - get_push_opcode(1) + 2).into() - } else { - 1 - }; - // Check if the new address has a prefix of size >= 32 - let mut has_prefix = true; - for i in address.virt as i32 - 32..address.virt as i32 { - let opcode = u256_to_u8(self.memory.get(MemoryAddress { + + let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( + 0, + |acc, (pos, opcode)| { + let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { + code[prefix_start..pos].iter().enumerate().fold( + true, + |acc, (prefix_pos, &byte)| { + acc && (byte > get_push_opcode(32) + || (prefix_start + prefix_pos) as i32 + + (byte as i32 - get_push_opcode(1) as i32) + + 1 + < pos as i32) + }, + ) + } else { + false + }; + if has_prefix { + pos - 32 + } else { + acc + } + }, + ); + Ok(proof.into()) + } +} + +impl GenerationState { + fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { + let mut state = self.soft_clone(); + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + // Generate the jumpdest table + let code = (0..code_len) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { context: self.registers.context, segment: Segment::Code as usize, - virt: i as usize, - }))?; - if i < 0 - || (opcode >= get_push_opcode(1) - && opcode <= get_push_opcode(32) - && i + (opcode - get_push_opcode(1)) as i32 + 1 >= address.virt as i32) - { - has_prefix = false; - break; - } - } - if has_prefix { - proof = address.virt - 32; + virt: i, + })) + }) + .collect::, _>>()?; + + // We need to set the the simulated jumpdest bits to one as otherwise + // the simulation will fail + let mut jumpdest_table = vec![]; + for (pos, opcode) in CodeIterator::new(&code) { + jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); + if opcode == get_opcode("JUMPDEST") { + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: pos, + }, + U256::one(), + ); } } - if address.virt > self.last_jumpdest_address { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidJumpDestination, - )); + + // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call + self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + &mut state, + ) + .ok(); + + Ok(()) + } +} + +struct CodeIterator<'a> { + code: &'a Vec, + pos: usize, + end: usize, +} + +impl<'a> CodeIterator<'a> { + fn new(code: &'a Vec) -> Self { + CodeIterator { + end: code.len(), + code, + pos: 0, + } + } + fn until(code: &'a Vec, end: usize) -> Self { + CodeIterator { + end: std::cmp::min(code.len(), end), + code, + pos: 0, } - Ok(proof.into()) + } +} + +impl<'a> Iterator for CodeIterator<'a> { + type Item = (usize, u8); + + fn next(&mut self) -> Option { + let CodeIterator { code, pos, end } = self; + if *pos >= *end { + return None; + } + let opcode = code[*pos]; + let old_pos = *pos; + *pos += if opcode >= get_push_opcode(1) && opcode <= get_push_opcode(32) { + (opcode - get_push_opcode(1) + 2).into() + } else { + 1 + }; + Some((old_pos, opcode)) } } diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 51fea9a4e8..ab33a66103 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -377,7 +377,7 @@ where let alphas = challenger.get_n_challenges(config.num_challenges); - // #[cfg(test)] + #[cfg(test)] { check_constraints( stark, @@ -636,7 +636,7 @@ where .collect() } -// #[cfg(test)] +#[cfg(test)] /// Check that all constraints evaluate to zero on `H`. /// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. fn check_constraints<'a, F, C, S, const D: usize>( diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 0fa14321f2..046885439e 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,12 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?} stack = {:?}", op, state.stack()); + log::debug!( + "User instruction: {:?} ctx = {:?} stack = {:?}", + op, + state.registers.context, + state.stack() + ); } fill_op_flag(op, &mut row); From 746e13448b0c8469b81b0da7fb3a280455344c6b Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 17:06:42 +0100 Subject: [PATCH 020/175] Fix jumpdest analisys test --- evm/src/cpu/kernel/interpreter.rs | 13 ++-- .../kernel/tests/core/jumpdest_analysis.rs | 68 +++++++++---------- evm/src/generation/prover_input.rs | 6 -- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 78691632a9..a16d2d3af8 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -292,10 +292,15 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] - .content = jumpdest_bits - .into_iter() - .map(|x| u256_from_bool(x)) - .collect(); + .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); + self.generation_state.jumpdest_addresses = Some( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i) + .collect(), + ) } fn incr(&mut self, n: usize) { diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index d3edc17bfc..58e9f936b5 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -4,37 +4,37 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -// #[test] -// fn test_jumpdest_analysis() -> Result<()> { -// let jumpdest_analysis = KERNEL.global_labels["validate_jumpdest_table"]; -// const CONTEXT: usize = 3; // arbitrary - -// let add = get_opcode("ADD"); -// let push2 = get_push_opcode(2); -// let jumpdest = get_opcode("JUMPDEST"); - -// #[rustfmt::skip] -// let code: Vec = vec![ -// add, -// jumpdest, -// push2, -// jumpdest, // part of PUSH2 -// jumpdest, // part of PUSH2 -// jumpdest, -// add, -// jumpdest, -// ]; - -// let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; - -// // Contract creation transaction. -// let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; -// let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); -// interpreter.set_code(CONTEXT, code); -// interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); - -// interpreter.run()?; -// assert_eq!(interpreter.stack(), vec![]); - -// Ok(()) -// } +#[test] +fn test_validate_jumpdest_table() -> Result<()> { + let validate_jumpdest_table = KERNEL.global_labels["validate_jumpdest_table"]; + const CONTEXT: usize = 3; // arbitrary + + let add = get_opcode("ADD"); + let push2 = get_push_opcode(2); + let jumpdest = get_opcode("JUMPDEST"); + + #[rustfmt::skip] + let code: Vec = vec![ + add, + jumpdest, + push2, + jumpdest, // part of PUSH2 + jumpdest, // part of PUSH2 + jumpdest, + add, + jumpdest, + ]; + + let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + + // Contract creation transaction. + let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; + let mut interpreter = Interpreter::new_with_kernel(validate_jumpdest_table, initial_stack); + interpreter.set_code(CONTEXT, code); + interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + Ok(()) +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 2435f4ebf7..53e25db4b2 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -276,12 +276,6 @@ impl GenerationState { /// Return the proof for the last jump adddress fn run_next_jumpdest_table_proof(&mut self) -> Result { - let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::CodeSize as usize, - }))?; - let code = (0..self.last_jumpdest_address) .map(|i| { u256_to_u8(self.memory.get(MemoryAddress { From 71dff6e9827f501bc59416dc25ce06c4aec030ab Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Wed, 13 Dec 2023 18:35:17 -0500 Subject: [PATCH 021/175] Constrain MSTORE_32BYTES new offset limbs (#1415) --- evm/src/cpu/byte_unpacking.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/evm/src/cpu/byte_unpacking.rs b/evm/src/cpu/byte_unpacking.rs index aed3bc4e77..14518bfad9 100644 --- a/evm/src/cpu/byte_unpacking.rs +++ b/evm/src/cpu/byte_unpacking.rs @@ -16,7 +16,7 @@ pub(crate) fn eval_packed( // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES // by the 5th bit set to 0. let filter = lv.op.m_op_32bytes * (lv.opcode_bits[5] - P::ONES); - let new_offset = nv.mem_channels[0].value[0]; + let new_offset = nv.mem_channels[0].value; let virt = lv.mem_channels[2].value[0]; // Read len from opcode bits and constrain the pushed new offset. let len_bits: P = lv.opcode_bits[..5] @@ -25,7 +25,10 @@ pub(crate) fn eval_packed( .map(|(i, &bit)| bit * P::Scalar::from_canonical_u64(1 << i)) .sum(); let len = len_bits + P::ONES; - yield_constr.constraint(filter * (new_offset - virt - len)); + yield_constr.constraint(filter * (new_offset[0] - virt - len)); + for &limb in &new_offset[1..] { + yield_constr.constraint(filter * limb); + } } pub(crate) fn eval_ext_circuit, const D: usize>( @@ -38,7 +41,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( // by the 5th bit set to 0. let filter = builder.mul_sub_extension(lv.op.m_op_32bytes, lv.opcode_bits[5], lv.op.m_op_32bytes); - let new_offset = nv.mem_channels[0].value[0]; + let new_offset = nv.mem_channels[0].value; let virt = lv.mem_channels[2].value[0]; // Read len from opcode bits and constrain the pushed new offset. let len_bits = lv.opcode_bits[..5].iter().enumerate().fold( @@ -47,8 +50,12 @@ pub(crate) fn eval_ext_circuit, const D: usize>( builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) }, ); - let diff = builder.sub_extension(new_offset, virt); + let diff = builder.sub_extension(new_offset[0], virt); let diff = builder.sub_extension(diff, len_bits); let constr = builder.mul_sub_extension(filter, diff, filter); yield_constr.constraint(builder, constr); + for &limb in &new_offset[1..] { + let constr = builder.mul_extension(filter, limb); + yield_constr.constraint(builder, constr); + } } From 2c5347c45f07f31cdfd7a183cdf5f9873d5c32e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Fri, 15 Dec 2023 09:49:19 +0100 Subject: [PATCH 022/175] Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- evm/src/cpu/kernel/asm/core/call.asm | 1 - evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 10 +++++----- evm/src/cpu/kernel/interpreter.rs | 2 +- evm/src/generation/mod.rs | 4 ++-- evm/src/generation/prover_input.rs | 14 +++++++------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 46765954c4..5173d35822 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -370,7 +370,6 @@ call_too_deep: GET_CONTEXT // stack: ctx, code_size, retdest %validate_jumpdest_table - PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 76c25fa07d..79475b3789 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -122,9 +122,9 @@ global is_jumpdest: //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest - %eq_const(0) + IS_ZERO %jumpi(verify_path) - //stack: proof_prefix_addr, ctx, jumpdest, retdest + // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, // or larger than 127 @@ -141,7 +141,7 @@ global is_jumpdest: %jump(verify_path) return_is_jumpdest: - //stack: proof_prefix_addr, jumpdest, ctx, retdest + // stack: proof_prefix_addr, jumpdest, ctx, retdest %pop3 JUMP @@ -187,7 +187,7 @@ global validate_jumpdest_table: // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) DUP1 %jumpi(check_proof) - // If proof == 0 there are no more jump destionations to check + // If proof == 0 there are no more jump destinations to check POP // This is just a hook used for avoiding verification of the jumpdest // table in another contexts. It is useful during proof generation, @@ -196,7 +196,7 @@ global validate_jumpdest_table_end: POP JUMP check_proof: - %sub_const(1) + %decrement DUP2 DUP2 // stack: address, ctx, address, ctx // We read the proof diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index a16d2d3af8..9b66b5de19 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -303,7 +303,7 @@ impl<'a> Interpreter<'a> { ) } - fn incr(&mut self, n: usize) { + const fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 71176da518..9af830d4a2 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -293,7 +293,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( state.registers.program_counter = KERNEL.global_labels[initial_label]; let context = state.registers.context; - log::debug!("Simulating CPU for jumpdest analysis "); + log::debug!("Simulating CPU for jumpdest analysis."); loop { if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { @@ -317,7 +317,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( { // TODO: hotfix for avoiding deeper calls to abort let jumpdest = u256_to_usize(state.registers.stack_top) - .map_err(|_| anyhow::Error::msg("Not a valid jump destination"))?; + .map_err(|_| anyhow!("Not a valid jump destination"))?; state.memory.set( MemoryAddress { context: state.registers.context, diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 53e25db4b2..1808f3f3b2 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -274,7 +274,7 @@ impl GenerationState { } } - /// Return the proof for the last jump adddress + /// Returns the proof for the last jump address. fn run_next_jumpdest_table_proof(&mut self) -> Result { let code = (0..self.last_jumpdest_address) .map(|i| { @@ -286,12 +286,12 @@ impl GenerationState { }) .collect::, _>>()?; - // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem + // TODO: The proof searching algorithm is not very efficient. But luckily it doesn't seem // a problem as is done natively. - // Search the closest address to last_jumpdest_address for which none of + // Search the closest address to `last_jumpdest_address` for which none of // the previous 32 bytes in the code (including opcodes and pushed bytes) - // are PUSHXX and the address is in its range + // are PUSHXX and the address is in its range. let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( 0, @@ -340,9 +340,9 @@ impl GenerationState { }) .collect::, _>>()?; - // We need to set the the simulated jumpdest bits to one as otherwise - // the simulation will fail - let mut jumpdest_table = vec![]; + // We need to set the simulated jumpdest bits to one as otherwise + // the simulation will fail. + let mut jumpdest_table = Vec::with_capacity(code.len()); for (pos, opcode) in CodeIterator::new(&code) { jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); if opcode == get_opcode("JUMPDEST") { From fdd7ee46fe735186b00a7090ead9ff1ae660f14d Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Fri, 15 Dec 2023 15:49:34 +0100 Subject: [PATCH 023/175] fix: make `from_noncanonical_biguint` work for zero (#1427) --- field/src/goldilocks_field.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/field/src/goldilocks_field.rs b/field/src/goldilocks_field.rs index 94db5b6b66..36c6aad24f 100644 --- a/field/src/goldilocks_field.rs +++ b/field/src/goldilocks_field.rs @@ -3,7 +3,7 @@ use core::hash::{Hash, Hasher}; use core::iter::{Product, Sum}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use num::{BigUint, Integer}; +use num::{BigUint, Integer, ToPrimitive}; use plonky2_util::{assume, branch_hint}; use serde::{Deserialize, Serialize}; @@ -147,7 +147,7 @@ impl Field for GoldilocksField { } fn from_noncanonical_biguint(n: BigUint) -> Self { - Self(n.mod_floor(&Self::order()).to_u64_digits()[0]) + Self(n.mod_floor(&Self::order()).to_u64().unwrap()) } #[inline(always)] From 81f13f3f8a1aa60d0b2ab4caa8eacc871bee1eed Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 17:11:00 +0100 Subject: [PATCH 024/175] Eliminate nested simulations --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 7 +- evm/src/cpu/kernel/interpreter.rs | 22 ++-- evm/src/generation/mod.rs | 109 ++++++++++-------- evm/src/generation/prover_input.rs | 45 ++++---- evm/src/generation/state.rs | 4 +- evm/src/witness/transition.rs | 7 +- 6 files changed, 103 insertions(+), 91 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 79475b3789..cfc3575b14 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -114,15 +114,12 @@ global is_jumpdest: MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - // Slightly more efficient than `%eq_const(0x5b) ISZERO` - PUSH 0x5b - SUB - %jumpi(panic) + %assert_eq_const(0x5b) //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest - IS_ZERO + ISZERO %jumpi(verify_path) // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 9b66b5de19..f007595a1e 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1,7 +1,7 @@ //! An EVM interpreter for testing and debugging purposes. use core::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::ops::Range; use anyhow::{anyhow, bail, ensure}; @@ -293,17 +293,19 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); - self.generation_state.jumpdest_addresses = Some( - jumpdest_bits - .into_iter() - .enumerate() - .filter(|&(_, x)| x) - .map(|(i, _)| i) - .collect(), - ) + self.generation_state.jumpdest_addresses = Some(HashMap::from([( + context, + BTreeSet::from_iter( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i), + ), + )])); } - const fn incr(&mut self, n: usize) { + pub(crate) fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 9af830d4a2..5163767e10 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use anyhow::anyhow; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; @@ -27,6 +27,7 @@ use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::util::{h2u, u256_to_u8, u256_to_usize}; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -287,56 +288,68 @@ fn simulate_cpu_between_labels_and_get_user_jumps( initial_label: &str, final_label: &str, state: &mut GenerationState, -) -> anyhow::Result> { - let halt_pc = KERNEL.global_labels[final_label]; - let mut jumpdest_addresses = HashSet::new(); - state.registers.program_counter = KERNEL.global_labels[initial_label]; - let context = state.registers.context; +) -> Result<(), ProgramError> { + if let Some(_) = state.jumpdest_addresses { + Ok(()) + } else { + const JUMP_OPCODE: u8 = 0x56; + const JUMPI_OPCODE: u8 = 0x57; - log::debug!("Simulating CPU for jumpdest analysis."); + let halt_pc = KERNEL.global_labels[final_label]; + let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); - loop { - if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { - state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] - } - let pc = state.registers.program_counter; - let halt = state.registers.is_kernel && pc == halt_pc && state.registers.context == context; - let opcode = u256_to_u8(state.memory.get(MemoryAddress { - context: state.registers.context, - segment: Segment::Code as usize, - virt: state.registers.program_counter, - })) - .map_err(|_| anyhow::Error::msg("Invalid opcode."))?; - let cond = if let Ok(cond) = stack_peek(state, 1) { - cond != U256::zero() - } else { - false - }; - if !state.registers.is_kernel - && (opcode == get_opcode("JUMP") || (opcode == get_opcode("JUMPI") && cond)) - { - // TODO: hotfix for avoiding deeper calls to abort - let jumpdest = u256_to_usize(state.registers.stack_top) - .map_err(|_| anyhow!("Not a valid jump destination"))?; - state.memory.set( - MemoryAddress { - context: state.registers.context, - segment: Segment::JumpdestBits as usize, - virt: jumpdest, - }, - U256::one(), - ); - if (state.registers.context == context) { - jumpdest_addresses.insert(jumpdest); + state.registers.program_counter = KERNEL.global_labels[initial_label]; + let initial_context = state.registers.context; + + log::debug!("Simulating CPU for jumpdest analysis."); + + loop { + if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { + state.registers.program_counter = + KERNEL.global_labels["validate_jumpdest_table_end"] } + let pc = state.registers.program_counter; + let context = state.registers.context; + let halt = state.registers.is_kernel + && pc == halt_pc + && state.registers.context == initial_context; + let opcode = u256_to_u8(state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: state.registers.program_counter, + }))?; + let cond = if let Ok(cond) = stack_peek(state, 1) { + cond != U256::zero() + } else { + false + }; + if !state.registers.is_kernel + && (opcode == JUMP_OPCODE || (opcode == JUMPI_OPCODE && cond)) + { + // Avoid deeper calls to abort + let jumpdest = u256_to_usize(state.registers.stack_top)?; + state.memory.set( + MemoryAddress { + context, + segment: Segment::JumpdestBits as usize, + virt: jumpdest, + }, + U256::one(), + ); + if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { + ctx_addresses.insert(jumpdest); + } else { + jumpdest_addresses.insert(context, BTreeSet::from([jumpdest])); + } + } + if halt { + log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + state.jumpdest_addresses = Some(jumpdest_addresses); + return Ok(()); + } + transition(state).map_err(|_| { + ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation) + })?; } - if halt { - log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - let mut jumpdest_addresses: Vec = jumpdest_addresses.into_iter().collect(); - jumpdest_addresses.sort(); - return Ok(jumpdest_addresses); - } - - transition(state)?; } } diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 1808f3f3b2..35571dcb43 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -16,7 +16,6 @@ use serde::{Deserialize, Serialize}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, @@ -250,8 +249,9 @@ impl GenerationState { } /// Return the next used jump addres fn run_next_jumpdest_table_address(&mut self) -> Result { + let context = self.registers.context; let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, + context, segment: Segment::ContextMetadata as usize, virt: ContextMetadata::CodeSize as usize, }))?; @@ -260,14 +260,14 @@ impl GenerationState { self.generate_jumpdest_table()?; } - let Some(jumpdest_table) = &mut self.jumpdest_addresses else { - // TODO: Add another error + let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); }; - if let Some(next_jumpdest_address) = jumpdest_table.pop() { - self.last_jumpdest_address = next_jumpdest_address; - Ok((next_jumpdest_address + 1).into()) + if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + { + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) } else { self.jumpdest_addresses = None; Ok(U256::zero()) @@ -293,6 +293,9 @@ impl GenerationState { // the previous 32 bytes in the code (including opcodes and pushed bytes) // are PUSHXX and the address is in its range. + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x7f; + let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( 0, |acc, (pos, opcode)| { @@ -300,9 +303,9 @@ impl GenerationState { code[prefix_start..pos].iter().enumerate().fold( true, |acc, (prefix_pos, &byte)| { - acc && (byte > get_push_opcode(32) + acc && (byte > PUSH32_OPCODE || (prefix_start + prefix_pos) as i32 - + (byte as i32 - get_push_opcode(1) as i32) + + (byte as i32 - PUSH1_OPCODE as i32) + 1 < pos as i32) }, @@ -323,6 +326,7 @@ impl GenerationState { impl GenerationState { fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { + const JUMPDEST_OPCODE: u8 = 0x5b; let mut state = self.soft_clone(); let code_len = u256_to_usize(self.memory.get(MemoryAddress { context: self.registers.context, @@ -344,8 +348,8 @@ impl GenerationState { // the simulation will fail. let mut jumpdest_table = Vec::with_capacity(code.len()); for (pos, opcode) in CodeIterator::new(&code) { - jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); - if opcode == get_opcode("JUMPDEST") { + jumpdest_table.push((pos, opcode == JUMPDEST_OPCODE)); + if opcode == JUMPDEST_OPCODE { state.memory.set( MemoryAddress { context: state.registers.context, @@ -358,32 +362,31 @@ impl GenerationState { } // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + simulate_cpu_between_labels_and_get_user_jumps( "validate_jumpdest_table_end", "terminate_common", &mut state, - ) - .ok(); - + )?; + self.jumpdest_addresses = state.jumpdest_addresses; Ok(()) } } struct CodeIterator<'a> { - code: &'a Vec, + code: &'a [u8], pos: usize, end: usize, } impl<'a> CodeIterator<'a> { - fn new(code: &'a Vec) -> Self { + fn new(code: &'a [u8]) -> Self { CodeIterator { end: code.len(), code, pos: 0, } } - fn until(code: &'a Vec, end: usize) -> Self { + fn until(code: &'a [u8], end: usize) -> Self { CodeIterator { end: std::cmp::min(code.len(), end), code, @@ -396,14 +399,16 @@ impl<'a> Iterator for CodeIterator<'a> { type Item = (usize, u8); fn next(&mut self) -> Option { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x70; let CodeIterator { code, pos, end } = self; if *pos >= *end { return None; } let opcode = code[*pos]; let old_pos = *pos; - *pos += if opcode >= get_push_opcode(1) && opcode <= get_push_opcode(32) { - (opcode - get_push_opcode(1) + 2).into() + *pos += if opcode >= PUSH1_OPCODE && opcode <= PUSH32_OPCODE { + (opcode - PUSH1_OPCODE + 2).into() } else { 1 }; diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 79dd94fb2f..de07c942a3 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; @@ -52,7 +52,7 @@ pub(crate) struct GenerationState { pub(crate) trie_root_ptrs: TrieRootPtrs, pub(crate) last_jumpdest_address: usize, - pub(crate) jumpdest_addresses: Option>, + pub(crate) jumpdest_addresses: Option>>, } impl GenerationState { diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 046885439e..cf2e3bbeba 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,12 +395,7 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!( - "User instruction: {:?} ctx = {:?} stack = {:?}", - op, - state.registers.context, - state.stack() - ); + log::debug!("User instruction: {:?}", op); } fill_op_flag(op, &mut row); From ad8c2df84a3584d187b17053a87b14c491849d60 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 17:13:52 +0100 Subject: [PATCH 025/175] Remove U256::as_u8 in comment --- evm/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/util.rs b/evm/src/util.rs index 7a635c0e1d..43545a6b48 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,7 +70,7 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } -/// Safe alternative to `U256::as_u8()`, which errors in case of overflow instead of panicking. +/// Safe conversion from U256 to u8, which errors in case of overflow instead of panicking. pub(crate) fn u256_to_u8(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } From 5a0c1ad8b78ee87e478e6918b732345a50b7213d Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 18:14:47 +0100 Subject: [PATCH 026/175] Fix fmt --- evm/src/generation/prover_input.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 35571dcb43..3a298e8179 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -261,13 +261,16 @@ impl GenerationState { } let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { - return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); }; - if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) + && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() { - self.last_jumpdest_address = next_jumpdest_address; - Ok((next_jumpdest_address + 1).into()) + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) } else { self.jumpdest_addresses = None; Ok(U256::zero()) From 77f1cd34968f98eecdb6e142db5b16c6ea986f33 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 18:52:40 +0100 Subject: [PATCH 027/175] Clippy --- evm/src/generation/mod.rs | 2 +- evm/src/generation/prover_input.rs | 2 +- evm/src/generation/state.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 5163767e10..a49e291d63 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -289,7 +289,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( final_label: &str, state: &mut GenerationState, ) -> Result<(), ProgramError> { - if let Some(_) = state.jumpdest_addresses { + if state.jumpdest_addresses.is_some() { Ok(()) } else { const JUMP_OPCODE: u8 = 0x56; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 3a298e8179..926b876da5 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -410,7 +410,7 @@ impl<'a> Iterator for CodeIterator<'a> { } let opcode = code[*pos]; let old_pos = *pos; - *pos += if opcode >= PUSH1_OPCODE && opcode <= PUSH32_OPCODE { + *pos += if (PUSH1_OPCODE..=PUSH32_OPCODE).contains(&opcode) { (opcode - PUSH1_OPCODE + 2).into() } else { 1 diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index de07c942a3..1c50cc294c 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -177,7 +177,7 @@ impl GenerationState { pub(crate) fn soft_clone(&self) -> GenerationState { Self { inputs: self.inputs.clone(), - registers: self.registers.clone(), + registers: self.registers, memory: self.memory.clone(), traces: Traces::default(), rlp_prover_inputs: self.rlp_prover_inputs.clone(), From a64311cfd455ea5805e0216adc9d6435268e6048 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 15 Dec 2023 19:35:27 +0100 Subject: [PATCH 028/175] Add aborting signal (#1429) * Add aborting signal * Clippy * Update to Option following comment --- evm/src/fixed_recursive_verifier.rs | 15 +++++++-- evm/src/generation/mod.rs | 3 ++ evm/src/keccak/keccak_stark.rs | 1 + evm/src/prover.rs | 50 +++++++++++++++++++++++++++-- evm/tests/add11_yml.rs | 2 +- evm/tests/basic_smart_contract.rs | 2 +- evm/tests/empty_txn_list.rs | 4 +-- evm/tests/erc20.rs | 2 +- evm/tests/log_opcode.rs | 8 ++--- evm/tests/self_balance_gas_cost.rs | 2 +- evm/tests/selfdestruct.rs | 2 +- evm/tests/simple_transfer.rs | 2 +- evm/tests/withdrawals.rs | 2 +- 13 files changed, 77 insertions(+), 18 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 87f076c1b5..063f479a28 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -1,6 +1,8 @@ use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use hashbrown::HashMap; @@ -39,7 +41,7 @@ use crate::proof::{ BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, }; -use crate::prover::prove; +use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ add_common_recursion_gates, add_virtual_public_values, get_memory_extra_looking_sum_circuit, recursive_stark_circuit, set_public_value_targets, PlonkWrapperCircuit, PublicInputs, @@ -967,8 +969,15 @@ where config: &StarkConfig, generation_inputs: GenerationInputs, timing: &mut TimingTree, + abort_signal: Option>, ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { - let all_proof = prove::(all_stark, config, generation_inputs, timing)?; + let all_proof = prove::( + all_stark, + config, + generation_inputs, + timing, + abort_signal.clone(), + )?; let mut root_inputs = PartialWitness::new(); for table in 0..NUM_TABLES { @@ -996,6 +1005,8 @@ where F::from_canonical_usize(index_verifier_data), ); root_inputs.set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof); + + check_abort_signal(abort_signal.clone())?; } root_inputs.set_verifier_data_target( diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 77b6fd36d7..f692ae399e 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,4 +1,6 @@ use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; use anyhow::anyhow; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; @@ -23,6 +25,7 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; +use crate::prover::check_abort_signal; use crate::util::h2u; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 18ba3a5f27..3e1bf19262 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -765,6 +765,7 @@ mod tests { }, &mut Challenger::new(), &mut timing, + None, )?; timing.print(); diff --git a/evm/src/prover.rs b/evm/src/prover.rs index ab33a66103..32989c8fc0 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -1,4 +1,7 @@ -use anyhow::{ensure, Result}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +use anyhow::{anyhow, ensure, Result}; use itertools::Itertools; use once_cell::sync::Lazy; use plonky2::field::extension::Extendable; @@ -32,6 +35,7 @@ use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; +use crate::witness::errors::ProgramError; #[cfg(test)] use crate::{ cross_table_lookup::testutils::check_ctls, verifier::testutils::get_memory_extra_looking_values, @@ -43,6 +47,7 @@ pub fn prove( config: &StarkConfig, inputs: GenerationInputs, timing: &mut TimingTree, + abort_signal: Option>, ) -> Result> where F: RichField + Extendable, @@ -54,7 +59,16 @@ where "generate all traces", generate_traces(all_stark, inputs, config, timing)? ); - let proof = prove_with_traces(all_stark, config, traces, public_values, timing)?; + check_abort_signal(abort_signal.clone())?; + + let proof = prove_with_traces( + all_stark, + config, + traces, + public_values, + timing, + abort_signal, + )?; Ok(proof) } @@ -65,6 +79,7 @@ pub(crate) fn prove_with_traces( trace_poly_values: [Vec>; NUM_TABLES], public_values: PublicValues, timing: &mut TimingTree, + abort_signal: Option>, ) -> Result> where F: RichField + Extendable, @@ -136,7 +151,8 @@ where ctl_data_per_table, &mut challenger, &ctl_challenges, - timing + timing, + abort_signal, )? ); @@ -172,6 +188,7 @@ fn prove_with_commitments( challenger: &mut Challenger, ctl_challenges: &GrandProductChallengeSet, timing: &mut TimingTree, + abort_signal: Option>, ) -> Result<[StarkProofWithMetadata; NUM_TABLES]> where F: RichField + Extendable, @@ -189,6 +206,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let byte_packing_proof = timed!( @@ -203,6 +221,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let cpu_proof = timed!( @@ -217,6 +236,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let keccak_proof = timed!( @@ -231,6 +251,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let keccak_sponge_proof = timed!( @@ -245,6 +266,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let logic_proof = timed!( @@ -259,6 +281,7 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), )? ); let memory_proof = timed!( @@ -273,6 +296,7 @@ where ctl_challenges, challenger, timing, + abort_signal, )? ); @@ -300,12 +324,15 @@ pub(crate) fn prove_single_table( ctl_challenges: &GrandProductChallengeSet, challenger: &mut Challenger, timing: &mut TimingTree, + abort_signal: Option>, ) -> Result> where F: RichField + Extendable, C: GenericConfig, S: Stark, { + check_abort_signal(abort_signal.clone())?; + let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); let fri_params = config.fri_params(degree_bits); @@ -392,6 +419,8 @@ where ); } + check_abort_signal(abort_signal.clone())?; + let quotient_polys = timed!( timing, "compute quotient polys", @@ -469,6 +498,8 @@ where "ient_commitment, ]; + check_abort_signal(abort_signal.clone())?; + let opening_proof = timed!( timing, "compute openings proof", @@ -636,6 +667,19 @@ where .collect() } +/// Utility method that checks whether a kill signal has been emitted by one of the workers, +/// which will result in an early abort for all the other processes involved in the same set +/// of transactions. +pub(crate) fn check_abort_signal(abort_signal: Option>) -> Result<()> { + if let Some(signal) = abort_signal { + if signal.load(Ordering::Relaxed) { + return Err(anyhow!("Stopping job from abort signal.")); + } + } + + Ok(()) +} + #[cfg(test)] /// Check that all constraints evaluate to zero on `H`. /// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index 040750fa58..6a15dfc06d 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -168,7 +168,7 @@ fn add11_yml() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index 28db58ed54..7d07ca19ac 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -200,7 +200,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 684a8f3625..16486677ef 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -114,7 +114,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let mut timing = TimingTree::new("prove", log::Level::Info); // We're missing some preprocessed circuits. assert!(all_circuits - .prove_root(&all_stark, &config, inputs.clone(), &mut timing) + .prove_root(&all_stark, &config, inputs.clone(), &mut timing, None) .is_err()); // Expand the preprocessed circuits. @@ -127,7 +127,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let mut timing = TimingTree::new("prove", log::Level::Info); let (root_proof, public_values) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); all_circuits.verify_root(root_proof.clone())?; diff --git a/evm/tests/erc20.rs b/evm/tests/erc20.rs index 9c4bfa8353..48d0d75364 100644 --- a/evm/tests/erc20.rs +++ b/evm/tests/erc20.rs @@ -176,7 +176,7 @@ fn test_erc20() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index b00fe36f9e..157b9fe6ad 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -234,7 +234,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); // Assert that the proof leads to the correct state and receipt roots. @@ -450,7 +450,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { let mut timing = TimingTree::new("prove root first", log::Level::Info); let (root_proof_first, public_values_first) = - all_circuits.prove_root(&all_stark, &config, inputs_first, &mut timing)?; + all_circuits.prove_root(&all_stark, &config, inputs_first, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); all_circuits.verify_root(root_proof_first.clone())?; @@ -570,7 +570,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { let mut timing = TimingTree::new("prove root second", log::Level::Info); let (root_proof_second, public_values_second) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None.clone())?; timing.filter(Duration::from_millis(100)).print(); all_circuits.verify_root(root_proof_second.clone())?; @@ -635,7 +635,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { }; let (root_proof, public_values) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None)?; all_circuits.verify_root(root_proof.clone())?; // We can just duplicate the initial proof as the state didn't change. diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index 7346dc2487..538f2aa798 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -187,7 +187,7 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/selfdestruct.rs b/evm/tests/selfdestruct.rs index fb33b18fb2..829e0b21b0 100644 --- a/evm/tests/selfdestruct.rs +++ b/evm/tests/selfdestruct.rs @@ -139,7 +139,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index ede18bf809..5fd252df45 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -155,7 +155,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) diff --git a/evm/tests/withdrawals.rs b/evm/tests/withdrawals.rs index 29c958170a..ef2d19b02a 100644 --- a/evm/tests/withdrawals.rs +++ b/evm/tests/withdrawals.rs @@ -85,7 +85,7 @@ fn test_withdrawals() -> anyhow::Result<()> { }; let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); verify_proof(&all_stark, proof, &config) From 68b9f0ad1c288e6415f19b8802a395bc7205db16 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Fri, 15 Dec 2023 19:44:59 -0500 Subject: [PATCH 029/175] Add ERC721 test (#1425) * Add ERC721 test * Add IS_READ column to BytePacking CTL * Apply comment --- evm/src/byte_packing/byte_packing_stark.rs | 2 +- evm/src/cpu/cpu_stark.rs | 11 +- evm/tests/erc721.rs | 314 +++++++++++++++++++++ 3 files changed, 323 insertions(+), 4 deletions(-) create mode 100644 evm/tests/erc721.rs diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index c3cfea71e5..e5596b3e2c 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -76,7 +76,7 @@ pub(crate) fn ctl_looked_data() -> Vec> { (0..NUM_BYTES).map(|i| (index_len(i), F::from_canonical_usize(i + 1))), ); - Column::singles([ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) + Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) .chain([sequence_len]) .chain(Column::singles(&[TIMESTAMP])) .chain(outputs) diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index d4c663196f..c93f1b8770 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -127,7 +127,9 @@ pub(crate) fn ctl_arithmetic_base_rows() -> TableWithColumns { /// Creates the vector of `Columns` corresponding to the contents of General Purpose channels when calling byte packing. /// We use `ctl_data_keccak_sponge` because the `Columns` are the same as the ones computed for `KeccakSpongeStark`. pub(crate) fn ctl_data_byte_packing() -> Vec> { - ctl_data_keccak_sponge() + let mut res = vec![Column::constant(F::ONE)]; // is_read + res.extend(ctl_data_keccak_sponge()); + res } /// CTL filter for the `MLOAD_32BYTES` operation. @@ -144,6 +146,8 @@ pub(crate) fn ctl_filter_byte_packing() -> Filter { /// Creates the vector of `Columns` corresponding to the contents of General Purpose channels when calling byte unpacking. pub(crate) fn ctl_data_byte_unpacking() -> Vec> { + let is_read = Column::constant(F::ZERO); + // When executing MSTORE_32BYTES, the GP memory channels are used as follows: // GP channel 0: stack[-1] = context // GP channel 1: stack[-2] = segment @@ -165,7 +169,7 @@ pub(crate) fn ctl_data_byte_unpacking() -> Vec> { let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - let mut res = vec![context, segment, virt, len, timestamp]; + let mut res = vec![is_read, context, segment, virt, len, timestamp]; res.extend(val); res @@ -186,6 +190,7 @@ pub(crate) fn ctl_filter_byte_unpacking() -> Filter { /// Creates the vector of `Columns` corresponding to the contents of the CPU registers when performing a `PUSH`. /// `PUSH` internal reads are done by calling `BytePackingStark`. pub(crate) fn ctl_data_byte_packing_push() -> Vec> { + let is_read = Column::constant(F::ONE); let context = Column::single(COL_MAP.code_context); let segment = Column::constant(F::from_canonical_usize(Segment::Code as usize)); // The initial offset if `pc + 1`. @@ -199,7 +204,7 @@ pub(crate) fn ctl_data_byte_packing_push() -> Vec> { let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - let mut res = vec![context, segment, virt, len, timestamp]; + let mut res = vec![is_read, context, segment, virt, len, timestamp]; res.extend(val); res diff --git a/evm/tests/erc721.rs b/evm/tests/erc721.rs new file mode 100644 index 0000000000..0c6d50d836 --- /dev/null +++ b/evm/tests/erc721.rs @@ -0,0 +1,314 @@ +use std::str::FromStr; +use std::time::Duration; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use eth_trie_utils::nibbles::Nibbles; +use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; +use hex_literal::hex; +use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::KeccakGoldilocksConfig; +use plonky2::util::timing::TimingTree; +use plonky2_evm::all_stark::AllStark; +use plonky2_evm::config::StarkConfig; +use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; +use plonky2_evm::generation::{GenerationInputs, TrieInputs}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; +use plonky2_evm::prover::prove; +use plonky2_evm::verifier::verify_proof; +use plonky2_evm::Node; + +type F = GoldilocksField; +const D: usize = 2; +type C = KeccakGoldilocksConfig; + +/// Test a simple ERC721 token transfer. +/// Used the following Solidity code: +/// ```solidity +/// pragma solidity ^0.8.20; +/// +/// import "@openzeppelin/contracts@5.0.1/token/ERC721/ERC721.sol"; +/// import "@openzeppelin/contracts@5.0.1/access/Ownable.sol"; +/// +/// contract TestToken is ERC721, Ownable { +/// constructor(address initialOwner) +/// ERC721("TestToken", "TEST") +/// Ownable(initialOwner) +/// {} +/// +/// function safeMint(address to, uint256 tokenId) public onlyOwner { +/// _safeMint(to, tokenId); +/// } +/// } +/// ``` +/// +/// The transaction calls the `safeTransferFrom` function to transfer token `1337` from address +/// `0x5B38Da6a701c568545dCfcB03FcB875f56beddC4` to address `0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2`. +#[test] +fn test_erc721() -> anyhow::Result<()> { + init_logger(); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let owner = hex!("5B38Da6a701c568545dCfcB03FcB875f56beddC4"); + let contract = hex!("f2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9"); + + let owner_state_key = keccak(owner); + let contract_state_key = keccak(contract); + + let owner_nibbles = Nibbles::from_bytes_be(owner_state_key.as_bytes()).unwrap(); + let contract_nibbles = Nibbles::from_bytes_be(contract_state_key.as_bytes()).unwrap(); + + let mut state_trie_before = HashedPartialTrie::from(Node::Empty); + state_trie_before.insert(owner_nibbles, rlp::encode(&owner_account()).to_vec()); + state_trie_before.insert(contract_nibbles, rlp::encode(&contract_account()).to_vec()); + + let storage_tries = vec![(contract_state_key, contract_storage())]; + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + storage_tries, + }; + + let txn = signed_tx(); + + let gas_used = 58_418.into(); + + let contract_code = [contract_bytecode(), vec![]] + .map(|v| (keccak(v.clone()), v)) + .into(); + + let expected_state_trie_after: HashedPartialTrie = { + let mut state_trie_after = HashedPartialTrie::from(Node::Empty); + let owner_account = owner_account(); + let owner_account_after = AccountRlp { + nonce: owner_account.nonce + 1, + balance: owner_account.balance - gas_used * 0xa, + ..owner_account + }; + state_trie_after.insert(owner_nibbles, rlp::encode(&owner_account_after).to_vec()); + let contract_account_after = AccountRlp { + storage_root: contract_storage_after().hash(), + ..contract_account() + }; + state_trie_after.insert( + contract_nibbles, + rlp::encode(&contract_account_after).to_vec(), + ); + + state_trie_after + }; + + let logs = vec![LogRlp { + address: H160::from_str("0xf2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9").unwrap(), + topics: vec![ + H256::from_str("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") + .unwrap(), + H256::from_str("0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4") + .unwrap(), + H256::from_str("0x000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2") + .unwrap(), + H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000539") + .unwrap(), + ], + data: vec![].into(), + }]; + + let mut bloom_bytes = [0u8; 256]; + add_logs_to_bloom(&mut bloom_bytes, &logs); + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: gas_used, + bloom: bloom_bytes.to_vec().into(), + logs, + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert(Nibbles::from_str("0x80").unwrap(), receipt_0.encode(0)); + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + + let bloom = bloom_bytes + .chunks_exact(32) + .map(U256::from_big_endian) + .collect::>(); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: gas_used, + block_bloom: bloom.try_into().unwrap(), + }; + + let inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; + timing.filter(Duration::from_millis(100)).print(); + + verify_proof(&all_stark, proof, &config) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} + +fn contract_bytecode() -> Vec { + hex!("608060405234801561000f575f80fd5b5060043610610109575f3560e01c8063715018a6116100a0578063a22cb4651161006f578063a22cb465146102a1578063b88d4fde146102bd578063c87b56dd146102d9578063e985e9c514610309578063f2fde38b1461033957610109565b8063715018a61461023f5780638da5cb5b1461024957806395d89b4114610267578063a14481941461028557610109565b806323b872dd116100dc57806323b872dd146101a757806342842e0e146101c35780636352211e146101df57806370a082311461020f57610109565b806301ffc9a71461010d57806306fdde031461013d578063081812fc1461015b578063095ea7b31461018b575b5f80fd5b61012760048036038101906101229190611855565b610355565b604051610134919061189a565b60405180910390f35b610145610436565b604051610152919061193d565b60405180910390f35b61017560048036038101906101709190611990565b6104c5565b60405161018291906119fa565b60405180910390f35b6101a560048036038101906101a09190611a3d565b6104e0565b005b6101c160048036038101906101bc9190611a7b565b6104f6565b005b6101dd60048036038101906101d89190611a7b565b6105f5565b005b6101f960048036038101906101f49190611990565b610614565b60405161020691906119fa565b60405180910390f35b61022960048036038101906102249190611acb565b610625565b6040516102369190611b05565b60405180910390f35b6102476106db565b005b6102516106ee565b60405161025e91906119fa565b60405180910390f35b61026f610716565b60405161027c919061193d565b60405180910390f35b61029f600480360381019061029a9190611a3d565b6107a6565b005b6102bb60048036038101906102b69190611b48565b6107bc565b005b6102d760048036038101906102d29190611cb2565b6107d2565b005b6102f360048036038101906102ee9190611990565b6107ef565b604051610300919061193d565b60405180910390f35b610323600480360381019061031e9190611d32565b610855565b604051610330919061189a565b60405180910390f35b610353600480360381019061034e9190611acb565b6108e3565b005b5f7f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061041f57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061042f575061042e82610967565b5b9050919050565b60605f805461044490611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461047090611d9d565b80156104bb5780601f10610492576101008083540402835291602001916104bb565b820191905f5260205f20905b81548152906001019060200180831161049e57829003601f168201915b5050505050905090565b5f6104cf826109d0565b506104d982610a56565b9050919050565b6104f282826104ed610a8f565b610a96565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610566575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161055d91906119fa565b60405180910390fd5b5f6105798383610574610a8f565b610aa8565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146105ef578382826040517f64283d7b0000000000000000000000000000000000000000000000000000000081526004016105e693929190611dcd565b60405180910390fd5b50505050565b61060f83838360405180602001604052805f8152506107d2565b505050565b5f61061e826109d0565b9050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610696575f6040517f89c62b6400000000000000000000000000000000000000000000000000000000815260040161068d91906119fa565b60405180910390fd5b60035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b6106e3610cb3565b6106ec5f610d3a565b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606001805461072590611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461075190611d9d565b801561079c5780601f106107735761010080835404028352916020019161079c565b820191905f5260205f20905b81548152906001019060200180831161077f57829003601f168201915b5050505050905090565b6107ae610cb3565b6107b88282610dfd565b5050565b6107ce6107c7610a8f565b8383610e1a565b5050565b6107dd8484846104f6565b6107e984848484610f83565b50505050565b60606107fa826109d0565b505f610804611135565b90505f8151116108225760405180602001604052805f81525061084d565b8061082c8461114b565b60405160200161083d929190611e3c565b6040516020818303038152906040525b915050919050565b5f60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b6108eb610cb3565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361095b575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161095291906119fa565b60405180910390fd5b61096481610d3a565b50565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f806109db83611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610a4d57826040517f7e273289000000000000000000000000000000000000000000000000000000008152600401610a449190611b05565b60405180910390fd5b80915050919050565b5f60045f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f33905090565b610aa3838383600161124e565b505050565b5f80610ab384611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614610af457610af381848661140d565b5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610b7f57610b335f855f8061124e565b600160035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825403925050819055505b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610bfe57600160035f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8460025f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4809150509392505050565b610cbb610a8f565b73ffffffffffffffffffffffffffffffffffffffff16610cd96106ee565b73ffffffffffffffffffffffffffffffffffffffff1614610d3857610cfc610a8f565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401610d2f91906119fa565b60405180910390fd5b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160065f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610e16828260405180602001604052805f8152506114d0565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610e8a57816040517f5b08ba18000000000000000000000000000000000000000000000000000000008152600401610e8191906119fa565b60405180910390fd5b8060055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610f76919061189a565b60405180910390a3505050565b5f8373ffffffffffffffffffffffffffffffffffffffff163b111561112f578273ffffffffffffffffffffffffffffffffffffffff1663150b7a02610fc6610a8f565b8685856040518563ffffffff1660e01b8152600401610fe89493929190611eb1565b6020604051808303815f875af192505050801561102357506040513d601f19601f820116820180604052508101906110209190611f0f565b60015b6110a4573d805f8114611051576040519150601f19603f3d011682016040523d82523d5f602084013e611056565b606091505b505f81510361109c57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161109391906119fa565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461112d57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161112491906119fa565b60405180910390fd5b505b50505050565b606060405180602001604052805f815250905090565b60605f6001611159846114eb565b0190505f8167ffffffffffffffff81111561117757611176611b8e565b5b6040519080825280601f01601f1916602001820160405280156111a95781602001600182028036833780820191505090505b5090505f82602001820190505b60011561120a578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816111ff576111fe611f3a565b5b0494505f85036111b6575b819350505050919050565b5f60025f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b808061128657505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156113b8575f611295846109d0565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156112ff57508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561131257506113108184610855565b155b1561135457826040517fa9fbf51f00000000000000000000000000000000000000000000000000000000815260040161134b91906119fa565b60405180910390fd5b81156113b657838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b8360045f8581526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b61141883838361163c565b6114cb575f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361148c57806040517f7e2732890000000000000000000000000000000000000000000000000000000081526004016114839190611b05565b60405180910390fd5b81816040517f177e802f0000000000000000000000000000000000000000000000000000000081526004016114c2929190611f67565b60405180910390fd5b505050565b6114da83836116fc565b6114e65f848484610f83565b505050565b5f805f90507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611547577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161153d5761153c611f3a565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310611584576d04ee2d6d415b85acef8100000000838161157a57611579611f3a565b5b0492506020810190505b662386f26fc1000083106115b357662386f26fc1000083816115a9576115a8611f3a565b5b0492506010810190505b6305f5e10083106115dc576305f5e10083816115d2576115d1611f3a565b5b0492506008810190505b61271083106116015761271083816115f7576115f6611f3a565b5b0492506004810190505b60648310611624576064838161161a57611619611f3a565b5b0492506002810190505b600a8310611633576001810190505b80915050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156116f357508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806116b457506116b38484610855565b5b806116f257508273ffffffffffffffffffffffffffffffffffffffff166116da83610a56565b73ffffffffffffffffffffffffffffffffffffffff16145b5b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361176c575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161176391906119fa565b60405180910390fd5b5f61177883835f610aa8565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146117ea575f6040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081526004016117e191906119fa565b60405180910390fd5b505050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61183481611800565b811461183e575f80fd5b50565b5f8135905061184f8161182b565b92915050565b5f6020828403121561186a576118696117f8565b5b5f61187784828501611841565b91505092915050565b5f8115159050919050565b61189481611880565b82525050565b5f6020820190506118ad5f83018461188b565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156118ea5780820151818401526020810190506118cf565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61190f826118b3565b61191981856118bd565b93506119298185602086016118cd565b611932816118f5565b840191505092915050565b5f6020820190508181035f8301526119558184611905565b905092915050565b5f819050919050565b61196f8161195d565b8114611979575f80fd5b50565b5f8135905061198a81611966565b92915050565b5f602082840312156119a5576119a46117f8565b5b5f6119b28482850161197c565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6119e4826119bb565b9050919050565b6119f4816119da565b82525050565b5f602082019050611a0d5f8301846119eb565b92915050565b611a1c816119da565b8114611a26575f80fd5b50565b5f81359050611a3781611a13565b92915050565b5f8060408385031215611a5357611a526117f8565b5b5f611a6085828601611a29565b9250506020611a718582860161197c565b9150509250929050565b5f805f60608486031215611a9257611a916117f8565b5b5f611a9f86828701611a29565b9350506020611ab086828701611a29565b9250506040611ac18682870161197c565b9150509250925092565b5f60208284031215611ae057611adf6117f8565b5b5f611aed84828501611a29565b91505092915050565b611aff8161195d565b82525050565b5f602082019050611b185f830184611af6565b92915050565b611b2781611880565b8114611b31575f80fd5b50565b5f81359050611b4281611b1e565b92915050565b5f8060408385031215611b5e57611b5d6117f8565b5b5f611b6b85828601611a29565b9250506020611b7c85828601611b34565b9150509250929050565b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b611bc4826118f5565b810181811067ffffffffffffffff82111715611be357611be2611b8e565b5b80604052505050565b5f611bf56117ef565b9050611c018282611bbb565b919050565b5f67ffffffffffffffff821115611c2057611c1f611b8e565b5b611c29826118f5565b9050602081019050919050565b828183375f83830152505050565b5f611c56611c5184611c06565b611bec565b905082815260208101848484011115611c7257611c71611b8a565b5b611c7d848285611c36565b509392505050565b5f82601f830112611c9957611c98611b86565b5b8135611ca9848260208601611c44565b91505092915050565b5f805f8060808587031215611cca57611cc96117f8565b5b5f611cd787828801611a29565b9450506020611ce887828801611a29565b9350506040611cf98782880161197c565b925050606085013567ffffffffffffffff811115611d1a57611d196117fc565b5b611d2687828801611c85565b91505092959194509250565b5f8060408385031215611d4857611d476117f8565b5b5f611d5585828601611a29565b9250506020611d6685828601611a29565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611db457607f821691505b602082108103611dc757611dc6611d70565b5b50919050565b5f606082019050611de05f8301866119eb565b611ded6020830185611af6565b611dfa60408301846119eb565b949350505050565b5f81905092915050565b5f611e16826118b3565b611e208185611e02565b9350611e308185602086016118cd565b80840191505092915050565b5f611e478285611e0c565b9150611e538284611e0c565b91508190509392505050565b5f81519050919050565b5f82825260208201905092915050565b5f611e8382611e5f565b611e8d8185611e69565b9350611e9d8185602086016118cd565b611ea6816118f5565b840191505092915050565b5f608082019050611ec45f8301876119eb565b611ed160208301866119eb565b611ede6040830185611af6565b8181036060830152611ef08184611e79565b905095945050505050565b5f81519050611f098161182b565b92915050565b5f60208284031215611f2457611f236117f8565b5b5f611f3184828501611efb565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f604082019050611f7a5f8301856119eb565b611f876020830184611af6565b939250505056fea2646970667358221220432b30673e00c0eb009e1718c271f4cfdfbeded17345829703b06d322360990164736f6c63430008160033").into() +} + +fn insert_storage(trie: &mut HashedPartialTrie, slot: U256, value: U256) { + let mut bytes = [0; 32]; + slot.to_big_endian(&mut bytes); + let key = keccak(bytes); + let nibbles = Nibbles::from_bytes_be(key.as_bytes()).unwrap(); + let r = rlp::encode(&value); + let r = r.freeze().to_vec(); + trie.insert(nibbles, r); +} + +fn sd2u(s: &str) -> U256 { + U256::from_dec_str(s).unwrap() +} + +fn sh2u(s: &str) -> U256 { + U256::from_str_radix(s, 16).unwrap() +} + +fn contract_storage() -> HashedPartialTrie { + let mut trie = HashedPartialTrie::from(Node::Empty); + insert_storage( + &mut trie, + U256::zero(), + sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), + ); + insert_storage( + &mut trie, + U256::one(), + sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), + ); + insert_storage( + &mut trie, + sd2u("6"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + insert_storage( + &mut trie, + sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + insert_storage( + &mut trie, + sh2u("0x118c1ea466562cb796e30ef705e4db752f5c39d773d22c5efd8d46f67194e78a"), + sd2u("1"), + ); + trie +} + +fn contract_storage_after() -> HashedPartialTrie { + let mut trie = HashedPartialTrie::from(Node::Empty); + insert_storage( + &mut trie, + U256::zero(), + sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), + ); + insert_storage( + &mut trie, + U256::one(), + sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), + ); + insert_storage( + &mut trie, + sd2u("6"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + insert_storage( + &mut trie, + sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), + sh2u("0xab8483f64d9c6d1ecf9b849ae677dd3315835cb2"), + ); + insert_storage( + &mut trie, + sh2u("0xf3aa6a8a9f7e3707e36cc99c499a27514922afe861ec3d80a1a314409cba92f9"), + sd2u("1"), + ); + trie +} + +fn owner_account() -> AccountRlp { + AccountRlp { + nonce: 2.into(), + balance: 0x1000000.into(), + storage_root: HashedPartialTrie::from(Node::Empty).hash(), + code_hash: keccak([]), + } +} + +fn contract_account() -> AccountRlp { + AccountRlp { + nonce: 0.into(), + balance: 0.into(), + storage_root: contract_storage().hash(), + code_hash: keccak(contract_bytecode()), + } +} + +fn signed_tx() -> Vec { + hex!("f8c5020a8307a12094f2b1114c644cbb3ff63bf1dd284c8cd716e95be980b86442842e0e0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2000000000000000000000000000000000000000000000000000000000000053925a0414867f13ac63d663e84099d52c8215615666ea37c969c69aa58a0fad26a3f6ea01a7160c6274969083b2316eb8ca6011b4bf6b00972159a78bf64d06fa40c1402").into() +} + +fn add_logs_to_bloom(bloom: &mut [u8; 256], logs: &Vec) { + for log in logs { + add_to_bloom(bloom, log.address.as_bytes()); + for topic in &log.topics { + add_to_bloom(bloom, topic.as_bytes()); + } + } +} + +fn add_to_bloom(bloom: &mut [u8; 256], bloom_entry: &[u8]) { + let bloom_hash = keccak(bloom_entry).to_fixed_bytes(); + + for idx in 0..3 { + let bit_pair = u16::from_be_bytes(bloom_hash[2 * idx..2 * (idx + 1)].try_into().unwrap()); + let bit_to_set = 0x07FF - (bit_pair & 0x07FF); + let byte_index = bit_to_set / 8; + let bit_value = 1 << (7 - bit_to_set % 8); + bloom[byte_index as usize] |= bit_value; + } +} From f8f6b07a3905185af302d58fb6b97c55d12e57be Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 16 Dec 2023 17:02:56 +0100 Subject: [PATCH 030/175] Change context to current context for BN precompiles (#1428) * Change context to current for BN precompiles * Rename segments * rustfmt --- .../asm/curve/bn254/curve_arithmetic/curve_mul.asm | 6 +++--- .../asm/curve/bn254/curve_arithmetic/msm.asm | 14 +++++++------- .../bn254/curve_arithmetic/precomputation.asm | 6 +++--- evm/src/cpu/kernel/asm/curve/wnaf.asm | 3 ++- evm/src/cpu/kernel/asm/memory/core.asm | 13 +++++++++++++ evm/src/cpu/kernel/constants/mod.rs | 2 +- evm/src/memory/segments.rs | 6 +++--- 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm index ecbb3de009..93864c5519 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm @@ -27,12 +27,12 @@ bn_mul_valid_point: bn_mul_after_glv: // stack: bneg, a, b, x, y, bn_msm, bn_mul_end, retdest // Store bneg at this (otherwise unused) location. Will be used later in the MSM. - %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q, @BN_BNEG_LOC) + %mstore_current(@SEGMENT_BN_TABLE_Q, @BN_BNEG_LOC) // stack: a, b, x, y, bn_msm, bn_mul_end, retdest - PUSH bn_mul_after_a SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_A PUSH @BN_SCALAR %jump(wnaf) + PUSH bn_mul_after_a SWAP1 PUSH @SEGMENT_BN_WNAF_A PUSH @BN_SCALAR %jump(wnaf) bn_mul_after_a: // stack: b, x, y, bn_msm, bn_mul_end, retdest - PUSH bn_mul_after_b SWAP1 PUSH @SEGMENT_KERNEL_BN_WNAF_B PUSH @BN_SCALAR %jump(wnaf) + PUSH bn_mul_after_b SWAP1 PUSH @SEGMENT_BN_WNAF_B PUSH @BN_SCALAR %jump(wnaf) bn_mul_after_b: // stack: x, y, bn_msm, bn_mul_end, retdest %jump(bn_precompute_table) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm index 1036228737..d5b97312ba 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm @@ -42,31 +42,31 @@ bn_msm_loop_add_b_nonzero: %macro bn_mload_wnaf_a // stack: i - %mload_kernel(@SEGMENT_KERNEL_BN_WNAF_A) + %mload_current(@SEGMENT_BN_WNAF_A) %endmacro %macro bn_mload_wnaf_b // stack: i - %mload_kernel(@SEGMENT_KERNEL_BN_WNAF_B) + %mload_current(@SEGMENT_BN_WNAF_B) %endmacro %macro bn_mload_point_a // stack: w DUP1 - %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + %mload_current(@SEGMENT_BN_TABLE_Q) //stack: Gy, w - SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + SWAP1 %decrement %mload_current(@SEGMENT_BN_TABLE_Q) //stack: Gx, Gy %endmacro %macro bn_mload_point_b // stack: w DUP1 - %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) - PUSH @BN_BNEG_LOC %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + %mload_current(@SEGMENT_BN_TABLE_Q) + PUSH @BN_BNEG_LOC %mload_current(@SEGMENT_BN_TABLE_Q) %stack (bneg, Gy, w) -> (@BN_BASE, Gy, bneg, bneg, Gy, w) SUB SWAP1 ISZERO MUL SWAP2 MUL ADD - SWAP1 %decrement %mload_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + SWAP1 %decrement %mload_current(@SEGMENT_BN_TABLE_Q) //stack: Gx, Gy PUSH @BN_GLV_BETA MULFP254 diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm index a8c6ada926..5ee6685fe6 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm @@ -1,5 +1,5 @@ // Precompute a table of multiples of the BN254 point `Q = (Qx, Qy)`. -// Let `(Qxi, Qyi) = i * Q`, then store in the `SEGMENT_KERNEL_BN_TABLE_Q` segment of memory the values +// Let `(Qxi, Qyi) = i * Q`, then store in the `SEGMENT_BN_TABLE_Q` segment of memory the values // `i-1 => Qxi`, `i => Qyi if i < 16 else -Qy(32-i)` for `i in range(1, 32, 2)`. global bn_precompute_table: // stack: Qx, Qy, retdest @@ -12,14 +12,14 @@ bn_precompute_table_loop: // stack i, Qx2, Qy2, Qx, Qy, retdest PUSH 1 DUP2 SUB %stack (im, i, Qx2, Qy2, Qx, Qy, retdest) -> (i, Qy, im, Qx, i, Qx2, Qy2, Qx, Qy, retdest) - %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + %mstore_current(@SEGMENT_BN_TABLE_Q) %mstore_current(@SEGMENT_BN_TABLE_Q) // stack: i, Qx2, Qy2, Qx, Qy, retdest DUP1 PUSH 32 SUB PUSH 1 DUP2 SUB // stack: 31-i, 32-i, i, Qx2, Qy2, Qx, Qy, retdest DUP7 PUSH @BN_BASE SUB // TODO: Could maybe avoid storing Qx a second time here, not sure if it would be more efficient. %stack (Qyy, iii, ii, i, Qx2, Qy2, Qx, Qy, retdest) -> (iii, Qx, ii, Qyy, i, Qx2, Qy2, Qx, Qy, retdest) - %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) %mstore_kernel(@SEGMENT_KERNEL_BN_TABLE_Q) + %mstore_current(@SEGMENT_BN_TABLE_Q) %mstore_current(@SEGMENT_BN_TABLE_Q) // stack: i, Qx2, Qy2, Qx, Qy, retdest PUSH 2 ADD // stack: i+2, Qx2, Qy2, Qx, Qy, retdest diff --git a/evm/src/cpu/kernel/asm/curve/wnaf.asm b/evm/src/cpu/kernel/asm/curve/wnaf.asm index 674f8479c3..a416d1ba79 100644 --- a/evm/src/cpu/kernel/asm/curve/wnaf.asm +++ b/evm/src/cpu/kernel/asm/curve/wnaf.asm @@ -34,7 +34,8 @@ wnaf_loop_contd: DUP2 SWAP1 SUB %stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest) SUB - %stack (i, m, o, segment, n, retdest) -> (m, 0, segment, i, o, segment, n, retdest) + GET_CONTEXT + %stack (ctx, i, m, o, segment, n, retdest) -> (m, ctx, segment, i, o, segment, n, retdest) MSTORE_GENERAL // stack: o, segment, n, retdest DUP3 ISZERO %jumpi(wnaf_end) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index dcfc12bdea..a4c99cec15 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -107,6 +107,19 @@ // stack: (empty) %endmacro +%macro mstore_current(segment, offset) + // stack: value + PUSH $offset + // stack: offset, value + PUSH $segment + // stack: segment, offset, value + GET_CONTEXT + // stack: context, segment, offset, value + %stack(context, segment, offset, value) -> (value, context, segment, offset) + MSTORE_GENERAL + // stack: (empty) +%endmacro + // Load a single byte from user code. %macro mload_current_code // stack: offset diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 4c2df30888..6e2a0015d3 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -154,7 +154,7 @@ const EC_CONSTANTS: [(&str, [u8; 32]); 20] = [ ), ( "BN_BNEG_LOC", - // This just needs to be large enough to not interfere with anything else in SEGMENT_KERNEL_BN_TABLE_Q. + // This just needs to be large enough to not interfere with anything else in SEGMENT_BN_TABLE_Q. hex!("0000000000000000000000000000000000000000000000000000000000001337"), ), ( diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 51aa8be217..6e67e61942 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -133,9 +133,9 @@ impl Segment { Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", - Segment::BnWnafA => "SEGMENT_KERNEL_BN_WNAF_A", - Segment::BnWnafB => "SEGMENT_KERNEL_BN_WNAF_B", - Segment::BnTableQ => "SEGMENT_KERNEL_BN_TABLE_Q", + Segment::BnWnafA => "SEGMENT_BN_WNAF_A", + Segment::BnWnafB => "SEGMENT_BN_WNAF_B", + Segment::BnTableQ => "SEGMENT_BN_TABLE_Q", Segment::BnPairing => "SEGMENT_KERNEL_BN_PAIRING", Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES", Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS", From 0b56ab75d6d845b9a12db96f39991b7ea3fd9110 Mon Sep 17 00:00:00 2001 From: BGluth Date: Sun, 17 Dec 2023 10:52:33 -0700 Subject: [PATCH 031/175] Added a Discord badge to `README.md` --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab40a5c918..6ee6b82a00 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Plonky2 & more +[![Discord](https://img.shields.io/discord/743511677072572486?logo=discord)](https://discord.gg/QZKRUpqCJ6) This repository was originally for Plonky2, a SNARK implementation based on techniques from PLONK and FRI. It has since expanded to include tools such as Starky, a highly performant STARK implementation. From 536cd1c89c7d5fc546ed4f1b8c9dd653300878ae Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:46:16 +0100 Subject: [PATCH 032/175] Regenerate tries upon Kernel failure during `hash_final_tries` (#1424) * Generate computed tries in case of failure * Only output debug info when hashing final tries * Clippy * Apply comments --- evm/src/generation/mod.rs | 53 ++++++- evm/src/generation/trie_extractor.rs | 204 ++++++++++++++++++++++++++- evm/src/util.rs | 30 ++++ 3 files changed, 284 insertions(+), 3 deletions(-) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index f692ae399e..d691d34e61 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -23,10 +23,11 @@ use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::generation::state::GenerationState; +use crate::generation::trie_extractor::{get_receipt_trie, get_state_trie, get_txn_trie}; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::prover::check_abort_signal; -use crate::util::h2u; +use crate::util::{h2u, u256_to_usize}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -200,7 +201,55 @@ pub fn generate_traces, const D: usize>( apply_metadata_and_tries_memops(&mut state, &inputs); - timed!(timing, "simulate CPU", simulate_cpu(&mut state)?); + let cpu_res = timed!(timing, "simulate CPU", simulate_cpu(&mut state)); + if cpu_res.is_err() { + // Retrieve previous PC (before jumping to KernelPanic), to see if we reached `hash_final_tries`. + // We will output debugging information on the final tries only if we got a root mismatch. + let previous_pc = state + .traces + .cpu + .last() + .expect("We should have CPU rows") + .program_counter + .to_canonical_u64() as usize; + + if KERNEL.offset_name(previous_pc).contains("hash_final_tries") { + let state_trie_ptr = u256_to_usize( + state + .memory + .read_global_metadata(GlobalMetadata::StateTrieRoot), + ) + .map_err(|_| anyhow!("State trie pointer is too large to fit in a usize."))?; + log::debug!( + "Computed state trie: {:?}", + get_state_trie::(&state.memory, state_trie_ptr) + ); + + let txn_trie_ptr = u256_to_usize( + state + .memory + .read_global_metadata(GlobalMetadata::TransactionTrieRoot), + ) + .map_err(|_| anyhow!("Transactions trie pointer is too large to fit in a usize."))?; + log::debug!( + "Computed transactions trie: {:?}", + get_txn_trie::(&state.memory, txn_trie_ptr) + ); + + let receipt_trie_ptr = u256_to_usize( + state + .memory + .read_global_metadata(GlobalMetadata::ReceiptTrieRoot), + ) + .map_err(|_| anyhow!("Receipts trie pointer is too large to fit in a usize."))?; + log::debug!( + "Computed receipts trie: {:?}", + get_receipt_trie::(&state.memory, receipt_trie_ptr) + ); + } + + cpu_res?; + } log::info!( "Trace lengths (before padding): {:?}", diff --git a/evm/src/generation/trie_extractor.rs b/evm/src/generation/trie_extractor.rs index fd06cec628..a7c97e1006 100644 --- a/evm/src/generation/trie_extractor.rs +++ b/evm/src/generation/trie_extractor.rs @@ -3,11 +3,13 @@ use std::collections::HashMap; use eth_trie_utils::nibbles::Nibbles; +use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie, WrappedNode}; use ethereum_types::{BigEndianHash, H256, U256, U512}; +use super::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::memory::segments::Segment; -use crate::util::u256_to_usize; +use crate::util::{u256_to_bool, u256_to_h160, u256_to_u8, u256_to_usize}; use crate::witness::errors::ProgramError; use crate::witness::memory::{MemoryAddress, MemoryState}; @@ -109,3 +111,203 @@ pub(crate) fn read_trie_helper( } } } + +pub(crate) fn read_receipt_trie_value( + slice: &[U256], +) -> Result<(Option, LegacyReceiptRlp), ProgramError> { + let first_value = slice[0]; + // Skip two elements for non-legacy Receipts, and only one otherwise. + let (first_byte, slice) = if first_value == U256::one() || first_value == U256::from(2u8) { + (Some(first_value.as_u32() as u8), &slice[2..]) + } else { + (None, &slice[1..]) + }; + + let status = u256_to_bool(slice[0])?; + let cum_gas_used = slice[1]; + let bloom = slice[2..2 + 256] + .iter() + .map(|&x| u256_to_u8(x)) + .collect::>()?; + // We read the number of logs at position `2 + 256 + 1`, and skip over the next element before parsing the logs. + let logs = read_logs(u256_to_usize(slice[2 + 256 + 1])?, &slice[2 + 256 + 3..])?; + + Ok(( + first_byte, + LegacyReceiptRlp { + status, + cum_gas_used, + bloom, + logs, + }, + )) +} + +pub(crate) fn read_logs(num_logs: usize, slice: &[U256]) -> Result, ProgramError> { + let mut offset = 0; + (0..num_logs) + .map(|_| { + let address = u256_to_h160(slice[offset])?; + let num_topics = u256_to_usize(slice[offset + 1])?; + + let topics = (0..num_topics) + .map(|i| H256::from_uint(&slice[offset + 2 + i])) + .collect(); + + let data_len = u256_to_usize(slice[offset + 2 + num_topics])?; + let log = LogRlp { + address, + topics, + data: slice[offset + 2 + num_topics + 1..offset + 2 + num_topics + 1 + data_len] + .iter() + .map(|&x| u256_to_u8(x)) + .collect::>()?, + }; + offset += 2 + num_topics + 1 + data_len; + Ok(log) + }) + .collect() +} + +pub(crate) fn read_state_rlp_value( + memory: &MemoryState, + slice: &[U256], +) -> Result, ProgramError> { + let storage_trie: HashedPartialTrie = get_trie(memory, slice[2].as_usize(), |_, x| { + Ok(rlp::encode(&read_storage_trie_value(x)).to_vec()) + })?; + let account = AccountRlp { + nonce: slice[0], + balance: slice[1], + storage_root: storage_trie.hash(), + code_hash: H256::from_uint(&slice[3]), + }; + Ok(rlp::encode(&account).to_vec()) +} + +pub(crate) fn read_txn_rlp_value( + _memory: &MemoryState, + slice: &[U256], +) -> Result, ProgramError> { + let txn_rlp_len = u256_to_usize(slice[0])?; + slice[1..txn_rlp_len + 1] + .iter() + .map(|&x| u256_to_u8(x)) + .collect::>() +} + +pub(crate) fn read_receipt_rlp_value( + _memory: &MemoryState, + slice: &[U256], +) -> Result, ProgramError> { + let (first_byte, receipt) = read_receipt_trie_value(slice)?; + let mut bytes = rlp::encode(&receipt).to_vec(); + if let Some(txn_byte) = first_byte { + bytes.insert(0, txn_byte); + } + + Ok(bytes) +} + +pub(crate) fn get_state_trie( + memory: &MemoryState, + ptr: usize, +) -> Result { + get_trie(memory, ptr, read_state_rlp_value) +} + +pub(crate) fn get_txn_trie( + memory: &MemoryState, + ptr: usize, +) -> Result { + get_trie(memory, ptr, read_txn_rlp_value) +} + +pub(crate) fn get_receipt_trie( + memory: &MemoryState, + ptr: usize, +) -> Result { + get_trie(memory, ptr, read_receipt_rlp_value) +} + +pub(crate) fn get_trie( + memory: &MemoryState, + ptr: usize, + read_rlp_value: fn(&MemoryState, &[U256]) -> Result, ProgramError>, +) -> Result { + let empty_nibbles = Nibbles { + count: 0, + packed: U512::zero(), + }; + Ok(N::new(get_trie_helper( + memory, + ptr, + read_rlp_value, + empty_nibbles, + )?)) +} + +pub(crate) fn get_trie_helper( + memory: &MemoryState, + ptr: usize, + read_value: fn(&MemoryState, &[U256]) -> Result, ProgramError>, + prefix: Nibbles, +) -> Result, ProgramError> { + let load = |offset| memory.get(MemoryAddress::new(0, Segment::TrieData, offset)); + let load_slice_from = |init_offset| { + &memory.contexts[0].segments[Segment::TrieData as usize].content[init_offset..] + }; + + let trie_type = PartialTrieType::all()[u256_to_usize(load(ptr))?]; + match trie_type { + PartialTrieType::Empty => Ok(Node::Empty), + PartialTrieType::Hash => { + let ptr_payload = ptr + 1; + let hash = H256::from_uint(&load(ptr_payload)); + Ok(Node::Hash(hash)) + } + PartialTrieType::Branch => { + let ptr_payload = ptr + 1; + let children = (0..16) + .map(|i| { + let child_ptr = u256_to_usize(load(ptr_payload + i as usize))?; + get_trie_helper(memory, child_ptr, read_value, prefix.merge_nibble(i as u8)) + }) + .collect::, _>>()?; + let children = core::array::from_fn(|i| WrappedNode::from(children[i].clone())); + let value_ptr = u256_to_usize(load(ptr_payload + 16))?; + let mut value: Vec = vec![]; + if value_ptr != 0 { + value = read_value(memory, load_slice_from(value_ptr))?; + }; + Ok(Node::Branch { children, value }) + } + PartialTrieType::Extension => { + let count = u256_to_usize(load(ptr + 1))?; + let packed = load(ptr + 2); + let nibbles = Nibbles { + count, + packed: packed.into(), + }; + let child_ptr = u256_to_usize(load(ptr + 3))?; + let child = WrappedNode::from(get_trie_helper( + memory, + child_ptr, + read_value, + prefix.merge_nibbles(&nibbles), + )?); + Ok(Node::Extension { nibbles, child }) + } + PartialTrieType::Leaf => { + let count = u256_to_usize(load(ptr + 1))?; + let packed = load(ptr + 2); + let nibbles = Nibbles { + count, + packed: packed.into(), + }; + let value_ptr = u256_to_usize(load(ptr + 3))?; + let value = read_value(memory, load_slice_from(value_ptr))?; + Ok(Node::Leaf { nibbles, value }) + } + } +} diff --git a/evm/src/util.rs b/evm/src/util.rs index 9cac52c66b..3d9564b5ed 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -75,6 +75,36 @@ pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } +/// Converts a `U256` to a `u8`, erroring in case of overlow instead of panicking. +pub(crate) fn u256_to_u8(u256: U256) -> Result { + u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) +} + +/// Converts a `U256` to a `bool`, erroring in case of overlow instead of panicking. +pub(crate) fn u256_to_bool(u256: U256) -> Result { + if u256 == U256::zero() { + Ok(false) + } else if u256 == U256::one() { + Ok(true) + } else { + Err(ProgramError::IntegerTooLarge) + } +} + +/// Converts a `U256` to a `H160`, erroring in case of overflow instead of panicking. +pub(crate) fn u256_to_h160(u256: U256) -> Result { + if u256.bits() / 8 > 20 { + return Err(ProgramError::IntegerTooLarge); + } + let mut bytes = [0u8; 32]; + u256.to_big_endian(&mut bytes); + Ok(H160( + bytes[12..] + .try_into() + .expect("This conversion cannot fail."), + )) +} + /// Returns the 32-bit little-endian limbs of a `U256`. pub(crate) fn u256_limbs(u256: U256) -> [F; 8] { u256.0 From f67ee258a172848a96fee20196a0151178a61ae6 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:11:16 +0100 Subject: [PATCH 033/175] Add exceptions handling to the interpreter (#1393) * Add exceptions handling to the interpreter * Apply comments * Fix comments --- evm/src/cpu/kernel/interpreter.rs | 833 +++++++++++++++-------- evm/src/cpu/kernel/tests/account_code.rs | 89 ++- evm/src/cpu/kernel/tests/add11.rs | 134 ++++ evm/src/cpu/kernel/tests/balance.rs | 32 +- evm/src/cpu/kernel/tests/mpt/delete.rs | 41 +- evm/src/cpu/kernel/tests/mpt/hash.rs | 8 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 20 +- evm/src/cpu/kernel/tests/mpt/read.rs | 16 +- evm/src/cpu/kernel/tests/receipt.rs | 22 +- 9 files changed, 848 insertions(+), 347 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index f187a4804a..5fbb8cd1f4 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -4,7 +4,7 @@ use core::cmp::Ordering; use std::collections::HashMap; use std::ops::Range; -use anyhow::{anyhow, bail, ensure}; +use anyhow::bail; use ethereum_types::{U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -21,10 +21,11 @@ use crate::generation::state::GenerationState; use crate::generation::GenerationInputs; use crate::memory::segments::Segment; use crate::util::u256_to_usize; -use crate::witness::errors::ProgramError; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; use crate::witness::operation::Operation; +use crate::witness::state::RegistersState; use crate::witness::transition::decode; use crate::witness::util::stack_peek; @@ -38,8 +39,16 @@ impl MemoryState { self.get(MemoryAddress::new(context, segment, offset)) } - fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) { + fn mstore_general( + &mut self, + context: usize, + segment: Segment, + offset: usize, + value: U256, + ) -> InterpreterMemOpKind { + let old_value = self.mload_general(context, segment, offset); self.set(MemoryAddress::new(context, segment, offset), value); + InterpreterMemOpKind::Write(old_value, context, segment as usize, offset) } } @@ -51,6 +60,22 @@ pub(crate) struct Interpreter<'a> { pub(crate) debug_offsets: Vec, running: bool, opcode_count: [usize; 0x100], + memops: Vec, +} + +/// Structure storing the state of the interpreter's registers. +struct InterpreterRegistersState { + kernel_mode: bool, + context: usize, + registers: RegistersState, +} + +/// Interpreter state at the last checkpoint: we only need to store +/// the state of the registers and the length of the vector of memory operations. +/// This data is enough to revert in case of an exception. +struct InterpreterCheckpoint { + registers: InterpreterRegistersState, + mem_len: usize, } pub(crate) fn run_interpreter( @@ -103,6 +128,16 @@ pub(crate) fn run<'a>( Ok(interpreter) } +/// Different types of Memory operations in the interpreter, and the data required to revert them. +enum InterpreterMemOpKind { + /// We need to provide the context. + Push(usize), + /// If we pop a certain value, we need to push it back to the correct context when reverting. + Pop(U256, usize), + /// If we write a value at a certain address, we need to write the old value back when reverting. + Write(U256, usize, usize, usize), +} + impl<'a> Interpreter<'a> { pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { Self::new( @@ -130,6 +165,7 @@ impl<'a> Interpreter<'a> { debug_offsets: vec![], running: false, opcode_count: [0; 256], + memops: vec![], }; result.generation_state.registers.program_counter = initial_offset; let initial_stack_len = initial_stack.len(); @@ -143,6 +179,74 @@ impl<'a> Interpreter<'a> { result } + fn checkpoint(&self) -> InterpreterCheckpoint { + let registers = InterpreterRegistersState { + kernel_mode: self.is_kernel(), + context: self.context(), + registers: self.generation_state.registers, + }; + InterpreterCheckpoint { + registers, + mem_len: self.memops.len(), + } + } + + fn roll_memory_back(&mut self, len: usize) { + // We roll the memory back until `memops` reaches length `len`. + debug_assert!(self.memops.len() >= len); + while self.memops.len() > len { + if let Some(op) = self.memops.pop() { + match op { + InterpreterMemOpKind::Push(context) => { + self.generation_state.memory.contexts[context].segments + [Segment::Stack as usize] + .content + .pop(); + } + InterpreterMemOpKind::Pop(value, context) => { + self.generation_state.memory.contexts[context].segments + [Segment::Stack as usize] + .content + .push(value) + } + InterpreterMemOpKind::Write(value, context, segment, offset) => { + self.generation_state.memory.contexts[context].segments[segment].content + [offset] = value + } + } + } + } + } + + fn rollback(&mut self, checkpoint: InterpreterCheckpoint) { + let InterpreterRegistersState { + kernel_mode, + context, + registers, + } = checkpoint.registers; + self.set_is_kernel(kernel_mode); + self.set_context(context); + self.generation_state.registers = registers; + self.roll_memory_back(checkpoint.mem_len); + } + + fn handle_error(&mut self, err: ProgramError) -> anyhow::Result<()> { + let exc_code: u8 = match err { + ProgramError::OutOfGas => 0, + ProgramError::InvalidOpcode => 1, + ProgramError::StackUnderflow => 2, + ProgramError::InvalidJumpDestination => 3, + ProgramError::InvalidJumpiDestination => 4, + ProgramError::StackOverflow => 5, + _ => bail!("TODO: figure out what to do with this..."), + }; + + self.run_exception(exc_code) + .map_err(|_| anyhow::Error::msg("error handling errored..."))?; + + Ok(()) + } + pub(crate) fn run(&mut self) -> anyhow::Result<()> { self.running = true; while self.running { @@ -150,7 +254,29 @@ impl<'a> Interpreter<'a> { if self.is_kernel() && self.halt_offsets.contains(&pc) { return Ok(()); }; - self.run_opcode()?; + + let checkpoint = self.checkpoint(); + let result = self.run_opcode(); + match result { + Ok(()) => Ok(()), + Err(e) => { + if self.is_kernel() { + let offset_name = + KERNEL.offset_name(self.generation_state.registers.program_counter); + bail!( + "{:?} in kernel at pc={}, stack={:?}, memory={:?}", + e, + offset_name, + self.stack(), + self.generation_state.memory.contexts[0].segments + [Segment::KernelGeneral as usize] + .content, + ); + } + self.rollback(checkpoint); + self.handle_error(e) + } + }?; } println!("Opcode count:"); for i in 0..0x100 { @@ -335,7 +461,10 @@ impl<'a> Interpreter<'a> { output } - pub(crate) fn push(&mut self, x: U256) { + pub(crate) fn push(&mut self, x: U256) -> Result<(), ProgramError> { + if !self.is_kernel() && self.stack_len() >= MAX_USER_STACK_SIZE { + return Err(ProgramError::StackOverflow); + } if self.stack_len() > 0 { let top = self .stack_top() @@ -346,24 +475,33 @@ impl<'a> Interpreter<'a> { } self.generation_state.registers.stack_top = x; self.generation_state.registers.stack_len += 1; + self.memops.push(InterpreterMemOpKind::Push(self.context())); + + Ok(()) } - fn push_bool(&mut self, x: bool) { - self.push(if x { U256::one() } else { U256::zero() }); + fn push_bool(&mut self, x: bool) -> Result<(), ProgramError> { + self.push(if x { U256::one() } else { U256::zero() })?; + Ok(()) } - pub(crate) fn pop(&mut self) -> U256 { + pub(crate) fn pop(&mut self) -> Result { let result = stack_peek(&self.generation_state, 0); + + if let Ok(val) = result { + self.memops + .push(InterpreterMemOpKind::Pop(val, self.context())); + } if self.stack_len() > 1 { let top = stack_peek(&self.generation_state, 1).unwrap(); self.generation_state.registers.stack_top = top; } self.generation_state.registers.stack_len -= 1; - result.expect("Empty stack") + result } - fn run_opcode(&mut self) -> anyhow::Result<()> { + fn run_opcode(&mut self) -> Result<(), ProgramError> { let opcode = self .code() .get(self.generation_state.registers.program_counter) @@ -371,108 +509,124 @@ impl<'a> Interpreter<'a> { self.opcode_count[opcode as usize] += 1; self.incr(1); match opcode { - 0x00 => self.run_syscall(opcode, 0, false)?, // "STOP", - 0x01 => self.run_add(), // "ADD", - 0x02 => self.run_mul(), // "MUL", - 0x03 => self.run_sub(), // "SUB", - 0x04 => self.run_div(), // "DIV", - 0x05 => self.run_syscall(opcode, 2, false)?, // "SDIV", - 0x06 => self.run_mod(), // "MOD", - 0x07 => self.run_syscall(opcode, 2, false)?, // "SMOD", - 0x08 => self.run_addmod(), // "ADDMOD", - 0x09 => self.run_mulmod(), // "MULMOD", - 0x0a => self.run_syscall(opcode, 2, false)?, // "EXP", - 0x0b => self.run_syscall(opcode, 2, false)?, // "SIGNEXTEND", - 0x0c => self.run_addfp254(), // "ADDFP254", - 0x0d => self.run_mulfp254(), // "MULFP254", - 0x0e => self.run_subfp254(), // "SUBFP254", - 0x0f => self.run_submod(), // "SUBMOD", - 0x10 => self.run_lt(), // "LT", - 0x11 => self.run_gt(), // "GT", - 0x12 => self.run_syscall(opcode, 2, false)?, // "SLT", - 0x13 => self.run_syscall(opcode, 2, false)?, // "SGT", - 0x14 => self.run_eq(), // "EQ", - 0x15 => self.run_iszero(), // "ISZERO", - 0x16 => self.run_and(), // "AND", - 0x17 => self.run_or(), // "OR", - 0x18 => self.run_xor(), // "XOR", - 0x19 => self.run_not(), // "NOT", - 0x1a => self.run_byte(), // "BYTE", - 0x1b => self.run_shl(), // "SHL", - 0x1c => self.run_shr(), // "SHR", - 0x1d => self.run_syscall(opcode, 2, false)?, // "SAR", - 0x20 => self.run_syscall(opcode, 2, false)?, // "KECCAK256", - 0x21 => self.run_keccak_general(), // "KECCAK_GENERAL", - 0x30 => self.run_syscall(opcode, 0, true)?, // "ADDRESS", - 0x31 => self.run_syscall(opcode, 1, false)?, // "BALANCE", - 0x32 => self.run_syscall(opcode, 0, true)?, // "ORIGIN", - 0x33 => self.run_syscall(opcode, 0, true)?, // "CALLER", - 0x34 => self.run_syscall(opcode, 0, true)?, // "CALLVALUE", - 0x35 => self.run_syscall(opcode, 1, false)?, // "CALLDATALOAD", - 0x36 => self.run_syscall(opcode, 0, true)?, // "CALLDATASIZE", - 0x37 => self.run_syscall(opcode, 3, false)?, // "CALLDATACOPY", - 0x38 => self.run_syscall(opcode, 0, true)?, // "CODESIZE", - 0x39 => self.run_syscall(opcode, 3, false)?, // "CODECOPY", - 0x3a => self.run_syscall(opcode, 0, true)?, // "GASPRICE", - 0x3b => self.run_syscall(opcode, 1, false)?, // "EXTCODESIZE", - 0x3c => self.run_syscall(opcode, 4, false)?, // "EXTCODECOPY", - 0x3d => self.run_syscall(opcode, 0, true)?, // "RETURNDATASIZE", - 0x3e => self.run_syscall(opcode, 3, false)?, // "RETURNDATACOPY", - 0x3f => self.run_syscall(opcode, 1, false)?, // "EXTCODEHASH", - 0x40 => self.run_syscall(opcode, 1, false)?, // "BLOCKHASH", - 0x41 => self.run_syscall(opcode, 0, true)?, // "COINBASE", - 0x42 => self.run_syscall(opcode, 0, true)?, // "TIMESTAMP", - 0x43 => self.run_syscall(opcode, 0, true)?, // "NUMBER", - 0x44 => self.run_syscall(opcode, 0, true)?, // "DIFFICULTY", - 0x45 => self.run_syscall(opcode, 0, true)?, // "GASLIMIT", - 0x46 => self.run_syscall(opcode, 0, true)?, // "CHAINID", - 0x47 => self.run_syscall(opcode, 0, true)?, // SELFABALANCE, - 0x48 => self.run_syscall(opcode, 0, true)?, // "BASEFEE", - 0x49 => self.run_prover_input()?, // "PROVER_INPUT", - 0x50 => self.run_pop(), // "POP", - 0x51 => self.run_syscall(opcode, 1, false)?, // "MLOAD", - 0x52 => self.run_syscall(opcode, 2, false)?, // "MSTORE", - 0x53 => self.run_syscall(opcode, 2, false)?, // "MSTORE8", - 0x54 => self.run_syscall(opcode, 1, false)?, // "SLOAD", - 0x55 => self.run_syscall(opcode, 2, false)?, // "SSTORE", - 0x56 => self.run_jump(), // "JUMP", - 0x57 => self.run_jumpi(), // "JUMPI", - 0x58 => self.run_pc(), // "PC", - 0x59 => self.run_syscall(opcode, 0, true)?, // "MSIZE", - 0x5a => self.run_syscall(opcode, 0, true)?, // "GAS", - 0x5b => self.run_jumpdest(), // "JUMPDEST", + 0x00 => self.run_syscall(opcode, 0, false), // "STOP", + 0x01 => self.run_add(), // "ADD", + 0x02 => self.run_mul(), // "MUL", + 0x03 => self.run_sub(), // "SUB", + 0x04 => self.run_div(), // "DIV", + 0x05 => self.run_syscall(opcode, 2, false), // "SDIV", + 0x06 => self.run_mod(), // "MOD", + 0x07 => self.run_syscall(opcode, 2, false), // "SMOD", + 0x08 => self.run_addmod(), // "ADDMOD", + 0x09 => self.run_mulmod(), // "MULMOD", + 0x0a => self.run_syscall(opcode, 2, false), // "EXP", + 0x0b => self.run_syscall(opcode, 2, false), // "SIGNEXTEND", + 0x0c => self.run_addfp254(), // "ADDFP254", + 0x0d => self.run_mulfp254(), // "MULFP254", + 0x0e => self.run_subfp254(), // "SUBFP254", + 0x0f => self.run_submod(), // "SUBMOD", + 0x10 => self.run_lt(), // "LT", + 0x11 => self.run_gt(), // "GT", + 0x12 => self.run_syscall(opcode, 2, false), // "SLT", + 0x13 => self.run_syscall(opcode, 2, false), // "SGT", + 0x14 => self.run_eq(), // "EQ", + 0x15 => self.run_iszero(), // "ISZERO", + 0x16 => self.run_and(), // "AND", + 0x17 => self.run_or(), // "OR", + 0x18 => self.run_xor(), // "XOR", + 0x19 => self.run_not(), // "NOT", + 0x1a => self.run_byte(), // "BYTE", + 0x1b => self.run_shl(), // "SHL", + 0x1c => self.run_shr(), // "SHR", + 0x1d => self.run_syscall(opcode, 2, false), // "SAR", + 0x20 => self.run_syscall(opcode, 2, false), // "KECCAK256", + 0x21 => self.run_keccak_general(), // "KECCAK_GENERAL", + 0x30 => self.run_syscall(opcode, 0, true), // "ADDRESS", + 0x31 => self.run_syscall(opcode, 1, false), // "BALANCE", + 0x32 => self.run_syscall(opcode, 0, true), // "ORIGIN", + 0x33 => self.run_syscall(opcode, 0, true), // "CALLER", + 0x34 => self.run_syscall(opcode, 0, true), // "CALLVALUE", + 0x35 => self.run_syscall(opcode, 1, false), // "CALLDATALOAD", + 0x36 => self.run_syscall(opcode, 0, true), // "CALLDATASIZE", + 0x37 => self.run_syscall(opcode, 3, false), // "CALLDATACOPY", + 0x38 => self.run_syscall(opcode, 0, true), // "CODESIZE", + 0x39 => self.run_syscall(opcode, 3, false), // "CODECOPY", + 0x3a => self.run_syscall(opcode, 0, true), // "GASPRICE", + 0x3b => self.run_syscall(opcode, 1, false), // "EXTCODESIZE", + 0x3c => self.run_syscall(opcode, 4, false), // "EXTCODECOPY", + 0x3d => self.run_syscall(opcode, 0, true), // "RETURNDATASIZE", + 0x3e => self.run_syscall(opcode, 3, false), // "RETURNDATACOPY", + 0x3f => self.run_syscall(opcode, 1, false), // "EXTCODEHASH", + 0x40 => self.run_syscall(opcode, 1, false), // "BLOCKHASH", + 0x41 => self.run_syscall(opcode, 0, true), // "COINBASE", + 0x42 => self.run_syscall(opcode, 0, true), // "TIMESTAMP", + 0x43 => self.run_syscall(opcode, 0, true), // "NUMBER", + 0x44 => self.run_syscall(opcode, 0, true), // "DIFFICULTY", + 0x45 => self.run_syscall(opcode, 0, true), // "GASLIMIT", + 0x46 => self.run_syscall(opcode, 0, true), // "CHAINID", + 0x47 => self.run_syscall(opcode, 0, true), // SELFABALANCE, + 0x48 => self.run_syscall(opcode, 0, true), // "BASEFEE", + 0x49 => self.run_prover_input(), // "PROVER_INPUT", + 0x50 => self.run_pop(), // "POP", + 0x51 => self.run_syscall(opcode, 1, false), // "MLOAD", + 0x52 => self.run_syscall(opcode, 2, false), // "MSTORE", + 0x53 => self.run_syscall(opcode, 2, false), // "MSTORE8", + 0x54 => self.run_syscall(opcode, 1, false), // "SLOAD", + 0x55 => self.run_syscall(opcode, 2, false), // "SSTORE", + 0x56 => self.run_jump(), // "JUMP", + 0x57 => self.run_jumpi(), // "JUMPI", + 0x58 => self.run_pc(), // "PC", + 0x59 => self.run_syscall(opcode, 0, true), // "MSIZE", + 0x5a => self.run_syscall(opcode, 0, true), // "GAS", + 0x5b => self.run_jumpdest(), // "JUMPDEST", x if (0x5f..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH" - x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f)?, // "DUP" - x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f)?, // "SWAP" - 0xa0 => self.run_syscall(opcode, 2, false)?, // "LOG0", - 0xa1 => self.run_syscall(opcode, 3, false)?, // "LOG1", - 0xa2 => self.run_syscall(opcode, 4, false)?, // "LOG2", - 0xa3 => self.run_syscall(opcode, 5, false)?, // "LOG3", - 0xa4 => self.run_syscall(opcode, 6, false)?, // "LOG4", - 0xa5 => bail!( - "Executed PANIC, stack={:?}, memory={:?}", - self.stack(), - self.get_kernel_general_memory() - ), // "PANIC", + x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f), // "DUP" + x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f), // "SWAP" + 0xa0 => self.run_syscall(opcode, 2, false), // "LOG0", + 0xa1 => self.run_syscall(opcode, 3, false), // "LOG1", + 0xa2 => self.run_syscall(opcode, 4, false), // "LOG2", + 0xa3 => self.run_syscall(opcode, 5, false), // "LOG3", + 0xa4 => self.run_syscall(opcode, 6, false), // "LOG4", + 0xa5 => { + log::warn!( + "Kernel panic at {}, stack = {:?}, memory = {:?}", + KERNEL.offset_name(self.generation_state.registers.program_counter), + self.stack(), + self.get_kernel_general_memory() + ); + Err(ProgramError::KernelPanic) + } // "PANIC", x if (0xc0..0xe0).contains(&x) => self.run_mstore_32bytes(x - 0xc0 + 1), // "MSTORE_32BYTES", - 0xf0 => self.run_syscall(opcode, 3, false)?, // "CREATE", - 0xf1 => self.run_syscall(opcode, 7, false)?, // "CALL", - 0xf2 => self.run_syscall(opcode, 7, false)?, // "CALLCODE", - 0xf3 => self.run_syscall(opcode, 2, false)?, // "RETURN", - 0xf4 => self.run_syscall(opcode, 6, false)?, // "DELEGATECALL", - 0xf5 => self.run_syscall(opcode, 4, false)?, // "CREATE2", - 0xf6 => self.run_get_context(), // "GET_CONTEXT", - 0xf7 => self.run_set_context(), // "SET_CONTEXT", - 0xf8 => self.run_mload_32bytes(), // "MLOAD_32BYTES", - 0xf9 => self.run_exit_kernel(), // "EXIT_KERNEL", - 0xfa => self.run_syscall(opcode, 6, false)?, // "STATICCALL", - 0xfb => self.run_mload_general(), // "MLOAD_GENERAL", - 0xfc => self.run_mstore_general(), // "MSTORE_GENERAL", - 0xfd => self.run_syscall(opcode, 2, false)?, // "REVERT", - 0xfe => bail!("Executed INVALID"), // "INVALID", - 0xff => self.run_syscall(opcode, 1, false)?, // "SELFDESTRUCT", - _ => bail!("Unrecognized opcode {}.", opcode), - }; + 0xf0 => self.run_syscall(opcode, 3, false), // "CREATE", + 0xf1 => self.run_syscall(opcode, 7, false), // "CALL", + 0xf2 => self.run_syscall(opcode, 7, false), // "CALLCODE", + 0xf3 => self.run_syscall(opcode, 2, false), // "RETURN", + 0xf4 => self.run_syscall(opcode, 6, false), // "DELEGATECALL", + 0xf5 => self.run_syscall(opcode, 4, false), // "CREATE2", + 0xf6 => self.run_get_context(), // "GET_CONTEXT", + 0xf7 => self.run_set_context(), // "SET_CONTEXT", + 0xf8 => self.run_mload_32bytes(), // "MLOAD_32BYTES", + 0xf9 => self.run_exit_kernel(), // "EXIT_KERNEL", + 0xfa => self.run_syscall(opcode, 6, false), // "STATICCALL", + 0xfb => self.run_mload_general(), // "MLOAD_GENERAL", + 0xfc => self.run_mstore_general(), // "MSTORE_GENERAL", + 0xfd => self.run_syscall(opcode, 2, false), // "REVERT", + 0xfe => { + log::warn!( + "Invalid opcode at {}", + KERNEL.offset_name(self.generation_state.registers.program_counter), + ); + Err(ProgramError::InvalidOpcode) + } // "INVALID", + 0xff => self.run_syscall(opcode, 1, false), // "SELFDESTRUCT", + _ => { + log::warn!( + "Unrecognized opcode at {}", + KERNEL.offset_name(self.generation_state.registers.program_counter), + ); + Err(ProgramError::InvalidOpcode) + } + }?; if self .debug_offsets @@ -488,6 +642,19 @@ impl<'a> Interpreter<'a> { .unwrap_or(Operation::ProverInput); self.generation_state.registers.gas_used += gas_to_charge(op); + if !self.is_kernel() { + let gas_limit_address = MemoryAddress { + context: self.context(), + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::GasLimit as usize, + }; + let gas_limit = + u256_to_usize(self.generation_state.memory.get(gas_limit_address))? as u64; + if self.generation_state.registers.gas_used > gas_limit { + return Err(ProgramError::OutOfGas); + } + } + Ok(()) } @@ -499,177 +666,201 @@ impl<'a> Interpreter<'a> { KERNEL.offset_label(self.generation_state.registers.program_counter) } - fn run_add(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x.overflowing_add(y).0); + fn run_add(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x.overflowing_add(y).0)?; + Ok(()) } - fn run_mul(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x.overflowing_mul(y).0); + fn run_mul(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x.overflowing_mul(y).0)?; + Ok(()) } - fn run_sub(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x.overflowing_sub(y).0); + fn run_sub(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x.overflowing_sub(y).0)?; + Ok(()) } - fn run_addfp254(&mut self) { - let x = self.pop() % BN_BASE; - let y = self.pop() % BN_BASE; + fn run_addfp254(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()? % BN_BASE; + let y = self.pop()? % BN_BASE; // BN_BASE is 254-bit so addition can't overflow - self.push((x + y) % BN_BASE); + self.push((x + y) % BN_BASE)?; + Ok(()) } - fn run_mulfp254(&mut self) { - let x = self.pop(); - let y = self.pop(); + fn run_mulfp254(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; self.push( U256::try_from(x.full_mul(y) % BN_BASE) .expect("BN_BASE is 254 bit so the U512 fits in a U256"), - ); + )?; + Ok(()) } - fn run_subfp254(&mut self) { - let x = self.pop() % BN_BASE; - let y = self.pop() % BN_BASE; + fn run_subfp254(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()? % BN_BASE; + let y = self.pop()? % BN_BASE; // BN_BASE is 254-bit so addition can't overflow - self.push((x + (BN_BASE - y)) % BN_BASE); + self.push((x + (BN_BASE - y)) % BN_BASE)?; + Ok(()) } - fn run_div(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(if y.is_zero() { U256::zero() } else { x / y }); + fn run_div(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(if y.is_zero() { U256::zero() } else { x / y })?; + Ok(()) } - fn run_mod(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(if y.is_zero() { U256::zero() } else { x % y }); + fn run_mod(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(if y.is_zero() { U256::zero() } else { x % y })?; + Ok(()) } - fn run_addmod(&mut self) { - let x = self.pop(); - let y = self.pop(); - let z = self.pop(); + fn run_addmod(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + let z = self.pop()?; self.push(if z.is_zero() { z } else { let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); U256::try_from((x + y) % z) .expect("Inputs are U256 and their sum mod a U256 fits in a U256.") - }); + })?; + + Ok(()) } - fn run_submod(&mut self) { - let x = self.pop(); - let y = self.pop(); - let z = self.pop(); + fn run_submod(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + let z = self.pop()?; self.push(if z.is_zero() { z } else { let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); U256::try_from((z + x - y) % z) .expect("Inputs are U256 and their difference mod a U256 fits in a U256.") - }); + })?; + + Ok(()) } - fn run_mulmod(&mut self) { - let x = self.pop(); - let y = self.pop(); - let z = self.pop(); + fn run_mulmod(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + let z = self.pop()?; self.push(if z.is_zero() { z } else { U256::try_from(x.full_mul(y) % z) .expect("Inputs are U256 and their product mod a U256 fits in a U256.") - }); + })?; + Ok(()) } - fn run_lt(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push_bool(x < y); + fn run_lt(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push_bool(x < y)?; + Ok(()) } - fn run_gt(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push_bool(x > y); + fn run_gt(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push_bool(x > y)?; + Ok(()) } - fn run_eq(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push_bool(x == y); + fn run_eq(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push_bool(x == y)?; + Ok(()) } - fn run_iszero(&mut self) { - let x = self.pop(); - self.push_bool(x.is_zero()); + fn run_iszero(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + self.push_bool(x.is_zero())?; + Ok(()) } - fn run_and(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x & y); + fn run_and(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x & y)?; + Ok(()) } - fn run_or(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x | y); + fn run_or(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x | y)?; + Ok(()) } - fn run_xor(&mut self) { - let x = self.pop(); - let y = self.pop(); - self.push(x ^ y); + fn run_xor(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let y = self.pop()?; + self.push(x ^ y)?; + Ok(()) } - fn run_not(&mut self) { - let x = self.pop(); - self.push(!x); + fn run_not(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + self.push(!x)?; + Ok(()) } - fn run_byte(&mut self) { - let i = self.pop(); - let x = self.pop(); + fn run_byte(&mut self) -> anyhow::Result<(), ProgramError> { + let i = self.pop()?; + let x = self.pop()?; let result = if i < 32.into() { x.byte(31 - i.as_usize()) } else { 0 }; - self.push(result.into()); + self.push(result.into())?; + Ok(()) } - fn run_shl(&mut self) { - let shift = self.pop(); - let value = self.pop(); + fn run_shl(&mut self) -> anyhow::Result<(), ProgramError> { + let shift = self.pop()?; + let value = self.pop()?; self.push(if shift < U256::from(256usize) { value << shift } else { U256::zero() - }); + })?; + Ok(()) } - fn run_shr(&mut self) { - let shift = self.pop(); - let value = self.pop(); - self.push(value >> shift); + fn run_shr(&mut self) -> anyhow::Result<(), ProgramError> { + let shift = self.pop()?; + let value = self.pop()?; + self.push(value >> shift)?; + Ok(()) } - fn run_keccak_general(&mut self) { - let context = self.pop().as_usize(); - let segment = Segment::all()[self.pop().as_usize()]; + fn run_keccak_general(&mut self) -> anyhow::Result<(), ProgramError> { + let context = self.pop()?.as_usize(); + let segment = Segment::all()[self.pop()?.as_usize()]; // Not strictly needed but here to avoid surprises with MSIZE. assert_ne!(segment, Segment::MainMemory, "Call KECCAK256 instead."); - let offset = self.pop().as_usize(); - let size = self.pop().as_usize(); + let offset = self.pop()?.as_usize(); + let size = self.pop()?.as_usize(); let bytes = (offset..offset + size) .map(|i| { self.generation_state @@ -680,24 +871,25 @@ impl<'a> Interpreter<'a> { .collect::>(); println!("Hashing {:?}", &bytes); let hash = keccak(bytes); - self.push(U256::from_big_endian(hash.as_bytes())); + self.push(U256::from_big_endian(hash.as_bytes()))?; + Ok(()) } - fn run_prover_input(&mut self) -> anyhow::Result<()> { + fn run_prover_input(&mut self) -> Result<(), ProgramError> { let prover_input_fn = self .prover_inputs_map .get(&(self.generation_state.registers.program_counter - 1)) - .ok_or_else(|| anyhow!("Offset not in prover inputs."))?; - let output = self - .generation_state - .prover_input(prover_input_fn) - .map_err(|_| anyhow!("Invalid prover inputs."))?; - self.push(output); + .ok_or(ProgramError::ProverInputError( + ProverInputError::InvalidMptInput, + ))?; + let output = self.generation_state.prover_input(prover_input_fn)?; + self.push(output)?; Ok(()) } - fn run_pop(&mut self) { - self.pop(); + fn run_pop(&mut self) -> anyhow::Result<(), ProgramError> { + self.pop()?; + Ok(()) } fn run_syscall( @@ -705,18 +897,18 @@ impl<'a> Interpreter<'a> { opcode: u8, stack_values_read: usize, stack_len_increased: bool, - ) -> anyhow::Result<()> { + ) -> Result<(), ProgramError> { TryInto::::try_into(self.generation_state.registers.gas_used) - .map_err(|_| anyhow!("Gas overflow"))?; + .map_err(|_| ProgramError::GasLimitError)?; if self.generation_state.registers.stack_len < stack_values_read { - return Err(anyhow!("Stack underflow")); + return Err(ProgramError::StackUnderflow); } if stack_len_increased && !self.is_kernel() && self.generation_state.registers.stack_len >= MAX_USER_STACK_SIZE { - return Err(anyhow!("Stack overflow")); + return Err(ProgramError::StackOverflow); }; let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"]; @@ -728,7 +920,7 @@ impl<'a> Interpreter<'a> { }; let new_program_counter = - u256_to_usize(handler_addr).map_err(|_| anyhow!("The program counter is too large"))?; + u256_to_usize(handler_addr).map_err(|_| ProgramError::IntegerTooLarge)?; let syscall_info = U256::from(self.generation_state.registers.program_counter) + U256::from((self.is_kernel() as usize) << 32) @@ -737,25 +929,35 @@ impl<'a> Interpreter<'a> { self.set_is_kernel(true); self.generation_state.registers.gas_used = 0; - self.push(syscall_info); + self.push(syscall_info)?; Ok(()) } - fn run_jump(&mut self) { - let x = self.pop().as_usize(); - self.jump_to(x); + fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + // Check that the destination is valid. + let x: u32 = x + .try_into() + .map_err(|_| ProgramError::InvalidJumpDestination)?; + self.jump_to(x as usize)?; + Ok(()) } - fn run_jumpi(&mut self) { - let x = self.pop().as_usize(); - let b = self.pop(); + fn run_jumpi(&mut self) -> anyhow::Result<(), ProgramError> { + let x = self.pop()?; + let b = self.pop()?; if !b.is_zero() { - self.jump_to(x); + let x: u32 = x + .try_into() + .map_err(|_| ProgramError::InvalidJumpiDestination)?; + self.jump_to(x as usize)?; } + + Ok(()) } - fn run_pc(&mut self) { + fn run_pc(&mut self) -> anyhow::Result<(), ProgramError> { self.push( (self .generation_state @@ -763,17 +965,19 @@ impl<'a> Interpreter<'a> { .program_counter .saturating_sub(1)) .into(), - ); + )?; + Ok(()) } - fn run_jumpdest(&mut self) { + fn run_jumpdest(&mut self) -> anyhow::Result<(), ProgramError> { assert!(!self.is_kernel(), "JUMPDEST is not needed in kernel code"); + Ok(()) } - fn jump_to(&mut self, offset: usize) { + fn jump_to(&mut self, offset: usize) -> anyhow::Result<(), ProgramError> { // The JUMPDEST rule is not enforced in kernel mode. if !self.is_kernel() && self.jumpdests.binary_search(&offset).is_err() { - panic!("Destination is not a JUMPDEST."); + return Err(ProgramError::InvalidJumpDestination); } self.generation_state.registers.program_counter = offset; @@ -781,44 +985,55 @@ impl<'a> Interpreter<'a> { if self.halt_offsets.contains(&offset) { self.running = false; } + Ok(()) } - fn run_push(&mut self, num_bytes: u8) { + fn run_push(&mut self, num_bytes: u8) -> anyhow::Result<(), ProgramError> { let x = U256::from_big_endian(&self.code_slice(num_bytes as usize)); self.incr(num_bytes as usize); - self.push(x); + self.push(x)?; + Ok(()) } - fn run_dup(&mut self, n: u8) -> anyhow::Result<()> { - assert!(n > 0); - - self.push( - stack_peek(&self.generation_state, n as usize - 1) - .map_err(|_| anyhow!("Stack underflow."))?, - ); - + fn run_dup(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { + let len = self.stack_len(); + if !self.is_kernel() && len >= MAX_USER_STACK_SIZE { + return Err(ProgramError::StackOverflow); + } + if n as usize > self.stack_len() { + return Err(ProgramError::StackUnderflow); + } + self.push(stack_peek(&self.generation_state, n as usize - 1)?)?; Ok(()) } - fn run_swap(&mut self, n: u8) -> anyhow::Result<()> { + fn run_swap(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { let len = self.stack_len(); - ensure!(len > n as usize); - let to_swap = stack_peek(&self.generation_state, n as usize) - .map_err(|_| anyhow!("Stack underflow"))?; + if n as usize >= len { + return Err(ProgramError::StackUnderflow); + } + let to_swap = stack_peek(&self.generation_state, n as usize)?; let old_value = self.stack_segment_mut()[len - n as usize - 1]; - self.stack_segment_mut()[len - n as usize - 1] = self - .stack_top() - .expect("The stack is checked to be nonempty."); + + self.stack_segment_mut()[len - n as usize - 1] = self.stack_top()?; + let mem_write_op = InterpreterMemOpKind::Write( + old_value, + self.context(), + Segment::Stack as usize, + len - n as usize - 1, + ); + self.memops.push(mem_write_op); self.generation_state.registers.stack_top = to_swap; Ok(()) } - fn run_get_context(&mut self) { - self.push(self.context().into()); + fn run_get_context(&mut self) -> anyhow::Result<(), ProgramError> { + self.push(self.context().into())?; + Ok(()) } - fn run_set_context(&mut self) { - let new_ctx = self.pop().as_usize(); + fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { + let new_ctx = self.pop()?.as_usize(); let sp_to_save = self.stack_len().into(); let old_ctx = self.context(); @@ -839,25 +1054,30 @@ impl<'a> Interpreter<'a> { } self.set_context(new_ctx); self.generation_state.registers.stack_len = new_sp; + Ok(()) } - fn run_mload_general(&mut self) { - let context = self.pop().as_usize(); - let segment = Segment::all()[self.pop().as_usize()]; - let offset = self.pop().as_usize(); + fn run_mload_general(&mut self) -> anyhow::Result<(), ProgramError> { + let context = self.pop()?.as_usize(); + let segment = Segment::all()[self.pop()?.as_usize()]; + let offset = self.pop()?.as_usize(); let value = self .generation_state .memory .mload_general(context, segment, offset); assert!(value.bits() <= segment.bit_range()); - self.push(value); + self.push(value)?; + Ok(()) } - fn run_mload_32bytes(&mut self) { - let context = self.pop().as_usize(); - let segment = Segment::all()[self.pop().as_usize()]; - let offset = self.pop().as_usize(); - let len = self.pop().as_usize(); + fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { + let context = self.pop()?.as_usize(); + let segment = Segment::all()[self.pop()?.as_usize()]; + let offset = self.pop()?.as_usize(); + let len = self.pop()?.as_usize(); + if len > 32 { + return Err(ProgramError::IntegerTooLarge); + } let bytes: Vec = (0..len) .map(|i| { self.generation_state @@ -867,24 +1087,28 @@ impl<'a> Interpreter<'a> { }) .collect(); let value = U256::from_big_endian(&bytes); - self.push(value); + self.push(value)?; + Ok(()) } - fn run_mstore_general(&mut self) { - let value = self.pop(); - let context = self.pop().as_usize(); - let segment = Segment::all()[self.pop().as_usize()]; - let offset = self.pop().as_usize(); - self.generation_state + fn run_mstore_general(&mut self) -> anyhow::Result<(), ProgramError> { + let value = self.pop()?; + let context = self.pop()?.as_usize(); + let segment = Segment::all()[self.pop()?.as_usize()]; + let offset = self.pop()?.as_usize(); + let memop = self + .generation_state .memory .mstore_general(context, segment, offset, value); + self.memops.push(memop); + Ok(()) } - fn run_mstore_32bytes(&mut self, n: u8) { - let context = self.pop().as_usize(); - let segment = Segment::all()[self.pop().as_usize()]; - let offset = self.pop().as_usize(); - let value = self.pop(); + fn run_mstore_32bytes(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { + let context = self.pop()?.as_usize(); + let segment = Segment::all()[self.pop()?.as_usize()]; + let offset = self.pop()?.as_usize(); + let value = self.pop()?; let mut bytes = vec![0; 32]; value.to_little_endian(&mut bytes); @@ -892,16 +1116,21 @@ impl<'a> Interpreter<'a> { bytes.reverse(); for (i, &byte) in bytes.iter().enumerate() { - self.generation_state - .memory - .mstore_general(context, segment, offset + i, byte.into()); + let memop = self.generation_state.memory.mstore_general( + context, + segment, + offset + i, + byte.into(), + ); + self.memops.push(memop); } self.push(U256::from(offset + n as usize)); + Ok(()) } - fn run_exit_kernel(&mut self) { - let kexit_info = self.pop(); + fn run_exit_kernel(&mut self) -> anyhow::Result<(), ProgramError> { + let kexit_info = self.pop()?; let kexit_info_u64 = kexit_info.0[0]; let program_counter = kexit_info_u64 as u32 as usize; @@ -909,24 +1138,58 @@ impl<'a> Interpreter<'a> { assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1); let is_kernel_mode = is_kernel_mode_val != 0; let gas_used_val = kexit_info.0[3]; - if TryInto::::try_into(gas_used_val).is_err() { - panic!("Gas overflow"); - } + TryInto::::try_into(gas_used_val).map_err(|_| ProgramError::GasLimitError)?; self.generation_state.registers.program_counter = program_counter; self.set_is_kernel(is_kernel_mode); self.generation_state.registers.gas_used = gas_used_val; + + Ok(()) + } + + fn run_exception(&mut self, exc_code: u8) -> Result<(), ProgramError> { + let disallowed_len = MAX_USER_STACK_SIZE + 1; + + if self.stack_len() == disallowed_len { + // This is a stack overflow that should have been caught earlier. + return Err(ProgramError::StackOverflow); + }; + + let handler_jumptable_addr = KERNEL.global_labels["exception_jumptable"]; + let handler_addr = { + let offset = handler_jumptable_addr + (exc_code as usize) * (BYTES_PER_OFFSET as usize); + assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); + self.get_memory_segment(Segment::Code)[offset..offset + 3] + .iter() + .fold(U256::from(0), |acc, &elt| acc * 256 + elt) + }; + + let new_program_counter = u256_to_usize(handler_addr)?; + + let exc_info = U256::from(self.generation_state.registers.program_counter) + + (U256::from(self.generation_state.registers.gas_used) << 192); + + self.push(exc_info)?; + + // Set registers before pushing to the stack; in particular, we need to set kernel mode so we + // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we + // make `exc_info`, which should contain the old values. + self.generation_state.registers.program_counter = new_program_counter; + self.set_is_kernel(true); + self.generation_state.registers.gas_used = 0; + + Ok(()) } pub(crate) const fn stack_len(&self) -> usize { self.generation_state.registers.stack_len } - pub(crate) fn stack_top(&self) -> anyhow::Result { + pub(crate) fn stack_top(&self) -> anyhow::Result { if self.stack_len() > 0 { Ok(self.generation_state.registers.stack_top) } else { - Err(anyhow!("Stack underflow")) + Err(ProgramError::StackUnderflow) } } diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index dadfaf4ba0..20c98bf976 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -115,9 +115,15 @@ fn prepare_interpreter( trie_data.push(account.code_hash.into_uint()); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(value_ptr.into()); // value_ptr - interpreter.push(k.try_into_u256().unwrap()); // key + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value_ptr.into()) + .expect("The stack should not overflow"); // value_ptr + interpreter + .push(k.try_into_u256().unwrap()) + .expect("The stack should not overflow"); // key interpreter.run()?; assert_eq!( @@ -129,8 +135,12 @@ fn prepare_interpreter( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initial length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( @@ -162,11 +172,15 @@ fn test_extcodesize() -> Result<()> { // Test `extcodesize` interpreter.generation_state.registers.program_counter = extcodesize; - interpreter.pop(); - interpreter.pop(); + interpreter.pop().expect("The stack should not be empty"); + interpreter.pop().expect("The stack should not be empty"); assert!(interpreter.stack().is_empty()); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(U256::from_big_endian(address.as_bytes())); + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(U256::from_big_endian(address.as_bytes())) + .expect("The stack should not overflow"); interpreter.generation_state.inputs.contract_code = HashMap::from([(keccak(&code), code.clone())]); interpreter.run()?; @@ -211,14 +225,24 @@ fn test_extcodecopy() -> Result<()> { // Test `extcodecopy` interpreter.generation_state.registers.program_counter = extcodecopy; - interpreter.pop(); - interpreter.pop(); + interpreter.pop().expect("The stack should not be empty"); + interpreter.pop().expect("The stack should not be empty"); assert!(interpreter.stack().is_empty()); - interpreter.push(size.into()); - interpreter.push(offset.into()); - interpreter.push(dest_offset.into()); - interpreter.push(U256::from_big_endian(address.as_bytes())); - interpreter.push((0xDEADBEEFu64 + (1 << 32)).into()); // kexit_info + interpreter + .push(size.into()) + .expect("The stack should not overflow"); + interpreter + .push(offset.into()) + .expect("The stack should not overflow"); + interpreter + .push(dest_offset.into()) + .expect("The stack should not overflow"); + interpreter + .push(U256::from_big_endian(address.as_bytes())) + .expect("The stack should not overflow"); + interpreter + .push((0xDEADBEEFu64 + (1 << 32)).into()) + .expect("The stack should not overflow"); // kexit_info interpreter.generation_state.inputs.contract_code = HashMap::from([(keccak(&code), code.clone())]); interpreter.run()?; @@ -342,8 +366,12 @@ fn sstore() -> Result<()> { interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.set_is_kernel(true); interpreter.set_context(0); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initia length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( @@ -402,27 +430,38 @@ fn sload() -> Result<()> { // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter_all_accounts(&mut interpreter, trie_inputs, addr, &code)?; - interpreter.run()?; // The first two elements in the stack are `success` and `leftover_gas`, // returned by the `sys_stop` opcode. - interpreter.pop(); - interpreter.pop(); + interpreter + .pop() + .expect("The stack length should not be empty."); + interpreter + .pop() + .expect("The stack length should not be empty."); // The SLOAD in the provided code should return 0, since // the storage trie is empty. The last step in the code // pushes the value 3. assert_eq!(interpreter.stack(), vec![0x0.into(), 0x3.into()]); - interpreter.pop(); - interpreter.pop(); + interpreter + .pop() + .expect("The stack length should not be empty."); + interpreter + .pop() + .expect("The stack length should not be empty."); // Now, execute mpt_hash_state_trie. We check that the state trie has not changed. let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; interpreter.set_is_kernel(true); interpreter.set_context(0); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initia length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow."); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow."); interpreter.run()?; assert_eq!( diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index a48bd450bc..9ba65db2c9 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -204,3 +204,137 @@ fn test_add11_yml() { interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } + +#[test] +fn test_add11_yml_with_exception() { + // In this test, we make sure that the user code throws a stack underflow exception. + let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); + let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); + let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); + + let beneficiary_state_key = keccak(beneficiary); + let sender_state_key = keccak(sender); + let to_hashed = keccak(to); + + let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); + + let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x8e, 0x00]; + let code_hash = keccak(code); + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(code_hash, code.to_vec()); + + let beneficiary_account_before = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + let sender_account_before = AccountRlp { + balance: 0x0de0b6b3a7640000u64.into(), + ..AccountRlp::default() + }; + let to_account_before = AccountRlp { + balance: 0x0de0b6b3a7640000u64.into(), + code_hash, + ..AccountRlp::default() + }; + + let mut state_trie_before = HashedPartialTrie::from(Node::Empty); + state_trie_before.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_before).to_vec(), + ); + state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); + state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: Node::Empty.into(), + receipts_trie: Node::Empty.into(), + storage_tries: vec![(to_hashed, Node::Empty.into())], + }; + + let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); + let txn_gas_limit = 400_000; + let gas_price = 10; + + let initial_stack = vec![]; + let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + + prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code); + // Here, since the transaction fails, it consumes its gas limit, and does nothing else. + let expected_state_trie_after = { + let beneficiary_account_after = beneficiary_account_before; + // This is the only account that changes: the nonce and the balance are updated. + let sender_account_after = AccountRlp { + balance: sender_account_before.balance - txn_gas_limit * gas_price, + nonce: 1.into(), + ..AccountRlp::default() + }; + let to_account_after = to_account_before; + + let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); + expected_state_trie_after.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_after).to_vec(), + ); + expected_state_trie_after + .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); + expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); + expected_state_trie_after + }; + + let receipt_0 = LegacyReceiptRlp { + status: false, + cum_gas_used: txn_gas_limit.into(), + bloom: vec![0; 256].into(), + logs: vec![], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + ); + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.hash(), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + + // Set trie roots after the transaction was executed. + let metadata_to_set = [ + ( + GlobalMetadata::StateTrieRootDigestAfter, + h2u(trie_roots_after.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + h2u(trie_roots_after.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + h2u(trie_roots_after.receipts_root), + ), + // The gas used in this case is the transaction gas limit + (GlobalMetadata::BlockGasUsedAfter, txn_gas_limit.into()), + ]; + interpreter.set_global_metadata_multi_fields(&metadata_to_set); + + let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; + // Switch context and initialize memory with the data we need for the tests. + interpreter.generation_state.registers.program_counter = route_txn_label; + interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] + .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.set_is_kernel(true); + interpreter + .run() + .expect("Proving add11 with exception failed."); +} diff --git a/evm/src/cpu/kernel/tests/balance.rs b/evm/src/cpu/kernel/tests/balance.rs index d7f92f3dfe..22b484e779 100644 --- a/evm/src/cpu/kernel/tests/balance.rs +++ b/evm/src/cpu/kernel/tests/balance.rs @@ -58,9 +58,15 @@ fn prepare_interpreter( trie_data.push(account.code_hash.into_uint()); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(value_ptr.into()); // value_ptr - interpreter.push(k.try_into_u256().unwrap()); // key + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value_ptr.into()) + .expect("The stack should not overflow"); // value_ptr + interpreter + .push(k.try_into_u256().unwrap()) + .expect("The stack should not overflow"); // key interpreter.run()?; assert_eq!( @@ -72,8 +78,12 @@ fn prepare_interpreter( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initial trie data segment size, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial trie data segment size, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( @@ -104,11 +114,15 @@ fn test_balance() -> Result<()> { // Test `balance` interpreter.generation_state.registers.program_counter = KERNEL.global_labels["balance"]; - interpreter.pop(); - interpreter.pop(); + interpreter.pop().expect("The stack should not be empty"); + interpreter.pop().expect("The stack should not be empty"); assert!(interpreter.stack().is_empty()); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(U256::from_big_endian(address.as_bytes())); + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(U256::from_big_endian(address.as_bytes())) + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!(interpreter.stack(), vec![balance]); diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs index edce3e58c5..acb75b7b21 100644 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm/src/cpu/kernel/tests/mpt/delete.rs @@ -94,9 +94,15 @@ fn test_state_trie( trie_data.push(account.code_hash.into_uint()); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(value_ptr.into()); // value_ptr - interpreter.push(k.try_into_u256().unwrap()); // key + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value_ptr.into()) + .expect("The stack should not overflow"); // value_ptr + interpreter + .push(k.try_into_u256().unwrap()) + .expect("The stack should not overflow"); // key interpreter.run()?; assert_eq!( interpreter.stack().len(), @@ -108,21 +114,34 @@ fn test_state_trie( // Next, execute mpt_delete, deleting the account we just inserted. let state_trie_ptr = interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot); interpreter.generation_state.registers.program_counter = mpt_delete; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(k.try_into_u256().unwrap()); - interpreter.push(64.into()); - interpreter.push(state_trie_ptr); + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(k.try_into_u256().unwrap()) + .expect("The stack should not overflow"); + interpreter + .push(64.into()) + .expect("The stack should not overflow"); + interpreter + .push(state_trie_ptr) + .expect("The stack should not overflow"); interpreter.run()?; - let state_trie_ptr = interpreter.pop(); + let state_trie_ptr = interpreter.pop().expect("The stack should not be empty"); interpreter.set_global_metadata_field(GlobalMetadata::StateTrieRoot, state_trie_ptr); // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initial length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; - let state_trie_hash = H256::from_uint(&interpreter.pop()); + let state_trie_hash = + H256::from_uint(&interpreter.pop().expect("The stack should not be empty")); let expected_state_trie_hash = state_trie.hash(); assert_eq!(state_trie_hash, expected_state_trie_hash); diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs index 2902e56609..f432144af2 100644 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm/src/cpu/kernel/tests/mpt/hash.rs @@ -118,8 +118,12 @@ fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initial length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index e68e7d2fd0..9c2fd50b24 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -196,9 +196,15 @@ fn test_state_trie( trie_data.push(account.code_hash.into_uint()); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(value_ptr.into()); // value_ptr - interpreter.push(k.try_into_u256().unwrap()); // key + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value_ptr.into()) + .expect("The stack should not overflow"); // value_ptr + interpreter + .push(k.try_into_u256().unwrap()) + .expect("The stack should not overflow"); // key interpreter.run()?; assert_eq!( @@ -210,8 +216,12 @@ fn test_state_trie( // Now, execute mpt_hash_state_trie. interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(1.into()); // Initial length of the trie data segment, unused. + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( diff --git a/evm/src/cpu/kernel/tests/mpt/read.rs b/evm/src/cpu/kernel/tests/mpt/read.rs index 67fea585d3..d5913f9867 100644 --- a/evm/src/cpu/kernel/tests/mpt/read.rs +++ b/evm/src/cpu/kernel/tests/mpt/read.rs @@ -26,10 +26,18 @@ fn mpt_read() -> Result<()> { // Now, execute mpt_read on the state trie. interpreter.generation_state.registers.program_counter = mpt_read; - interpreter.push(0xdeadbeefu32.into()); - interpreter.push(0xABCDEFu64.into()); - interpreter.push(6.into()); - interpreter.push(interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot)); + interpreter + .push(0xdeadbeefu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(0xABCDEFu64.into()) + .expect("The stack should not overflow"); + interpreter + .push(6.into()) + .expect("The stack should not overflow"); + interpreter + .push(interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot)) + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!(interpreter.stack().len(), 1); diff --git a/evm/src/cpu/kernel/tests/receipt.rs b/evm/src/cpu/kernel/tests/receipt.rs index 503a4a6a29..d185948b0a 100644 --- a/evm/src/cpu/kernel/tests/receipt.rs +++ b/evm/src/cpu/kernel/tests/receipt.rs @@ -194,7 +194,7 @@ fn test_receipt_encoding() -> Result<()> { interpreter.set_memory_segment(Segment::TrieData, receipt); interpreter.run()?; - let rlp_pos = interpreter.pop(); + let rlp_pos = interpreter.pop().expect("The stack should not be empty"); let rlp_read: Vec = interpreter.get_rlp_memory(); @@ -295,7 +295,9 @@ fn test_receipt_bloom_filter() -> Result<()> { .map(U256::from); logs2.extend(cur_data); - interpreter.push(retdest); + interpreter + .push(retdest) + .expect("The stack should not overflow"); interpreter.generation_state.registers.program_counter = logs_bloom; interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); // Initialize transaction Bloom filter. interpreter.set_memory_segment(Segment::LogsData, logs2); @@ -427,7 +429,9 @@ fn test_mpt_insert_receipt() -> Result<()> { num_nibbles.into(), ]; for i in 0..initial_stack.len() { - interpreter.push(initial_stack[i]); + interpreter + .push(initial_stack[i]) + .expect("The stack should not overflow"); } interpreter.generation_state.registers.program_counter = mpt_insert; @@ -497,7 +501,9 @@ fn test_mpt_insert_receipt() -> Result<()> { num_nibbles.into(), ]; for i in 0..initial_stack2.len() { - interpreter.push(initial_stack2[i]); + interpreter + .push(initial_stack2[i]) + .expect("The stack should not overflow"); } cur_trie_data.extend(receipt_1); @@ -510,8 +516,12 @@ fn test_mpt_insert_receipt() -> Result<()> { // Finally, check that the hashes correspond. let mpt_hash_receipt = KERNEL.global_labels["mpt_hash_receipt_trie"]; interpreter.generation_state.registers.program_counter = mpt_hash_receipt; - interpreter.push(retdest); - interpreter.push(1.into()); // Initial length of the trie data segment, unused. + interpreter + .push(retdest) + .expect("The stack should not overflow"); + interpreter + .push(1.into()) // Initial length of the trie data segment, unused.; // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); interpreter.run()?; assert_eq!( interpreter.stack()[1], From 18e08f4f61305503877569a8f31a4f9d27462782 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:27:12 +0100 Subject: [PATCH 034/175] Filter range checks (#1433) * Add filtering to range-checks * Cleanup * Fix Clippy * Apply comment --- evm/src/arithmetic/arithmetic_stark.rs | 11 +- evm/src/byte_packing/byte_packing_stark.rs | 9 +- evm/src/cross_table_lookup.rs | 16 +++ evm/src/keccak_sponge/keccak_sponge_stark.rs | 9 +- evm/src/lookup.rs | 129 ++++++++++++++----- evm/src/memory/memory_stark.rs | 9 +- evm/src/prover.rs | 4 +- evm/src/stark.rs | 2 +- evm/src/vanishing_poly.rs | 2 +- 9 files changed, 136 insertions(+), 55 deletions(-) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 6b0c9fa5f5..52fc2f7893 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -14,7 +14,7 @@ use static_assertions::const_assert; use super::columns::NUM_ARITH_COLUMNS; use super::shift; use crate::all_stark::Table; -use crate::arithmetic::columns::{RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; +use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; @@ -290,11 +290,12 @@ impl, const D: usize> Stark for ArithmeticSta 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: SHARED_COLS.collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(SHARED_COLS).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; NUM_SHARED_COLS], }] } } diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index e5596b3e2c..0de8968aa0 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -391,11 +391,12 @@ impl, const D: usize> Stark for BytePackingSt 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: (value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; NUM_BYTES], }] } } diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index e8e9c1c283..65b27b1392 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -259,6 +259,14 @@ impl Column { res } + /// Evaluates the column on all rows. + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } + /// Circuit version of `eval`: Given a row's targets, returns their linear combination. pub(crate) fn eval_circuit( &self, @@ -401,6 +409,14 @@ impl Filter { .map(|col| col.eval_table(table, row)) .sum() } + + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } } /// A `Table` with a linear combination of columns and a filter. diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 75e0ced37f..2cfc3409ec 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -800,11 +800,12 @@ impl, const D: usize> Stark for KeccakSpongeS 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: get_block_bytes_range().collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(get_block_bytes_range()).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; KECCAK_RATE_BYTES], }] } } diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index a93e4fb348..7944d78b70 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -1,6 +1,6 @@ use itertools::Itertools; use num_bigint::BigUint; -use plonky2::field::batch_util::batch_add_inplace; +use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -12,22 +12,27 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::stark::Stark; -pub struct Lookup { +pub struct Lookup { /// Columns whose values should be contained in the lookup table. /// These are the f_i(x) polynomials in the logUp paper. - pub(crate) columns: Vec, + pub(crate) columns: Vec>, /// Column containing the lookup table. /// This is the t(x) polynomial in the paper. - pub(crate) table_column: usize, + pub(crate) table_column: Column, /// Column containing the frequencies of `columns` in `table_column`. /// This is the m(x) polynomial in the paper. - pub(crate) frequencies_column: usize, + pub(crate) frequencies_column: Column, + + /// Columns to filter some elements. There is at most one filter + /// column per column to range-check. + pub(crate) filter_columns: Vec>>, } -impl Lookup { +impl Lookup { pub(crate) fn num_helper_columns(&self, constraint_degree: usize) -> usize { // One helper column for each column batch of size `constraint_degree-1`, // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. @@ -41,7 +46,7 @@ impl Lookup { /// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`, /// and `Z(gx) = Z(x) + sum h_i(x) - m(x)g(x)` where `m` is the frequencies column. pub(crate) fn lookup_helper_columns( - lookup: &Lookup, + lookup: &Lookup, trace_poly_values: &[PolynomialValues], challenge: F, constraint_degree: usize, @@ -51,6 +56,8 @@ pub(crate) fn lookup_helper_columns( "TODO: Allow other constraint degrees." ); + assert_eq!(lookup.columns.len(), lookup.filter_columns.len()); + let num_total_logup_entries = trace_poly_values[0].values.len() * lookup.columns.len(); assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); @@ -67,30 +74,43 @@ pub(crate) fn lookup_helper_columns( // h_k polynomials; instead there's a separate helper column for it (see below). // * Here, we use 1 instead of -1 as the numerator (and subtract later). // * Here, for now, the batch size (l) is always constraint_degree - 1 = 2. - for mut col_inds in &lookup.columns.iter().chunks(constraint_degree - 1) { - let first = *col_inds.next().unwrap(); - // TODO: The clone could probably be avoided by using a modified version of `batch_multiplicative_inverse` - // taking `challenge` as an additional argument. - let mut column = trace_poly_values[first].values.clone(); + for (i, mut col_inds) in (&lookup.columns.iter().chunks(constraint_degree - 1)) + .into_iter() + .enumerate() + { + let first = col_inds.next().unwrap(); + + let mut column = first.eval_all_rows(trace_poly_values); + let length = column.len(); + for x in column.iter_mut() { *x = challenge + *x; } let mut acc = F::batch_multiplicative_inverse(&column); - for &ind in col_inds { - let mut column = trace_poly_values[ind].values.clone(); + if let Some(filter) = &lookup.filter_columns[0] { + batch_multiply_inplace(&mut acc, &filter.eval_all_rows(trace_poly_values)); + } + + for (j, ind) in col_inds.enumerate() { + let mut column = ind.eval_all_rows(trace_poly_values); for x in column.iter_mut() { *x = challenge + *x; } column = F::batch_multiplicative_inverse(&column); + let filter_idx = (constraint_degree - 1) * i + j + 1; + if let Some(filter) = &lookup.filter_columns[filter_idx] { + batch_multiply_inplace(&mut column, &filter.eval_all_rows(trace_poly_values)); + } batch_add_inplace(&mut acc, &column); } + helper_columns.push(acc.into()); } // Add `1/(table+challenge)` to the helper columns. // This is 1/phi_0(x) = 1/(x + t(x)) from the paper. // Here, we don't include m(x) in the numerator, instead multiplying it with this column later. - let mut table = trace_poly_values[lookup.table_column].values.clone(); + let mut table = lookup.table_column.eval_all_rows(trace_poly_values); for x in table.iter_mut() { *x = challenge + *x; } @@ -100,7 +120,7 @@ pub(crate) fn lookup_helper_columns( // This enforces the check from the paper, that the sum of the h_k(x) polynomials is 0 over H. // In the paper, that sum includes m(x)/(x + t(x)) = frequencies(x)/g(x), because that was bundled // into the h_k(x) polynomials. - let frequencies = &trace_poly_values[lookup.frequencies_column].values; + let frequencies = &lookup.frequencies_column.eval_all_rows(trace_poly_values); let mut z = Vec::with_capacity(frequencies.len()); z.push(F::ZERO); for i in 0..frequencies.len() - 1 { @@ -130,7 +150,7 @@ where /// Constraints for the logUp lookup argument. pub(crate) fn eval_packed_lookups_generic( stark: &S, - lookups: &[Lookup], + lookups: &[Lookup], vars: &S::EvaluationFrame, lookup_vars: LookupCheckVars, yield_constr: &mut ConstraintConsumer

, @@ -140,6 +160,8 @@ pub(crate) fn eval_packed_lookups_generic, S: Stark, { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); let degree = stark.constraint_degree(); assert_eq!(degree, 3, "TODO: Allow other constraint degrees."); let mut start = 0; @@ -147,19 +169,34 @@ pub(crate) fn eval_packed_lookups_generic>(); + let last_filter_value = filters[0]; + for (val, f) in col_values.zip_eq(filters) { + x *= val + challenge; + y += (val + challenge) * f; } match chunk.len() { 2 => yield_constr.constraint(x - y), - 1 => yield_constr.constraint(x - P::ONES), + 1 => yield_constr.constraint(x - last_filter_value), _ => todo!("Allow other constraint degrees."), } } @@ -167,12 +204,12 @@ pub(crate) fn eval_packed_lookups_generic>(); + let filters = lookup.filter_columns + [(degree - 1) * j..(degree - 1) * j + chunk.len()] + .iter() + .map(|maybe_filter| { + if let Some(filter) = maybe_filter { + filter.eval_filter_circuit(builder, local_values, next_values) + } else { + one + } + }) + .rev() + .collect::>(); + let last_filter_value = filters[0]; + for (&val, f) in col_values.iter().zip_eq(filters) { + let tmp = builder.add_extension(val, challenge); x = builder.mul_extension(x, tmp); - y = builder.add_extension(y, tmp); + y = builder.mul_add_extension(f, tmp, y); } match chunk.len() { 2 => { @@ -220,7 +276,7 @@ pub(crate) fn eval_ext_lookups_circuit< yield_constr.constraint(builder, tmp) } 1 => { - let tmp = builder.sub_extension(x, one); + let tmp = builder.sub_extension(x, last_filter_value); yield_constr.constraint(builder, tmp) } _ => todo!("Allow other constraint degrees."), @@ -229,14 +285,19 @@ pub(crate) fn eval_ext_lookups_circuit< let z = lookup_vars.local_values[start + num_helper_columns - 1]; let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; - let table_with_challenge = - builder.add_extension(vars.get_local_values()[lookup.table_column], challenge); + let table_column = lookup + .table_column + .eval_circuit(builder, vars.get_local_values()); + let table_with_challenge = builder.add_extension(table_column, challenge); let mut y = builder.add_many_extension( &lookup_vars.local_values[start..start + num_helper_columns - 1], ); + let frequencies_column = lookup + .frequencies_column + .eval_circuit(builder, vars.get_local_values()); y = builder.mul_extension(y, table_with_challenge); - y = builder.sub_extension(y, vars.get_local_values()[lookup.frequencies_column]); + y = builder.sub_extension(y, frequencies_column); let mut constraint = builder.sub_extension(next_z, z); constraint = builder.mul_extension(constraint, table_with_challenge); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 4dfdc913f1..666f4e1165 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -528,11 +528,12 @@ impl, const D: usize> Stark for MemoryStark Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: vec![RANGE_CHECK], - table_column: COUNTER, - frequencies_column: FREQUENCIES, + columns: vec![Column::single(RANGE_CHECK)], + table_column: Column::single(COUNTER), + frequencies_column: Column::single(FREQUENCIES), + filter_columns: vec![None], }] } } diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 32989c8fc0..cc748f169b 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -532,7 +532,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, auxiliary_polys_commitment: &'a PolynomialBatch, lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], + lookups: &[Lookup], ctl_data: &CtlData, alphas: Vec, degree_bits: usize, @@ -688,7 +688,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, auxiliary_commitment: &'a PolynomialBatch, lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], + lookups: &[Lookup], ctl_data: &CtlData, alphas: Vec, degree_bits: usize, diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 51a3ec1b14..6099abdd38 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -207,7 +207,7 @@ pub trait Stark, const D: usize>: Sync { } } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![] } diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs index 2e1adfc742..15444c7e98 100644 --- a/evm/src/vanishing_poly.rs +++ b/evm/src/vanishing_poly.rs @@ -19,7 +19,7 @@ use crate::stark::Stark; pub(crate) fn eval_vanishing_poly( stark: &S, vars: &S::EvaluationFrame, - lookups: &[Lookup], + lookups: &[Lookup], lookup_vars: Option>, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, From 096c7456bbf70a81810d9a81b382a2460deec247 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Tue, 19 Dec 2023 03:57:20 -0500 Subject: [PATCH 035/175] Constrain new top to loaded value in MLOAD_GENERAL (#1434) --- evm/src/cpu/memio.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 3fcdfc96ee..6ea8208989 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -43,6 +43,15 @@ fn eval_packed_load( yield_constr.constraint(filter * (load_channel.addr_segment - addr_segment)); yield_constr.constraint(filter * (load_channel.addr_virtual - addr_virtual)); + // Constrain the new top of the stack. + for (&limb_loaded, &limb_new_top) in load_channel + .value + .iter() + .zip(nv.mem_channels[0].value.iter()) + { + yield_constr.constraint(filter * (limb_loaded - limb_new_top)); + } + // Disable remaining memory channels, if any. for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS] { yield_constr.constraint(filter * channel.used); @@ -95,6 +104,17 @@ fn eval_ext_circuit_load, const D: usize>( yield_constr.constraint(builder, constr); } + // Constrain the new top of the stack. + for (&limb_loaded, &limb_new_top) in load_channel + .value + .iter() + .zip(nv.mem_channels[0].value.iter()) + { + let diff = builder.sub_extension(limb_loaded, limb_new_top); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + } + // Disable remaining memory channels, if any. for &channel in &lv.mem_channels[4..] { let constr = builder.mul_extension(filter, channel.used); From 7cb048842921fb709c4cc1d6b3fa846c24ac3f85 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:06:54 +0100 Subject: [PATCH 036/175] Minor cleanup (#1435) --- evm/src/cpu/kernel/interpreter.rs | 112 ++++++++++-------------------- 1 file changed, 36 insertions(+), 76 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5fbb8cd1f4..c437672113 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -242,9 +242,7 @@ impl<'a> Interpreter<'a> { }; self.run_exception(exc_code) - .map_err(|_| anyhow::Error::msg("error handling errored..."))?; - - Ok(()) + .map_err(|_| anyhow::Error::msg("error handling errored...")) } pub(crate) fn run(&mut self) -> anyhow::Result<()> { @@ -481,8 +479,7 @@ impl<'a> Interpreter<'a> { } fn push_bool(&mut self, x: bool) -> Result<(), ProgramError> { - self.push(if x { U256::one() } else { U256::zero() })?; - Ok(()) + self.push(if x { U256::one() } else { U256::zero() }) } pub(crate) fn pop(&mut self) -> Result { @@ -669,30 +666,26 @@ impl<'a> Interpreter<'a> { fn run_add(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x.overflowing_add(y).0)?; - Ok(()) + self.push(x.overflowing_add(y).0) } fn run_mul(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x.overflowing_mul(y).0)?; - Ok(()) + self.push(x.overflowing_mul(y).0) } fn run_sub(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x.overflowing_sub(y).0)?; - Ok(()) + self.push(x.overflowing_sub(y).0) } fn run_addfp254(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()? % BN_BASE; let y = self.pop()? % BN_BASE; // BN_BASE is 254-bit so addition can't overflow - self.push((x + y) % BN_BASE)?; - Ok(()) + self.push((x + y) % BN_BASE) } fn run_mulfp254(&mut self) -> anyhow::Result<(), ProgramError> { @@ -701,30 +694,26 @@ impl<'a> Interpreter<'a> { self.push( U256::try_from(x.full_mul(y) % BN_BASE) .expect("BN_BASE is 254 bit so the U512 fits in a U256"), - )?; - Ok(()) + ) } fn run_subfp254(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()? % BN_BASE; let y = self.pop()? % BN_BASE; // BN_BASE is 254-bit so addition can't overflow - self.push((x + (BN_BASE - y)) % BN_BASE)?; - Ok(()) + self.push((x + (BN_BASE - y)) % BN_BASE) } fn run_div(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(if y.is_zero() { U256::zero() } else { x / y })?; - Ok(()) + self.push(if y.is_zero() { U256::zero() } else { x / y }) } fn run_mod(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(if y.is_zero() { U256::zero() } else { x % y })?; - Ok(()) + self.push(if y.is_zero() { U256::zero() } else { x % y }) } fn run_addmod(&mut self) -> anyhow::Result<(), ProgramError> { @@ -737,9 +726,7 @@ impl<'a> Interpreter<'a> { let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); U256::try_from((x + y) % z) .expect("Inputs are U256 and their sum mod a U256 fits in a U256.") - })?; - - Ok(()) + }) } fn run_submod(&mut self) -> anyhow::Result<(), ProgramError> { @@ -752,9 +739,7 @@ impl<'a> Interpreter<'a> { let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); U256::try_from((z + x - y) % z) .expect("Inputs are U256 and their difference mod a U256 fits in a U256.") - })?; - - Ok(()) + }) } fn run_mulmod(&mut self) -> anyhow::Result<(), ProgramError> { @@ -766,62 +751,53 @@ impl<'a> Interpreter<'a> { } else { U256::try_from(x.full_mul(y) % z) .expect("Inputs are U256 and their product mod a U256 fits in a U256.") - })?; - Ok(()) + }) } fn run_lt(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push_bool(x < y)?; - Ok(()) + self.push_bool(x < y) } fn run_gt(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push_bool(x > y)?; - Ok(()) + self.push_bool(x > y) } fn run_eq(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push_bool(x == y)?; - Ok(()) + self.push_bool(x == y) } fn run_iszero(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; - self.push_bool(x.is_zero())?; - Ok(()) + self.push_bool(x.is_zero()) } fn run_and(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x & y)?; - Ok(()) + self.push(x & y) } fn run_or(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x | y)?; - Ok(()) + self.push(x | y) } fn run_xor(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; let y = self.pop()?; - self.push(x ^ y)?; - Ok(()) + self.push(x ^ y) } fn run_not(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; - self.push(!x)?; - Ok(()) + self.push(!x) } fn run_byte(&mut self) -> anyhow::Result<(), ProgramError> { @@ -832,8 +808,7 @@ impl<'a> Interpreter<'a> { } else { 0 }; - self.push(result.into())?; - Ok(()) + self.push(result.into()) } fn run_shl(&mut self) -> anyhow::Result<(), ProgramError> { @@ -843,15 +818,13 @@ impl<'a> Interpreter<'a> { value << shift } else { U256::zero() - })?; - Ok(()) + }) } fn run_shr(&mut self) -> anyhow::Result<(), ProgramError> { let shift = self.pop()?; let value = self.pop()?; - self.push(value >> shift)?; - Ok(()) + self.push(value >> shift) } fn run_keccak_general(&mut self) -> anyhow::Result<(), ProgramError> { @@ -871,8 +844,7 @@ impl<'a> Interpreter<'a> { .collect::>(); println!("Hashing {:?}", &bytes); let hash = keccak(bytes); - self.push(U256::from_big_endian(hash.as_bytes()))?; - Ok(()) + self.push(U256::from_big_endian(hash.as_bytes())) } fn run_prover_input(&mut self) -> Result<(), ProgramError> { @@ -883,13 +855,11 @@ impl<'a> Interpreter<'a> { ProverInputError::InvalidMptInput, ))?; let output = self.generation_state.prover_input(prover_input_fn)?; - self.push(output)?; - Ok(()) + self.push(output) } fn run_pop(&mut self) -> anyhow::Result<(), ProgramError> { - self.pop()?; - Ok(()) + self.pop().map(|_| ()) } fn run_syscall( @@ -929,9 +899,7 @@ impl<'a> Interpreter<'a> { self.set_is_kernel(true); self.generation_state.registers.gas_used = 0; - self.push(syscall_info)?; - - Ok(()) + self.push(syscall_info) } fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { @@ -940,8 +908,7 @@ impl<'a> Interpreter<'a> { let x: u32 = x .try_into() .map_err(|_| ProgramError::InvalidJumpDestination)?; - self.jump_to(x as usize)?; - Ok(()) + self.jump_to(x as usize) } fn run_jumpi(&mut self) -> anyhow::Result<(), ProgramError> { @@ -965,8 +932,7 @@ impl<'a> Interpreter<'a> { .program_counter .saturating_sub(1)) .into(), - )?; - Ok(()) + ) } fn run_jumpdest(&mut self) -> anyhow::Result<(), ProgramError> { @@ -991,8 +957,7 @@ impl<'a> Interpreter<'a> { fn run_push(&mut self, num_bytes: u8) -> anyhow::Result<(), ProgramError> { let x = U256::from_big_endian(&self.code_slice(num_bytes as usize)); self.incr(num_bytes as usize); - self.push(x)?; - Ok(()) + self.push(x) } fn run_dup(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { @@ -1003,8 +968,7 @@ impl<'a> Interpreter<'a> { if n as usize > self.stack_len() { return Err(ProgramError::StackUnderflow); } - self.push(stack_peek(&self.generation_state, n as usize - 1)?)?; - Ok(()) + self.push(stack_peek(&self.generation_state, n as usize - 1)?) } fn run_swap(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { @@ -1028,8 +992,7 @@ impl<'a> Interpreter<'a> { } fn run_get_context(&mut self) -> anyhow::Result<(), ProgramError> { - self.push(self.context().into())?; - Ok(()) + self.push(self.context().into()) } fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1066,8 +1029,7 @@ impl<'a> Interpreter<'a> { .memory .mload_general(context, segment, offset); assert!(value.bits() <= segment.bit_range()); - self.push(value)?; - Ok(()) + self.push(value) } fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1087,8 +1049,7 @@ impl<'a> Interpreter<'a> { }) .collect(); let value = U256::from_big_endian(&bytes); - self.push(value)?; - Ok(()) + self.push(value) } fn run_mstore_general(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1125,8 +1086,7 @@ impl<'a> Interpreter<'a> { self.memops.push(memop); } - self.push(U256::from(offset + n as usize)); - Ok(()) + self.push(U256::from(offset + n as usize)) } fn run_exit_kernel(&mut self) -> anyhow::Result<(), ProgramError> { From 829ae64fc42e167990a0fda7fb512c8008582987 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Tue, 19 Dec 2023 14:05:51 +0100 Subject: [PATCH 037/175] Improve proof generation --- evm/src/cpu/kernel/interpreter.rs | 21 +-- evm/src/generation/mod.rs | 10 +- evm/src/generation/prover_input.rs | 199 ++++++++++++++++++----------- evm/src/generation/state.rs | 9 +- 4 files changed, 144 insertions(+), 95 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index f007595a1e..db76ac4b38 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -293,16 +293,17 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); - self.generation_state.jumpdest_addresses = Some(HashMap::from([( - context, - BTreeSet::from_iter( - jumpdest_bits - .into_iter() - .enumerate() - .filter(|&(_, x)| x) - .map(|(i, _)| i), - ), - )])); + self.generation_state + .set_proofs_and_jumpdests(HashMap::from([( + context, + BTreeSet::from_iter( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i), + ), + )])); } pub(crate) fn incr(&mut self, n: usize) { diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index a49e291d63..2d5bca1ac1 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -288,9 +288,9 @@ fn simulate_cpu_between_labels_and_get_user_jumps( initial_label: &str, final_label: &str, state: &mut GenerationState, -) -> Result<(), ProgramError> { - if state.jumpdest_addresses.is_some() { - Ok(()) +) -> Result>>, ProgramError> { + if state.jumpdest_proofs.is_some() { + Ok(None) } else { const JUMP_OPCODE: u8 = 0x56; const JUMPI_OPCODE: u8 = 0x57; @@ -304,6 +304,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( log::debug!("Simulating CPU for jumpdest analysis."); loop { + // skip jumdest table validations in simulations if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] @@ -344,8 +345,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } if halt { log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - state.jumpdest_addresses = Some(jumpdest_addresses); - return Ok(()); + return Ok(Some(jumpdest_addresses)); } transition(state).map_err(|_| { ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 926b876da5..a5f73ae687 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,11 +1,10 @@ use std::cmp::min; -use std::collections::HashSet; +use std::collections::HashMap; use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; -use hashbrown::HashMap; use itertools::{enumerate, Itertools}; use num_bigint::BigUint; use plonky2::field::extension::Extendable; @@ -256,87 +255,98 @@ impl GenerationState { virt: ContextMetadata::CodeSize as usize, }))?; - if self.jumpdest_addresses.is_none() { - self.generate_jumpdest_table()?; + if self.jumpdest_proofs.is_none() { + self.generate_jumpdest_proofs()?; } - let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { return Err(ProgramError::ProverInputError( ProverInputError::InvalidJumpdestSimulation, )); }; - if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) - && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_address) = ctx_jumpdest_proofs.pop() { - self.last_jumpdest_address = next_jumpdest_address; Ok((next_jumpdest_address + 1).into()) } else { - self.jumpdest_addresses = None; + self.jumpdest_proofs = None; Ok(U256::zero()) } } /// Returns the proof for the last jump address. fn run_next_jumpdest_table_proof(&mut self) -> Result { - let code = (0..self.last_jumpdest_address) - .map(|i| { - u256_to_u8(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: i, - })) - }) - .collect::, _>>()?; + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); + }; + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_proof) = ctx_jumpdest_proofs.pop() + { + Ok(next_jumpdest_proof.into()) + } else { + Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )) + } + } +} - // TODO: The proof searching algorithm is not very efficient. But luckily it doesn't seem - // a problem as is done natively. +impl GenerationState { + fn generate_jumpdest_proofs(&mut self) -> Result<(), ProgramError> { + let checkpoint = self.checkpoint(); + let memory = self.memory.clone(); - // Search the closest address to `last_jumpdest_address` for which none of - // the previous 32 bytes in the code (including opcodes and pushed bytes) - // are PUSHXX and the address is in its range. + let code = self.get_current_code()?; + // We need to set the simulated jumpdest bits to one as otherwise + // the simulation will fail. + self.set_jumpdest_bits(&code); - const PUSH1_OPCODE: u8 = 0x60; - const PUSH32_OPCODE: u8 = 0x7f; - - let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( - 0, - |acc, (pos, opcode)| { - let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { - code[prefix_start..pos].iter().enumerate().fold( - true, - |acc, (prefix_pos, &byte)| { - acc && (byte > PUSH32_OPCODE - || (prefix_start + prefix_pos) as i32 - + (byte as i32 - PUSH1_OPCODE as i32) - + 1 - < pos as i32) - }, - ) - } else { - false - }; - if has_prefix { - pos - 32 + // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call + let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + self, + )? + else { + return Ok(()); + }; + + // Return to the state before starting the simulation + self.rollback(checkpoint); + self.memory = memory; + + // Find proofs for all context + self.set_proofs_and_jumpdests(jumpdest_table); + + Ok(()) + } + + pub(crate) fn set_proofs_and_jumpdests( + &mut self, + jumpdest_table: HashMap>, + ) { + self.jumpdest_proofs = Some(HashMap::from_iter(jumpdest_table.into_iter().map( + |(ctx, jumpdest_table)| { + let code = self.get_code(ctx).unwrap(); + if let Some(&largest_address) = jumpdest_table.last() { + let proofs = get_proofs_and_jumpdests(&code, largest_address, jumpdest_table); + (ctx, proofs) } else { - acc + (ctx, vec![]) } }, - ); - Ok(proof.into()) + ))); } -} -impl GenerationState { - fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { - const JUMPDEST_OPCODE: u8 = 0x5b; - let mut state = self.soft_clone(); - let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::CodeSize as usize, - }))?; - // Generate the jumpdest table + fn get_current_code(&self) -> Result, ProgramError> { + self.get_code(self.registers.context) + } + + fn get_code(&self, context: usize) -> Result, ProgramError> { + let code_len = self.get_code_len()?; let code = (0..code_len) .map(|i| { u256_to_u8(self.memory.get(MemoryAddress { @@ -346,16 +356,25 @@ impl GenerationState { })) }) .collect::, _>>()?; + Ok(code) + } - // We need to set the simulated jumpdest bits to one as otherwise - // the simulation will fail. - let mut jumpdest_table = Vec::with_capacity(code.len()); + fn get_code_len(&self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + Ok(code_len) + } + + fn set_jumpdest_bits<'a>(&mut self, code: &'a Vec) { + const JUMPDEST_OPCODE: u8 = 0x5b; for (pos, opcode) in CodeIterator::new(&code) { - jumpdest_table.push((pos, opcode == JUMPDEST_OPCODE)); if opcode == JUMPDEST_OPCODE { - state.memory.set( + self.memory.set( MemoryAddress { - context: state.registers.context, + context: self.registers.context, segment: Segment::JumpdestBits as usize, virt: pos, }, @@ -363,18 +382,50 @@ impl GenerationState { ); } } - - // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", - "terminate_common", - &mut state, - )?; - self.jumpdest_addresses = state.jumpdest_addresses; - Ok(()) } } +/// For each address in `jumpdest_table` it search a proof, that is the closest address +/// for which none of the previous 32 bytes in the code (including opcodes +/// and pushed bytes are PUSHXX and the address is in its range. It returns +/// a vector of even size containing proofs followed by their addresses +fn get_proofs_and_jumpdests<'a>( + code: &'a Vec, + largest_address: usize, + jumpdest_table: std::collections::BTreeSet, +) -> Vec { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x7f; + let (proofs, _) = CodeIterator::until(&code, largest_address + 1).fold( + (vec![], 0), + |(mut proofs, acc), (pos, opcode)| { + let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { + code[prefix_start..pos] + .iter() + .enumerate() + .fold(true, |acc, (prefix_pos, &byte)| { + acc && (byte > PUSH32_OPCODE + || (prefix_start + prefix_pos) as i32 + + (byte as i32 - PUSH1_OPCODE as i32) + + 1 + < pos as i32) + }) + } else { + false + }; + let acc = if has_prefix { pos - 32 } else { acc }; + if jumpdest_table.contains(&pos) { + // Push the proof + proofs.push(acc); + // Push the address + proofs.push(pos); + } + (proofs, acc) + }, + ); + proofs +} + struct CodeIterator<'a> { code: &'a [u8], pos: usize, diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 1c50cc294c..cc1df0919d 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -51,8 +51,7 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, - pub(crate) last_jumpdest_address: usize, - pub(crate) jumpdest_addresses: Option>>, + pub(crate) jumpdest_proofs: Option>>, } impl GenerationState { @@ -94,8 +93,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - last_jumpdest_address: 0, - jumpdest_addresses: None, + jumpdest_proofs: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -189,8 +187,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - last_jumpdest_address: 0, - jumpdest_addresses: None, + jumpdest_proofs: None, } } } From 7eff4e2751dea6ef67bd09b184599ff97f509ebf Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Tue, 19 Dec 2023 10:58:09 -0500 Subject: [PATCH 038/175] Constrain first offset of a segment (#1397) * Constrain first offset of a segment * Apply comment, revert debugging code * Modify specs * Apply comments --- evm/spec/tables/memory.tex | 5 ++++- evm/spec/zkevm.pdf | Bin 295781 -> 295982 bytes evm/src/memory/memory_stark.rs | 36 ++++++++++++++++++++++++++++----- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/evm/spec/tables/memory.tex b/evm/spec/tables/memory.tex index 883134d624..d39e99b23d 100644 --- a/evm/spec/tables/memory.tex +++ b/evm/spec/tables/memory.tex @@ -44,7 +44,7 @@ \subsection{Memory} \item $(1 - e_i) (a_{i + 1} - a_i) = 0$, \item $c_i < 2^{32}$. \end{enumerate} -The last constraint emulates a comparison between two addresses or timestamps by bounding their difference; this assumes that all addresses and timestamps fit in 32 bits and that the field is larger than that. +The third constraint emulates a comparison between two addresses or timestamps by bounding their difference; this assumes that all addresses and timestamps fit in 32 bits and that the field is larger than that. \subsubsection{Virtual memory} @@ -56,6 +56,9 @@ \subsubsection{Virtual memory} \end{enumerate} The comparisons now involve several columns, which requires some minor adaptations to the technique described above; we will leave these as an exercise to the reader. +Note that an additional constraint check is required: whenever we change the context or the segment, the virtual address must be range-checked to $2^{32}$. +Without this check, addresses could start at -1 (i.e. $p - 2$) and then increase properly. + \subsubsection{Timestamps} Memory operations are sorted by address $a$ and timestamp $\tau$. For a memory operation in the CPU, we have: diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 455491be7a3eb9fd96457e2d7645e161afcc5525..4f4f0a00b0effa6b21385b1cf3e66f00d8f93e83 100644 GIT binary patch delta 46918 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 z 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 delta 46735 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

fVy^ zmARl=f}NI5Us#5z2Ib>WY>d=7$SaE}NG?dqC*#zwXK?h5HfcEu1H)7`yK%r9#(hN@ zQ=g(pbdy@!mYPRS=z9H$oh*MEj_}CDgD)Hx97zrLa^Fi?K@PF+^(1n7B)j*SC*w1< zD}mSiPCy0;yZcpVr3fio$}<#y!EX=Q?3;P@O&4u$lUW&YJmM$Dl zontlM%#ksEdPF8Ckp8BcfUABW7i;_EFC^JL*{p4hcRwB<0uO1-lZpY(ASLmEbnpmi49E4bGF5fc=Wz<1qlZNW-SLUG#0N zamC-HQ}&v5@6A_;HB(%AU%>F~#*)22b1MtXrr;V&S@_%1qv};lgIDA0 z?efnMv|EF+f9BZnyts@;KiHjRcSpNqxxn(3Kl0N^y-}j`y6I-$b)g?9fcppP#9c`E z!o|{il)FiGkP5CM4TX%Bw;KUpvw$MvH6C0Bq|5C4!~rvlz#i!y$vYVs>o% zb`VWaebhy2%D0aH(Aih4eZ3woL$Rz%C-xZS2pv-Ni#`+<b*gwtcv{GUU3aeR=Y!Fif(in~eyuKQTXC z&f9OLaNUQo9AMB2;1|r+?1hV9ASC-kQ)RLDX}(pbC#*K*i~CEx*&Ud-RmpLl&muE? zsL>kj`x~K|&6de8cDeO2QEl(8C)DboP%C8ce)1o*>_TFw*;#%Xmo0Q<(cCM{X?^6PxuG74Oh39uIeF$eS+9 zi3(WMBWyZ!>Iu{U&s}!jZ<#cm=EEaSsI=cfh}dCF1|Flsmv$0s6HQU4V;w zu{qvT1A8l%7CW&8iU^jGMI*H=hso)}n^!^>TDhJq4ap8j&DeUita4)J5%z>psx&c$ z2=3qZ&DfCXdPV?SGO=4~UEL9BzHMt87?&|eTrDW<>NaWsx?3Ul&S4K{zG!la%Aug4 zlGsCEebi(AC}aPtednU6s|<|FT_Q`5+2FCLWU+r6D@nqU+oV6P{aLlFCzp>@+xgWhze`@h^KvHZS+a zrM!v`-8K`U2H{kY1&%?$IJuGDuhI?RonUq-yVg%TDO+X%79s1&tLCLL-7AG>U`|2W z2=#Oe6Hxj0h}ZIv3(|u0Eer*}wLr>Mno8`~0Ktp^dMc)NCuq)U!Zhj|45r``OM%H< z${k-|_boKP2~X|DAX#B5VUnE{*gPntkz0Ex?7VS)WD)s95J(_=EVCSJktkj8aOKIU zkXT^HhY5=$m{?=wCOd(w%pD)N-uty#N zd6q9}{OH=h6(S1ES!vP3arAGvh2N4Nq{^X6ULHQW1!V}n!>P9#NAG;71OE8m*?y-= z@lh5voxj^^U8P5}T?^`lV*@VGNrtF;!;jAZ)S^2h5B1?+N@t!b>~V>8xJ)1#9?+LC z3&`93ud?$U<$-&3XK;xTMq68kX{eJFYdcIfE{6H-aQo%aw4HpD7wh{K(SE)dC&cvw zf^eM6KyNlk+;FL6P^yJUrNIFPP&zPA$Vk@pbwz%JJRmUD!=QtKXogTpom%vrfyGDw z<=#5!&qmW0ez$&-5DlMpx1l*o@TrOKH5A7ByBO;nj$7o*=L(dqK@Kt0;lR#QIHk9+ zHA*~x;VHIRXr!-us7MjbUA<%`qePA-@{6Jc9Jpqg#q2LW9=`97^N@SuT52({T2bjN z4vj$1%oGyXHR192dXLCKBSs}eMp!g}Y4_vz6!0_<{a+jCR`x?Z2*boE*q5YaVI14F z8(6~#Qk``Tg6*vr?{5Nv@M7rHH1*k9`u+ov#s0f<+lrBtO%dWuX+!!22cJc_ThW?G zT7q%TXwJ|~1tA%)7QN)%S0ZU_UwVVAl%=7MVjbRKxE6%iTozezn28)v>?<$;vZ7TR zR zFQzmi`5OYrlK9-Vq;>JiHAK#t>fKqkf)^69n2v>9uw2f+S?Clzk+qWwjQ01%Y z&lZ8v>N|Vm@m&NhqBap*%1D%p6!reR2R~YI!XP& zEb~#>*;h4$py-3{s=1E%r)0Cz5)_C_ZtD8g`E=8JB(ckm+(k8b>U+Uq`k@8Fkn6o& zU#>->Z_0xVGj{+TU7zRaVgF`;QnERX#-?xxt8&sO-0Xd1^ePhYv;!DMqmzR0qH6&t7`J55z9vbYp#Wv-zTEPPg_)bFo zx~mcqMQq$Umo&wMGIRS504lNJ-Q;au{insh-lckrBYI>lE^HMvHWvHd)?DdJcUP8w zaNvIDNvZpA`6o>3zc+aS-ZCGx54eNQEUPtKC`*6gYQT3;+n3pIE<>&sBtGXd%85&4 zEqX=O^v$No&o2b`FvNq^mhO&+tGB-iFs~*0KqJA{)$t+mtk-2x{h~_Nl}&~g9R@9l7+X;V{HQE(qvH4s+yM}jw7KC8V3U~LxhvzTa8({(#A{X~Ed_0= z6zN!)gs4?brT`=oAO- ztmxrxAgCiAo1BG3%WL5+X7tvR)dsG31?RrqfcP|U)*8Ul?=cMk^NXGA;K`4W>B+54P&sZmcE-`fF@EDHGb-i$01ZMWjjLQOL|D z_H59s6y`as+8*khE^Fh$hY*jMAw5JfC&tv2RKN8bfaP#*nA1$~yB*x=%Zd?6p~$7y zJlH&l6W5e8u}%dyN*0xA*&txSfv4InQ35gE1G0f+|B2Tq*NlZ}nQ1rsEY-kGeJ-eB z>v;wuUFiBW;S66IPWJnSdotfpK72Vydld@y(v6;FfOVR8*(rArxwI!c_s*uv^uDJbg?n6z z!4hE!RBeV*j;7e%OAMN+6_%Zsv)?Qjv#z_no)k}D3+?ybbv}OGqw6hIM|K-NR>nUD zsDi1h)j~U|JTn(2p4v?%mad@;*9=l#9m~+)Xqr8#|C85rG+Rk))frFMN&S52A)cpX zOKWx?m9dAjQ)wLzH8rX3x&PSckXMtxoKmBp7m}h)IZ~^*#l819$iwWB&VxFZ)vaUf zr%f=|_eJ*8Y3&ODkxozT9@qxxuNqMUzzN1=N-*7W&Cc_87TFD1WUEaXnxev0$O2{d zy-^PfV0T!)?)x=0!`fp8uKiPZ6n``$?(V7sqlMa@#{QzbycSh!t<()={Gq?C-ebHj z-zm{y^CBo!4~O6<)#Tqdv+Pst#;;b)$@ek_cuQ}K(bCv!8%nQz4 z&sL=a5cRgS*Q}tQN@Lwoj)OY`UO;uF6`z4%t&NoCqSWT=n6MsTk)6ytsF`>I)={if z{M8a~j%3D^4jZ3DMeXsqUl%{8%1iArKn-}enMR|gIr;8eKMHHf5*Vr*n8_@1CJ z2n>-M`S;sH1m1LLM|qVN$mS2o=>1&Wc!457ph>9>!KMD6bcjQD*;*M3jRWl zXOQ9gChxR`2Qa(R!V4gzo`+l7yc735$=YQ3v({kpTjkv!`qRHB>N0vDtc_c00P~WW zVcn&Q4>tHKull21KFs>Ae%gmCk@qgJqJvaQWY#(L2h8dh6(%~*p!+vMR=DJ^!ien} zY}3<6lP(+6Q$Kbxisj9LUw%NJEANODg9sxW-OHC;IUt4eA2)mf+`Yf7hj~oZWw3h9 zReDIrU6{HKNrhzJor%_tCGeiOEHxh?D?2g~14K@rl-&57dv~XwSWa*4zq>YnLED-8 z9Z;G#W(`7Hx3Z`XWxcz7BDoq~=f#ugLYk%$Cs+FgYKg2t@K%?AR`Hi%v~~AxFIw6B3TmEe zgRz@~sa2Rm6%j4(P! z8s`$i4!yqmw^?)Td#9S4zti{FI#f`aswk!1x~V0aSp~771NqcYHJo?Flbvt#LJrSa z3g9V4HU5jDM^!(!>c~)tVjL2yd1#y36{HPaz$@5xoX2@UtG={3zzGFEeZufb zV?Z^g6pNn5+;#w1P1sGM)S6^cBM@9J2^licx7ejAfY$jr~r%^Ls z9pXg?RDq-y20^iEA?fhq7eM3Y;15fjwQp%JUM<1&S@n0k#fFaq&y%ImCjUfR%ruls zNG1wD$QI6`X*@(vYY`Z?-mX9Ci8J}$?VPG|kjyXXZY}{}RI_QF+a*Etzo|+40TywY zniF(+0SX&;BBb86$s1Ao&q^q9Sgze6!ely}7an$)Y-E z^iz8#-gmVLd+X@s^6_XX$8UA}YiyaNItCer(7Y|OfVJ$1eU&K5{Phf)veD@E_1Tt%-Q1#h4K6mQqD1 zWEY7L0h^XD z3CSQCtbl-IP`kjts0@nHlY77<1O;t!XnI_O7oBzhrt zv6EsN!Y~YY5Oe+~s50mjtC)@_7O;TcpR#sZG*;NPN3|C(g%nwVkH}SGU^w6e6|!p| z^vCR*K4!zHoMR5{_!SPPG~L3+WLh&>WB1Y* z(%8b&-lroOu;WdBa74r5B;{z5DVpDq19?H7PxrElAX|XT$YDZ~8_v@gq4wmo%FoKY zn*+AT;?{N^j(Mzizd^6u(2HTOolM_RK?l>sK7Wn1sRarlQgD>RJ^dP2cE682Jk!)R zT3~GQZ_aZD&G%c;)gKQWsWfEn)=xMT_m=)97khI9;I2Y{Oe4Erq4Ra7C6kx)dfU4~ ztj3wD`cDQE+C1|XuZHl$)m|KE=ZXhU2dv4N3JykJiM-A-}ep)jPQMc0YsJrK)>pK&Y;^#?>Ad%9a&e|k2c zm&+Q4gT}ey+RmK^7h&RgZ&>esd**iKb*svmTRL7o;p!D0m-8%!SU0H27GJZ2%it4d zUjZtj5tfo{*0wD=uX%V71cRm{k0}(rR!H!O=90%`rRA&%%IhH3P*UC9s&6ZJ6)NT} zOBEJZoGEn-vOTbP;T}thd$hPAFl_N7$o4F_V0*L>`B>@d7sWL-Jg2qUBXJ678cR$**{Lrw4tGRDYL- zwe&$dhj4&Wl5XA9rTvhtc0btE{&?ZIxcKD$JNdl>9k=Rq>Fax|eQZ@I7Z02KcK%|I z|LF?G<#eW6xLicPt3JZE3$_wPzhF+RZzg_cUtdR{;?7N>5lLM)?qXFDft%J?-w0r~ zVZ(>^pFs@*qP2JI#Yr0iFB0NH;t$?|RW50ALotZa@_iLCC4=^@QQIZIHS6Gc#=vGT zQZdVY8S6Sks$qkCPOQDNEI{kLZE9w%f%tC2e<#LsJc8;;d7DGDC@Th1mb)(FcJb4% z>3kFnHXr@NX(j|xG>-MC&wka`(GC!6n%KQUxSmKdCs{sST=E^O1M1n*V)fOXU1oKY z-Dz1`8j#%gLBwc!PB;(r!hFD5YKsJ!Ew`+(sT2?^u7NHC`R7E_w&#hV5fBcP?AVWT zq}x?fRfT7?;S>s##c=?ZwFm~+nLK1=K4-k$9GxHm^eBghjItws0Kp2}8UR>1Xf@LG zKKlHf{3;h=j(}trR*B4opyk~#%<-pvVw`tENTN$T`YTdO_ILV*7+ab{^mtmD@@$!{ zT$5Tccx-o;C;okpsp+ZrFR9Z3Qh?sLO08av0Ea{L=g!Q+clEP6A<2;-^w(fE?&azE|rNkWDiabvN8+&m_irOM+0>UE7&ke+1FDTb)mH!6}%&ifWu zb>tsce#gl<6$HK7!&H}IrRFW(Rd2@n# z7<(D^GG&HHpXO#`XRo(tobtALVaHe~43QOLTZ03!plEe_>xvFJ)TSs>AGX*6(!8fh zR;!mXNktK6_i+q2KzUkm7ACPGGjnWq!4+INE^sq(TQCj#z+^|wNOsXjwr#@bGEE$d zh%>}svAI#O)q(JZaszNP3CFRKQ!1u)<*Fwr>35?gZ#k34wdkq?Diux_l4(Sno3F^= zH<++mMj37n<1UDi(di3Os0zUM&T(=c-Nus{mgylx64ycujs!SK;Y|^6omx?~xzDkJ zP@}^Rvg<2eaKfy9k7eP~Abhyc5O6Pn6Vo)7z(p}!v+HLY<(Jvt0NWB5ixjr8OpJ_s z>(%Ujv5I>RKtHB2LoJu|vW?EmF6D22#GO1MlEJrp2iZ_XOr|h?Ig!s3Y7g%r#8sG& zYdJ4c%N}!-9t5a6J)(R(236MF`FZZ|z%G=<#Z-`YG zqto-2|9^#@RZw3sxAu#BaVzfb4u9O8;_mM5W#g3M?q1v-id%7acXxOA`kpgqKDqfW zvSzYpO>UBzWGB!1J)z|x8h-g|a&U$U{rV?>3LODPI^|JKyM9zTj0lj3S)r-kxT3)- z7lI;NUO&`yr|Vyfk%JC)KMLkZ$L=8?#*mLYBU)D4BDdOkgBc+cBMRirmo@~kI86@c zwQV4sL_VVV&A$|g&9|OMCqKOJ=jG3!nk(H^z-A7rosOvuw&KxgCW|(Hg-=Zq`C)X? zFD!{N4BAogKzJ1-|sC8 z3V-3T3GXylg7-ytqWJwg@>X+ga`aY19&H9vWz0p{!gDS*9t6;hivK;UM8z27^2wAr z=Jx&g86Yb)^HoTB}#PoK%BaV}M$R6$~N(rf)A;u*7$ zT&F~!RIR;Z^AJvNFTZ8o3d8xY?o+95+ux(o_g{MGn)(fSh~IqcLl}61 z(Npc_&wCt9_!jj`3uNe$qLg8B{WH4wV^uUw2=JWapOfqGwz39Sm=u?x8}ppP!M{YW zF>q`Cs*GTL?{pRi*^@oGN}ALTVHlUeIC*VM^*$#px#e@Lxce|p3;Rq<+n zbO%Ga_466~VN+|6opY?`Eh{4bvA;e1)&V~AaB>9gGVIjzo(Fc1Pj%N_V!?fzLR4*c ziob0`5ANbwu@SBtESM*T-Ei-OsOKn&Mz}y`Qu8fxsH{ ze)FGFnUn`+Fj_>m|NTb7nSiSG&wWg5)gF%v<;SAN6^qyQ4X0=wGdQ<&7PMtRw?Y7C zC@S>`DJQ)Zl-2qQu*qgqSlh0ioQ%$O6r|p_zwOA)^|iIf$4vD~T}$?fYQGMQAn|g8 zV!xoz=dEH8P)?lz`L!Cy;V9O*m)nb@atka%A1cx9!laWvjf{MTHWeAipgleYzC_5-?;^qB*q8*`#6h6$BN>OF z&;8?0P?e-iZ6V&H0b}F1zOWyYJcH948zui>8zpM@=5M8hYE0=~I+LrI`DDac$ddC$ z9oCG=3EuNi`j02gFT2Z6UM{84NczkesP%lf0Rw0zMnXW;YR)8H4ey1>H;1|5qvd)$ z75Ddo6R-L=O(s75gWoTO-g+HE5Z6wm^@`eVXBD%kB(Q~+089iArQH^-fOr;8O+f@Q zgQj{{_V{G^dq;?{wrmn^*SnbemP@n0Q2g;BM{5l9g=>BO?WAXEbb-Yr+uc zfnH)5XH^dZv?Hk+wRbloSDW-FlG^M)e3v_Utq1KYtCB!vUk$|?n~TeWh;jRMcfZDp zw;Ley3K@!)pF$hYlUP;aN#Xs>yvMmCe{C%urPqU)QFBNh-zy3ABAywAQ0u5kJT2#ZC9W zt?Qc4$~N&qyBDMiaxqi-xl@wcrR+4{=1e{2)YN|lD}TZ@GfY&x>s3udkUFt&777zT z@MirC4G@s{$5KUt$XYCgJaV4t2?jU3<2?*iD`1_jJ4J#Zu);$yZHWEebSrs%E5Epr z{*CJmcnh_0;C`?2o2WWGv~|CCX$tY{gjnobbVHyHpP2%JexL z@Ssll(=r%;8^<3mBhS~c!N0M8FC_JE7 z)h}W*RDzK~0kH(OV9eSCc5sqJZZc2+{tr_-cpUHe_i`Z~##%<^oS#Mt{mLZ_L@ii9yPP^!*oLW-(4;(I#lFR}GM5)!je zhX!=+tJbCe6pT!FI(V_FE?%l&=j}j|X7*t;~~vR^M5< zY5|U0kNdh(tXXqEzx^_It;Km;U(V{2F7tV7o>Iz)2><4=dDn+Bg?gd%02uGt<68~Dacf2U=48XK0&8JIL~9)Ti~oMG-;THg)!`i zCB2&f$~0~BCR z8J1#cvr>D0-cx!Kd|aJe*Wb@)+)pBtsv_`eoPIyIkod&*>uVow$&_ zLfIm2>063RUy0SNqDN1*u=Ib?1DOuRvtksy**ed6`w{miTr1BvQMMKNb6*ci%Wcpt zwwW~khK$$t6@$($K@qP9Lj(q|9fDiGvy0g^{y|R0E?)BcX)d2?xotgr;gVV$Rqi^` zJKD*SM^#qy^-=OFdm3kH7oZsjX>C-RYHd9&K2f5GX$-}aHC^-iPVkIA=Ltgek!bLQu zk}2c8w(PyH{B-HJDRomu*{Ser>B)uC*3%b9$j1S@nl8JH_nDfUT_ltYx&%^Zqf2T$ zr?hth-3<`%{1gUd%3Iz5NC&A}(0=Nh@)on6OQqn&;$~2FRckbF29GWMfnznv2uTh> z=N{?Cs)~%6gl~7;fQ+O5B$LU%P)+VUEklOojfqXI^e^u;C;x|B<;^l+7D1_6&_s9l z<4Pq)``b2cbSwFLcjED_f6HN|)B0*17qs#82{HwHBhCIRnpjUQ=TXM+JD0ue5 zCm>#9@B9lp^=)%Ux*T7M5(cR_AFdkiGOUF!`wt&E9b79e&EFUiIBxfz7DG-RB3@^f zrfu&pY&7eUEAin?bzp*1+uzM{y&$yP;)GrCY_H!~r5rFqy3)x9vcJ-~Q}{_Hf(xHU zASt3b3o9-hU)eRb0S{sFYWSYHx0i|^O25?G$usZw6QX<4=c4`fK*w?v)7&QZ5FHw} z#)WpAKz+v-(r8Jct3I9Hcq++|$s>oadYjoJ8xB`3c7&al-jfCIdeaf{5Y2wT*>$j3 zbNjb@x@GXUjj={JyZwi9BvPn9VbF>=O$BaKSZk5Ad0j!Xz~wRJPa{*vpC)|_b+xJ0 z$r-_fo!H#+xK*Y!9mox@MW>Qx2+>arJD2Fec>SC`%y160hWrHhL5;U(HnM+pDBi@T zf2b{jkfT3TAJ?eJ3==mXvK9ONk)H;eF&#Is9Bih@y<_b9#x_3+Une9VvpxwPJ6_I< z2iki}*OrOHf$rO)7ru^Wb(+-hKgv_$s~;BkCj%$XXj8kcCn{53RM&G}eF+EV$+EdZ zmSFrvl&*#$l!wml6yXXzNviqfXiI&C;X=^V{{)apLSM8P@Qelv2K&Cw5Ud7fT&RTA zK9ZP7dN$i>YjNt@AHRnRiOXal?E7c?RlT=gN*ZJj0WVN>^W#;tGzm^S{A6tXab(SV zlES~j!c*}`QP<^oT~xm9(1yCUrh9yt%mZJuxC0^AAQyz!6mPpE6yjSE_Qu|g6HB?B ze~n4Tmp4BEgKm6OB54nzJmcY(&gEAb(k#l?pdbp*!;;RqBN@Zra zYbe30V}YEoVN&XD`s2D%?p`7dCiQAU!B1m907p?bGOGBQL||ZT>{c|9zSxhuH!*U( zVN7VHco;Oe`Ah&tBX3{fw2ODZywut z&Q|Sd4~Jc{VK5=`SFe&J!}(afT-Py1MbCDkb- zApGK3z)iGNFPxA7d2c(uln+1&glIYLE&HK{%|LN%!m*E%-h$Nal8O66?k)`{VapLq zxmU!=%W#Hk2Kv$a1@lDcim%vY+At?^cxob^l#rm{ zpN3g!R|q-3HM@z4G3ph-E6GM{aH%EoaR3OkK;MKGU1aDo4bj%?xih<+?wR}&6 zds!hl6(+&%5F0Bp_q!1iJY&A!a?0*o&@CGKE0#vhV0woSHaL#OsA(Hq5<1(d7{-NY&+;ctmOFZk+ z^x^%>H=Hh;lkqd#uO zN6a@bkVTzNN-+;MTJ0@spt=?d{wUJ*xF@AyyaT)*+kJvnCiF8di$vh>vG1;O_ZzQw z{hns-kz!WboD!C?j!VhDD+}jUSPcHiUUrCTkPzLXP~0*(O8Rbu-dQp&u45-m%ypMg z@~P5~9-F_O5Or5Y$0RyZNL0R`|1cc;w9O_Wz-b4$mS@qq!q-R4-{#f(1@aU`mSt6Z z&t4A?yzNql-FkEo(zxwKbrNL}+^ug!GdWW8O`!ieaD_Rjf&FXX{~AQIgBoN2BpAnk zj4T8QQHT-A0|JNzxD^uaB1~Xbje0*`_$N!7)}x(_!^WEBwIM&ifGmZ*sMiqB&+VVw zj46EBvF}zz)gZ;HP`x$|1!VVdOyH7Dn*!=wO)N8+| z_k3{dVzsaqJh`$SQ@I{EBd0F1hw|$y=ehE~_#-V=)x4!`I(_9kWJUf^%408Lv#yeJ zDZW&uefXV(U&o@+oaseX_e&Zmy z@+C_jCX=JHww(o`3IvwSEf8A z_AUVLq^&r?7&E$Yr8`u<*!S@`yS-KN4QW6e;C{N%i`f0R(#-TY4Jhf%inuPJ`Y53a zvIb%GMOe=P12y}WZ*;XTlF=133_LaAO>;vS!-;YEU_7?CKi+P6{*-td&+uPpm`t~| z&{NIN)0(G;V6?{*-4;C=jby-e$|a&x6Q^K>2Ybq5I8wi&OfqLmo)N%=c=CeqvGyXn zBAH?aF>lrX%zSE(ErKf+x~|>KDM40B$#^dpV#A>V@jpFZ+W86gAj zeY-H3E>12K7~6c-hSpbS#br8PJEs2k5sMgB$0C>nI-hMqO|>pE$1Nm1MK=fsuqtjlv?!EgF7YThZU7^s=!$ zXxX&~Og(Ig_XD0Y=RdUiS!^Lsi*x{nh?%DR-#_}!gN2@7Bvapb^GM)?SDMkRC0D_4 zKclH@V$)XP-UyYvYGl>%?j+P#8n6561KLb3At{$1`x za!1@0pe#;O*_8}3(VgALf+LH@mVGaPsH6}flEGWZRVh>b{=VM13?JL9HQP=N@8tCI zZ0zc->zm1-WUH}xqTl%GdWs(Ua>Nc*O1h%T3tbi(TqzrcKFKO~8v1~j!%Kt|UC8y6 zxI)_*p~2rP`muB19FD>AOwRZ5W%bZk#GeaO`}iNlN_raA%?anONGyZH{Sb4Fx3y zyu6kYY-|xO%KJQK=I^NY{H1k2nfAPSs^H5l8c|W{oJ0{5Gc;>@;grs=V zL-Tpj&5%3rWby%(FHK8iM;}DDf~e@DEL9>1R8jvnb|bRFnl3cPV!rav;XDwfNwrmh zZkcdCb`;r2Ja;!~Sw~eZcp@0#W>66Vm>7eu3$0~V@^fksnsjN$5$rliNe>#xdvzr$tG(hmZ!K(CJ(l7U2wIMWv)fZ z+_7wDf9rnyIhJ+~>yIB1-TSVQG!mEyYHdXE{6*ivC-rEnG-0J#Akg%p z!l{zjc$S6bq6BgWL1MDV)FDCx3~!mTIfK`Tt;VF^3JW3Ql5t2$FrWK}{b%(X^7e9$ zYK0J>W*3sN^GfAd6#rU5!+134`q)@oB8(sSx2sMtQz6i#*vc&$*El8jC!7k#B;9bI z*M*++V&Ibc&Pfz8E{B30fV6l|)lx45Y z3%g;7>FdDGm5r~wH{I){wT>Z)b3w(UrtU?i+`ly+)O2| ztci%LaC!`F#AYG&Q<0AfMRKyDSS^sVRjJ7u(sn~tu(UE?>+R88^6W!MwZIQZ_&|gS z>h_O!`)5nyk)060a1BtZB*q^-OwLV(=Wp2PBz0Tuz7ix4XU8nD^ zBObn%fGa)y;dsfm<2whN`H60hik4p!!>qX+~PJT#9C> zKG*zl+K{e>FPP$cDw(F%j&L5;#{vD~0+Tj&Lo@=NyzBz}W5)Es<3eqvR@G$blU}k? z6&FPXBw6<=k5@BWzRfnG-=GdQKZMq)pQ}wIr>TvDB_3=q6|<9j6$a>)-6{eU%SM{- zV#xbMYQPo@^s>4JOfpw(IXUA!+DT067(t7SKh;-OWy@a{wb)D?-D}H2f;1s$wcQd( z#v}7l4ejS|S&V&%1PdlGLPgu8LHg=ar*`JK9o0GZGzE&8k))S)J${kDV%oW*torB4 z8F zojW6q2g~w`$Tv-YN>96$b{mO-JMd)pqk`5OrR=~YEg zHJ}|pm(c~Gm?6#3rrh)74gK z8?7SI@4^3U$=tJ+6X<2uBDmGoiJDkRwhq7}6O^34%lf|Wp+fP5o1D=#Iy!!@+k})S z?727)HJ0ruxJ!R&4UUIYV2{*}^r+-JWBUt`vtqZTPov>+*yS{(hgEqaw5vT{pCh}f zHgQd}usqmfWm&Om{-UMaQr|8E^o#Bsh# zm|#^i=-+1n)9f;m>H*Lr|63X6U4GCVdQm{&*Qu(BBoZsHY7Ep2>r1 z-EZB8o%>t^3JG8?o!A7ka_fIBLWer`ZHP2EHRvas#vIJ2k}#&H>^g8eo14pcFgYh; zb=bA}JRa46;Y(xc2xNljbab|wc?Yg2W|4gD!Uv6YK&X5>G@lYry6q@T1KFMwe(2Q$ zo!ls4JC&YPeh%bQcn@I^w3Cx087X@(BFJ&V!)04<-0>zSDe&?yGB~Y_b57ou9V#t6khdA%q$UjAM5d_PECutV6Tk=P7hZ`zplw$@4!=`p$Mwyam}d zG>^wbn5?gp&g@+|gU|1c4oyAQW$XCIz}rm9brS7Y#Qwl^b@=^xj06ht9BR0tU1T;A z97T0$))<`M{=vD1s_cW=lSVBm!^)wu8&`r4oB38Ro0@(ezN-nC@6Y9$zTjqC=pW3= zMCT~g_>6;Hl)yRKmGju_VBZQx=^(IJ)tQ2M7bIT2E#E%I7}T(nX_O^^S zs~-k&A>eLzJau<^tYLjsK!~%VG0L2qFZs4;V)~WR@<#IhK4r@ak{8^&J_X+zk`;Ii zn~d4$fNJv-C(o3CN*e3n7Gb9&0HYK?(KWRw82T`JJl~jLT+%{a8pr4!A-)XB+R~_- zZNYBCc%$L_x)w@ca)K3EVP4d3+G$?E-1T^HHGK+!*4@)nHgw>Iz6?&PqsssWvZoCY z|NV;Q-ZnHYVe~Tpkx(gF-7_ARGz2hXBIDrDW}<9?PbCt}T&%NlpzOmZxl0yo6X6&C zF$pfc#t!tc*lFQQhZxiyC5iktVI`0%@?N<*c#r*EZ{B20wxSIe){Kp(8pagkFy&qP z$Mh#7^gPY(5dNvN(7*^yY|hV7u;n?+wR5Il?giYBuNmhWl8Ral<+>ptK^_2qPvWgN z?LxG!Jd5HMU@4m{7q8-kw_p+gZM?oiIJvca<9a<-S4@BH9Y4Uzby+&w_eSdH(E<08 z9~oOZV5O=T=ds0rF#rMAXK+{J+uO7}hOhp!WKp~2$*Z1)Sik)T(=Zwq0L;!b-X%&Q zi%^H6ZgVW3Rh>8@!*sW;F<=owv#Dv#U;TAyHR(Y`+Uytqi z!{C$UAVImvN&4?$@Wb@=c=}-#I3utmt-v3A*&k{&cO{OBBRU%p%X zCr(hjtX8a+^YOMjm>pf_pZNSjTlSB8Jf#GQk>YO)$pOy&U)DV-o3{VhMqJOYbI0af zTKs1$zoF0`<>ccI=Sqz+S2J2@4@QAN`IsPiZ)&c`? z3>CEWy^`w_>mu!2qt~GZjwZN2wg^7O{#8lO;ZFKzqeKkAAjRW{@BAQ*MSmr?dxjWH z11F z+>?{Hc_umw-ERkqQ#{nrS%D|CW{&6FvvnSy;WCd$$N~vrxRwtAuo$0+_7ue$LG_zC zJFC>Q!kT;Te|7X{e42CeWps|_2Bl;kr?S&Trv#B<>J8S$ahC2*4J~zh?NRZ$81~my zb2BTlN zzT5&K;~=+bYHpwC)D-XNh+E{tCGocl{$D9%x592L*F@PITQiOoV5gyI4?XU~d9i^e z;;pfu)1*`w;7V`D?2Y7ZV6uc;piGdw#M?|5k4gmf)e|51f;m*__hQuud^|3?HGl4% zjG8wB59iJDpO_LID}bj{{a+?q&)bvz3=KgLuiu+Ox!R>)P3N=ck7qX*xA7MVfrjcO zKd&G4UU!EBZWijVUAlXdEL9Ji`X8Oox1nqa-jg{%C)88Mt_RbE0rS&!9_Q+oZYY~$ zC7r9?7$gw0>typ(k6o-7lXHJn8(@EMlnD8!Z0h}d+CY%U^Y)6QpvxN0VGV!1Xkh7* z`2E&ma+9C;WLjtB^^%$ho|o&FPC-sP$oFQuT@o4Tt}_3&0i%txSrYL0cTX{Ywkg9E~*VzP<&eIg6|{5FC&8F?F*tS(|7`x21DV5s`4FiX2^xVrOx-~8aOvIp~O{HAe=v3K%&z? zj^qq6CD>IvI3s-Dk!?!3C2x;vDGP`t)yYe$RI1zeFdv1f)hM_3l@2O?(D!PzuVl2k zVnup-jr2iVLPl1=5@~xQpEV$gRhq(J zhDb?PwEmNVU!usOALnkql9z>o&perc;iSLeC64Bq*?ZBNq0U5m`QK;WhR2a|9XZ!H zy;U>R_$?zdi&+sjboa8Vh!dHzY%#TR^@_5RD$7M4oD+erA0vR33 z>?B>uVWQ!Ec^-t(td+A>_`rNYE=Tj|QZ(NQ|H4WmBeEIBB&CzhUGgv=UOTvo919PZ z$@;5>ippe7QN`F_*AhG_o2n!yzKo_oO`AW-+b65(4|D~VDtVnx!C8;ltPU2t>YU=s zq5F4;J2KX0c_FxKiIvN~SeBJW8buejMInL9I{i2ai=gFd9SvXjNC&;enT}Ven#O+x-2x)4cD>gS9~T z+|3;aDIr8Y@$;)OJ8;eW4M(05B7tDz^Z{sn^KDHd!5%a?e7j-7(vjl)N{L1pC_Qt{ z3}pn*Jj_j+5qhD@M!z)R5N_D}6xV_rd-Xgwgz^dfL=r(aDdTK)@Q&jAbrrA zM9*KGdhl9@tmWqK!h$Z+NuIu`60&-8_;bh3{DE0+Otly85wHsdd3Wt5f^>J?I060g z#G3_;8n#3ZJvVF?6`6 zwD0I&TLZVwSfi(z+%OTj!ZmYR&Vi~X=SbM&w` zN9i=egbcpKF;!`3q?~2rHrAMD^9SOwgLC2JLnbF;XnQPy(0i!kLBuhZguy@c+BCkn z-B_dF#uiIl#SYH5gZ7-<_`QbyoxpGYb&>v(X&5iszo{UY6hmn%g;{ri=16)FPGYy9 z+@!7oc~x(F160pJ;I6LLv+yWkCpZ5I*8d6Y&_6{Rhro8kgb~07n{v%F*HwYf&hK8VQv)W`5GQ3&Y;ePYC>Gq@` zpiezBx%~Dol4}~)C1}nY`1Y55?4Td?Q~uY?{ZYU`c{c^H*k%L2P26QbMwmcGi{Us&u z8#0ocEiKn+OvY$A)91-*Z;1g*K1bv+qk87klmgXAm;2g|rk_{}PKJ7IzRDheR#oxt z(~Z^k@yS{w6Uz`o-%)_L!?wK4Wmf-Ucm&{{OlQ+QjHV?t) zWSEWi>GewEz;M0&U9caGJv98YO7^xrx6G0dmNw#mq~OmRt4TAJZwl<@<>wz^+w}+< zGJlYMGSrv5f7OX|CUh;&533DEF=*H=JAcxrj%uX3kc;4Xoyc>Jocs?k1sk)Kw!F)q?#K31eEYs_0b+)M5 zxx~NF2{8vIE^JhpHB#qGjxx)=;|spc`TwCh_%)u+ux|vhtq4DQQ$vF+OGHCIDVh^z zHc3}8B>EUU{R_XA3P`zQ-76%3`o@%AWs4a3qatU^!Ku?rUVF?o8#K@!&Z_w|J>Mlr{ibk~uAGb8K zl!VjiHg8C>l!Vpk!D?``41;S9w6i3v40GgL3TT2kM=`=%3U^04CpO|c5}Muk1LqO2 zZmBW#73BiVXBunz0o^^2v4NuDN3e*1jqvL^=^XgFCzd_H^Tkpr2K!ZZVR}qRbqI2T(rZRHm{9zbAII~z4%bNg!gfWg_ z&d8=9N1RMyJkePyLNJTzgXt#JY>30@J)}Y^umb!%HA@{>rZ34i<7vC=A@GY;M9R=TiCVus$a59CmiOU_va+Cv?r|RwDyA~ zvXmyw=34jIC9)(Y4Ciq7_QbP56FPh6WUIfzh6y3t_3{ILH!#6fBu%L{e@^hbTBWnF% zfx&VIg{T4^!#@B%7riS~aAG=2ljkAbMxZK(z(QSu7G`E5$j^mfle+&XjupGs<2SQ} z96{I@^=OMP=20$&(BubyQX0FdyYrn80mCtFuo{z0@Kk1f@Y(iIw2-)+iq;LU%D~0^ zvwA~&OU)HEnNZ&vQ~L_Z71neJ=%;TV!L5@YiA~U|sXE`3TEHv7%X_qytp3T~%*%y1 zO2FK5QRmXzSn?!YqiJzDm}kH}avaEzYc>uc+L+TtAQ;*IDfi5CzUQ+r=>eJNnZJ7)6@lvol7g|g(Lzm%Cc zBPyXM?E1^3W`4o$_HTWBv<8zg5UG?^fS}c7MJ%xyhUJE#HjoCF=)uQfd%TX&Hp7?m zmu7}y43steHs-I@Y0p5m@3FB~40Krrj2eOsAuduUlM5sUw9oiH|2!1wsW`=r8nUM5 zdOWhy1iKQ}gku)%Mg%K|RK^?98H$+p(78hxL-9=UCllekzrO?hONIS+g%=hQcg5W%mm=@Ipy1{UxnRh8{&q^@yXKa1LCPIQBhv)#1z01RtF1Y}|Y9 zt339%aO6LKhy14Y#r^t9wE7OU@4zoBm=qo$c#!u%x?l8JkYe~9oxhA}n${%JxH9JtjuQ4$ zK;zr~{H&Sh*S2aFK8?I^Z6Un<37iTZ&@_FA~9yr=Qpqz|bFNbk+0&iJ0EPNhL7CHkh`I5KO$YbGWPtJyn0(|VR3flx z@o<2behvd+h%&YItg^+91J2I!ftXu)f9L&y&y9?hdk{?i%2MCf+<4Qn98_G*XY{aX z@J(L3M{v$^1U}-C{f}kHqQU%8JZ5L=l;z5=C<7~YlYtLG_VmV`{Tz<;`kl%}_Vnf* zzDJHfwgX`b1$63f9{@xYR)y+wlY~a4>yAxSm6lb7bb&aX9?(s#M&JOs$3Wpg!>2gk zS$nIB)bU5xJ-hBUF{}6tl+-c57zLqS, const D: usize> MemoryStark { trace_col_vecs[COUNTER] = (0..height).map(|i| F::from_canonical_usize(i)).collect(); for i in 0..height { - let x = trace_col_vecs[RANGE_CHECK][i].to_canonical_u64() as usize; - trace_col_vecs[FREQUENCIES][x] += F::ONE; + let x_rc = trace_col_vecs[RANGE_CHECK][i].to_canonical_u64() as usize; + trace_col_vecs[FREQUENCIES][x_rc] += F::ONE; + if (trace_col_vecs[CONTEXT_FIRST_CHANGE][i] == F::ONE) + || (trace_col_vecs[SEGMENT_FIRST_CHANGE][i] == F::ONE) + { + // CONTEXT_FIRST_CHANGE and SEGMENT_FIRST_CHANGE should be 0 at the last row, so the index + // should never be out of bounds. + let x_fo = trace_col_vecs[ADDR_VIRTUAL][i + 1].to_canonical_u64() as usize; + trace_col_vecs[FREQUENCIES][x_fo] += F::ONE; + } } } @@ -176,7 +184,7 @@ impl, const D: usize> MemoryStark { /// reads to the same address, say at timestamps 50 and 80. fn fill_gaps(memory_ops: &mut Vec) { let max_rc = memory_ops.len().next_power_of_two() - 1; - for (mut curr, next) in memory_ops.clone().into_iter().tuple_windows() { + for (mut curr, mut next) in memory_ops.clone().into_iter().tuple_windows() { if curr.address.context != next.address.context || curr.address.segment != next.address.segment { @@ -186,6 +194,15 @@ impl, const D: usize> MemoryStark { // Similarly, the number of possible segments is a small constant, so any gap must // be small. max_rc will always be much larger, as just bootloading the kernel will // trigger thousands of memory operations. + // However, we do check that the first address accessed is range-checkable. If not, + // we could start at a negative address and cheat. + while next.address.virt > max_rc { + let mut dummy_address = next.address; + dummy_address.virt -= max_rc; + let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0, next.value); + memory_ops.push(dummy_read); + next = dummy_read; + } } else if curr.address.virt != next.address.virt { while next.address.virt - curr.address.virt - 1 > max_rc { let mut dummy_address = curr.address; @@ -530,10 +547,19 @@ impl, const D: usize> Stark for MemoryStark Vec> { vec![Lookup { - columns: vec![Column::single(RANGE_CHECK)], + columns: vec![ + Column::single(RANGE_CHECK), + Column::single_next_row(ADDR_VIRTUAL), + ], table_column: Column::single(COUNTER), frequencies_column: Column::single(FREQUENCIES), - filter_columns: vec![None], + filter_columns: vec![ + None, + Some(Filter::new_simple(Column::sum([ + CONTEXT_FIRST_CHANGE, + SEGMENT_FIRST_CHANGE, + ]))), + ], }] } } From 6ababc96ec6f2d12bbab2c2b45e0a0f0116d90de Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 20 Dec 2023 14:13:36 +0100 Subject: [PATCH 039/175] Remove aborts for invalid jumps --- evm/src/cpu/kernel/asm/core/call.asm | 2 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 74 +++++++++---------- evm/src/cpu/kernel/asm/util/basic_macros.asm | 13 ++++ .../kernel/tests/core/jumpdest_analysis.rs | 6 +- evm/src/generation/mod.rs | 16 +++- evm/src/generation/prover_input.rs | 5 +- evm/src/witness/transition.rs | 7 +- 7 files changed, 72 insertions(+), 51 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 5173d35822..fcb4eb32f9 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -369,7 +369,7 @@ call_too_deep: // Perform jumpdest analyis GET_CONTEXT // stack: ctx, code_size, retdest - %validate_jumpdest_table + %jumpdest_analisys PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index cfc3575b14..97224b3ebc 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -1,17 +1,14 @@ // Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], -// for the given context's code. Panics if we never hit final_pos +// for the given context's code. // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) -global verify_path: +global verify_path_and_write_table: loop: // stack: i, ctx, final_pos, retdest - // Ideally we would break if i >= final_pos, but checking i > final_pos is - // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is - // a no-op. DUP3 DUP2 EQ // i == final_pos - %jumpi(return) + %jumpi(proof_ok) DUP3 DUP2 GT // i > final_pos - %jumpi(panic) + %jumpi(proof_not_ok) // stack: i, ctx, final_pos, retdest %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) @@ -22,24 +19,29 @@ loop: // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest + // stack: opcode != JUMPDEST, opcode, i, ctx, final_pos, retdest %jumpi(continue) - // stack: JUMPDEST, i, ctx, code_len, retdest + // stack: JUMPDEST, i, ctx, final_pos, retdest %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) MSTORE_GENERAL continue: - // stack: opcode, i, ctx, code_len, retdest + // stack: opcode, i, ctx, final_pos, retdest %add_const(code_bytes_to_skip) %mload_kernel_code - // stack: bytes_to_skip, i, ctx, code_len, retdest + // stack: bytes_to_skip, i, ctx, final_pos, retdest ADD - // stack: i, ctx, code_len, retdest + // stack: i, ctx, final_pos, retdest %jump(loop) -return: - // stack: i, ctx, code_len, retdest +proof_ok: + // stack: i, ctx, final_pos, retdest + // We already know final pos is a jumpdest + %stack (i, ctx, final_pos) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i) + MSTORE_GENERAL + JUMP +proof_not_ok: %pop3 JUMP @@ -101,26 +103,21 @@ code_bytes_to_skip: // - code[jumpdest] = 0x5b. // stack: proof_prefix_addr, jumpdest, ctx, retdest // stack: (empty) abort if jumpdest is not a valid destination -global is_jumpdest: +global write_table_if_jumpdest: // stack: proof_prefix_addr, jumpdest, ctx, retdest - //%stack - // (proof_prefix_addr, jumpdest, ctx) -> - // (ctx, @SEGMENT_JUMPDEST_BITS, jumpdest, proof_prefix_addr, jumpdest, ctx) - //MLOAD_GENERAL - //%jumpi(return_is_jumpdest) %stack (proof_prefix_addr, jumpdest, ctx) -> (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - %assert_eq_const(0x5b) + %jump_eq_const(0x5b, return) //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest ISZERO - %jumpi(verify_path) + %jumpi(verify_path_and_write_table) // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, @@ -135,9 +132,8 @@ global is_jumpdest: %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) // check the remaining path - %jump(verify_path) - -return_is_jumpdest: + %jump(verify_path_and_write_table) +return: // stack: proof_prefix_addr, jumpdest, ctx, retdest %pop3 JUMP @@ -154,7 +150,7 @@ return_is_jumpdest: DUP1 %gt_const(127) %jumpi(%%ok) - %assert_lt_const($max) + %jumpi_lt_const($max, return) // stack: proof_prefix_addr, ctx, jumpdest PUSH 0 // We need something to pop %%ok: @@ -162,13 +158,13 @@ return_is_jumpdest: %increment %endmacro -%macro is_jumpdest +%macro write_table_if_jumpdest %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) - %jump(is_jumpdest) + %jump(write_table_if_jumpdest) %%after: %endmacro -// Check if the jumpdest table is correct. This is done by +// Write the jumpdest table. This is done by // non-deterministically guessing the sequence of jumpdest // addresses used during program execution within the current context. // For each jumpdest address we also non-deterministically guess @@ -179,7 +175,7 @@ return_is_jumpdest: // // stack: ctx, retdest // stack: (empty) -global validate_jumpdest_table: +global jumpdest_analisys: // If address > 0 then address is interpreted as address' + 1 // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) @@ -189,24 +185,22 @@ global validate_jumpdest_table: // This is just a hook used for avoiding verification of the jumpdest // table in another contexts. It is useful during proof generation, // allowing the avoidance of table verification when simulating user code. -global validate_jumpdest_table_end: +global jumpdest_analisys_end: POP JUMP check_proof: %decrement - DUP2 DUP2 - // stack: address, ctx, address, ctx + DUP2 SWAP1 + // stack: address, ctx, ctx // We read the proof PROVER_INPUT(jumpdest_table::next_proof) - // stack: proof, address, ctx, address, ctx - %is_jumpdest - %stack (address, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address, ctx) - MSTORE_GENERAL + // stack: proof, address, ctx, ctx + %write_table_if_jumpdest - %jump(validate_jumpdest_table) + %jump(jumpdest_analisys) -%macro validate_jumpdest_table +%macro jumpdest_analisys %stack (ctx) -> (ctx, %%after) - %jump(validate_jumpdest_table) + %jump(jumpdest_analisys) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index fc2472b3b8..d62dc27ec7 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -8,6 +8,19 @@ jumpi %endmacro +%macro jump_eq_const(c, jumpdest) + PUSH $c + SUB + %jumpi($jumpdest) +%endmacro + +%macro jumpi_lt_const(c, jumpdest) + // %assert_zero is cheaper than %assert_nonzero, so we will leverage the + // fact that (x < c) == !(x >= c). + %ge_const($c) + %jumpi($jumpdest) +%endmacro + %macro pop2 %rep 2 POP diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 58e9f936b5..3d97251cd2 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -5,8 +5,8 @@ use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; #[test] -fn test_validate_jumpdest_table() -> Result<()> { - let validate_jumpdest_table = KERNEL.global_labels["validate_jumpdest_table"]; +fn test_jumpdest_analisys() -> Result<()> { + let jumpdest_analisys = KERNEL.global_labels["jumpdest_analisys"]; const CONTEXT: usize = 3; // arbitrary let add = get_opcode("ADD"); @@ -29,7 +29,7 @@ fn test_validate_jumpdest_table() -> Result<()> { // Contract creation transaction. let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; - let mut interpreter = Interpreter::new_with_kernel(validate_jumpdest_table, initial_stack); + let mut interpreter = Interpreter::new_with_kernel(jumpdest_analisys, initial_stack); interpreter.set_code(CONTEXT, code); interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 2d5bca1ac1..9599f335cb 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -299,15 +299,15 @@ fn simulate_cpu_between_labels_and_get_user_jumps( let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); state.registers.program_counter = KERNEL.global_labels[initial_label]; + let initial_clock = state.traces.clock(); let initial_context = state.registers.context; log::debug!("Simulating CPU for jumpdest analysis."); loop { // skip jumdest table validations in simulations - if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { - state.registers.program_counter = - KERNEL.global_labels["validate_jumpdest_table_end"] + if state.registers.program_counter == KERNEL.global_labels["jumpdest_analisys"] { + state.registers.program_counter = KERNEL.global_labels["jumpdest_analisys_end"] } let pc = state.registers.program_counter; let context = state.registers.context; @@ -337,6 +337,11 @@ fn simulate_cpu_between_labels_and_get_user_jumps( }, U256::one(), ); + let jumpdest_opcode = state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: jumpdest, + }); if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { ctx_addresses.insert(jumpdest); } else { @@ -344,7 +349,10 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } } if halt { - log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + log::debug!( + "Simulated CPU halted after {} cycles", + state.traces.clock() - initial_clock + ); return Ok(Some(jumpdest_addresses)); } transition(state).map_err(|_| { diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index a5f73ae687..ca9208ea4a 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -306,7 +306,7 @@ impl GenerationState { // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", + "jumpdest_analisys_end", "terminate_common", self, )? @@ -385,7 +385,8 @@ impl GenerationState { } } -/// For each address in `jumpdest_table` it search a proof, that is the closest address +/// For each address in `jumpdest_table`, each bounded by larges_address, +/// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes /// and pushed bytes are PUSHXX and the address is in its range. It returns /// a vector of even size containing proofs followed by their addresses diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..b8f962e74f 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,12 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?}", op); + log::debug!( + "User instruction: {:?}, ctx = {:?}, stack = {:?}", + op, + state.registers.context, + state.stack() + ); } fill_op_flag(op, &mut row); From 9e39d88ab808c8f9d8a1b1619064fa20ec83fdb3 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 17:33:53 +0100 Subject: [PATCH 040/175] Rebase to main --- evm/src/cpu/kernel/asm/core/call.asm | 9 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 182 ++++++++++++++++++ evm/src/generation/mod.rs | 65 ++++++- evm/src/generation/prover_input.rs | 153 ++++++++++++++- evm/src/generation/state.rs | 26 +++ evm/src/util.rs | 5 + evm/src/witness/errors.rs | 2 + evm/src/witness/transition.rs | 2 +- 8 files changed, 432 insertions(+), 12 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 2e7d1d7345..5a2a14c45f 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -367,11 +367,12 @@ call_too_deep: %checkpoint // Checkpoint %increment_call_depth // Perform jumpdest analyis - PUSH %%after - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - GET_CONTEXT + // PUSH %%after + // %mload_context_metadata(@CTX_METADATA_CODE_SIZE) + // GET_CONTEXT // stack: ctx, code_size, retdest - %jump(jumpdest_analysis) + // %jump(jumpdest_analysis) + %validate_jumpdest_table %%after: PUSH 0 // jump dest EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e63..09bb35fad4 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -1,3 +1,48 @@ +// Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], +// for the given context's code. Panics if we never hit final_pos +// Pre stack: init_pos, ctx, final_pos, retdest +// Post stack: (empty) +global verify_path: +loop_new: + // stack: i, ctx, final_pos, retdest + // Ideally we would break if i >= final_pos, but checking i > final_pos is + // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is + // a no-op. + DUP3 DUP2 EQ // i == final_pos + %jumpi(return_new) + DUP3 DUP2 GT // i > final_pos + %jumpi(panic) + + // stack: i, ctx, final_pos, retdest + %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) + MLOAD_GENERAL + // stack: opcode, i, ctx, final_pos, retdest + + DUP1 + // Slightly more efficient than `%eq_const(0x5b) ISZERO` + PUSH 0x5b + SUB + // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest + %jumpi(continue_new) + + // stack: JUMPDEST, i, ctx, code_len, retdest + %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + MSTORE_GENERAL + +continue_new: + // stack: opcode, i, ctx, code_len, retdest + %add_const(code_bytes_to_skip) + %mload_kernel_code + // stack: bytes_to_skip, i, ctx, code_len, retdest + ADD + // stack: i, ctx, code_len, retdest + %jump(loop_new) + +return_new: + // stack: i, ctx, code_len, retdest + %pop3 + JUMP + // Populates @SEGMENT_JUMPDEST_BITS for the given context's code. // Pre stack: ctx, code_len, retdest // Post stack: (empty) @@ -89,3 +134,140 @@ code_bytes_to_skip: %rep 128 BYTES 1 // 0x80-0xff %endrep + + +// A proof attesting that jumpdest is a valid jump destinations is +// either 0 or an index 0 < i <= jumpdest - 32. +// A proof is valid if: +// - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b +// - i > 0 and: +// - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, +// - we can go from opcode i+32 to jumpdest, +// - code[jumpdest] = 0x5b. +// stack: proof_prefix_addr, jumpdest, retdest +// stack: (empty) abort if jumpdest is not a valid destination +global is_jumpdest: + GET_CONTEXT + // stack: ctx, proof_prefix_addr, jumpdest, retdest + %stack + (ctx, proof_prefix_addr, jumpdest) -> + (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) + MLOAD_GENERAL + // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest + + // Slightly more efficient than `%eq_const(0x5b) ISZERO` + PUSH 0x5b + SUB + %jumpi(panic) + + //stack: jumpdest, ctx, proof_prefix_addr, retdest + SWAP2 DUP1 + // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest + %eq_const(0) + %jumpi(verify_path) + //stack: proof_prefix_addr, ctx, jumpdest, retdest + // If we are here we need to check that the next 32 bytes are less + // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, + // or larger than 127 + %check_and_step(127) %check_and_step(126) %check_and_step(125) %check_and_step(124) + %check_and_step(123) %check_and_step(122) %check_and_step(121) %check_and_step(120) + %check_and_step(119) %check_and_step(118) %check_and_step(117) %check_and_step(116) + %check_and_step(115) %check_and_step(114) %check_and_step(113) %check_and_step(112) + %check_and_step(111) %check_and_step(110) %check_and_step(109) %check_and_step(108) + %check_and_step(107) %check_and_step(106) %check_and_step(105) %check_and_step(104) + %check_and_step(103) %check_and_step(102) %check_and_step(101) %check_and_step(100) + %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) + + // check the remaining path + %jump(verify_path) + +return_is_jumpdest: + //stack: proof_prefix_addr, jumpdest, retdest + %pop2 + JUMP + + +// Chek if the opcode pointed by proof_prefix address is +// less than max and increment proof_prefix_addr +%macro check_and_step(max) + %stack + (proof_prefix_addr, ctx, jumpdest) -> + (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) + MLOAD_GENERAL + // stack: opcode, proof_prefix_addr, ctx, jumpdest + DUP1 + %gt_const(127) + %jumpi(%%ok) + %assert_lt_const($max) + // stack: proof_prefix_addr, ctx, jumpdest + PUSH 0 // We need something to pop +%%ok: + POP + %increment +%endmacro + +%macro is_jumpdest + %stack (proof, addr) -> (proof, addr, %%after) + %jump(is_jumpdest) +%%after: +%endmacro + +// Check if the jumpdest table is correct. This is done by +// non-deterministically guessing the sequence of jumpdest +// addresses used during program execution within the current context. +// For each jumpdest address we also non-deterministically guess +// a proof, which is another address in the code, such that +// is_jumpdest don't abort when the proof is on the top of the stack +// an the jumpdest address below. If that's the case we set the +// corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. +// +// stack: retdest +// stack: (empty) +global validate_jumpdest_table: + // If address > 0 it is interpreted as address' = address - 1 + // and the next prover input should contain a proof for address'. + PROVER_INPUT(jumpdest_table::next_address) + DUP1 %jumpi(check_proof) + // If proof == 0 there are no more jump destionations to check + POP +global validate_jumpdest_table_end: + JUMP + // were set to 0 + //%mload_context_metadata(@CTX_METADATA_CODE_SIZE) + // get the code length in bytes + //%add_const(31) + //%div_const(32) + //GET_CONTEXT + //SWAP2 +//verify_chunk: + // stack: i (= proof), code_len, ctx = 0 + //%stack (i, code_len, ctx) -> (code_len, i, ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx) + //GT + //%jumpi(valid_table) + //%mload_packing + // stack: packed_bits, code_len, i, ctx + //%assert_eq_const(0) + //%increment + //%jump(verify_chunk) + +check_proof: + %sub_const(1) + DUP1 + // We read the proof + PROVER_INPUT(jumpdest_table::next_proof) + // stack: proof, address + %is_jumpdest + GET_CONTEXT + %stack (ctx, address) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address) + MSTORE_GENERAL + %jump(validate_jumpdest_table) +valid_table: + // stack: ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx, retdest + %pop7 + JUMP + +%macro validate_jumpdest_table + PUSH %%after + %jump(validate_jumpdest_table) +%%after: +%endmacro diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d691d34e61..2c3ee9001f 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -8,6 +8,7 @@ use ethereum_types::{Address, BigEndianHash, H256, U256}; use itertools::enumerate; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -21,13 +22,15 @@ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::opcodes::get_opcode; use crate::generation::state::GenerationState; use crate::generation::trie_extractor::{get_receipt_trie, get_state_trie, get_txn_trie}; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::prover::check_abort_signal; -use crate::util::{h2u, u256_to_usize}; +use crate::util::{h2u, u256_to_u8, u256_to_usize}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -38,7 +41,7 @@ pub(crate) mod state; mod trie_extractor; use self::mpt::{load_all_mpts, TrieRootPtrs}; -use crate::witness::util::mem_write_log; +use crate::witness::util::{mem_write_log, stack_peek}; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] @@ -296,9 +299,7 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn simulate_cpu, const D: usize>( - state: &mut GenerationState, -) -> anyhow::Result<()> { +fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { let halt_pc = KERNEL.global_labels["halt"]; loop { @@ -333,3 +334,57 @@ fn simulate_cpu, const D: usize>( transition(state)?; } } + +fn simulate_cpu_between_labels_and_get_user_jumps( + initial_label: &str, + final_label: &str, + state: &mut GenerationState, +) -> anyhow::Result> { + let halt_pc = KERNEL.global_labels[final_label]; + let mut jumpdest_addresses = HashSet::new(); + state.registers.program_counter = KERNEL.global_labels[initial_label]; + let context = state.registers.context; + + loop { + if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { + state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] + } + let pc = state.registers.program_counter; + let halt = state.registers.is_kernel && pc == halt_pc && state.registers.context == context; + let opcode = u256_to_u8(state.memory.get(MemoryAddress { + context: state.registers.context, + segment: Segment::Code as usize, + virt: state.registers.program_counter, + })) + .map_err(|_| anyhow::Error::msg("Invalid opcode."))?; + let cond = if let Ok(cond) = stack_peek(state, 1) { + cond != U256::zero() + } else { + false + }; + if !state.registers.is_kernel + && (opcode == get_opcode("JUMP") || (opcode == get_opcode("JUMPI") && cond)) + { + // TODO: hotfix for avoiding deeper calls to abort + let jumpdest = u256_to_usize(state.registers.stack_top) + .map_err(|_| anyhow::Error::msg("Not a valid jump destination"))?; + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: jumpdest, + }, + U256::one(), + ); + if (state.registers.context == context) { + jumpdest_addresses.insert(jumpdest); + } + } + if halt { + log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + return Ok(jumpdest_addresses.into_iter().collect()); + } + + transition(state)?; + } +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b2a8f0cea0..852cd77da5 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,24 +1,34 @@ +use std::cmp::min; +use std::collections::HashSet; use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; +use hashbrown::HashMap; use itertools::{enumerate, Itertools}; use num_bigint::BigUint; +use plonky2::field::extension::Extendable; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; +use crate::generation::simulate_cpu_between_labels_and_get_user_jumps; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::memory::segments::Segment::BnPairing; -use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; -use crate::witness::errors::ProgramError; +use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_u8, u256_to_usize}; use crate::witness::errors::ProverInputError::*; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::MemoryAddress; use crate::witness::util::{current_context_peek, stack_peek}; @@ -47,6 +57,7 @@ impl GenerationState { "bignum_modmul" => self.run_bignum_modmul(), "withdrawal" => self.run_withdrawal(), "num_bits" => self.run_num_bits(), + "jumpdest_table" => self.run_jumpdest_table(input_fn), _ => Err(ProgramError::ProverInputError(InvalidFunction)), } } @@ -229,6 +240,144 @@ impl GenerationState { Ok(num_bits.into()) } } + + fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { + match input_fn.0[1].as_str() { + "next_address" => self.run_next_jumpdest_table_address(), + "next_proof" => self.run_next_jumpdest_table_proof(), + _ => Err(ProgramError::ProverInputError(InvalidInput)), + } + } + /// Return the next used jump addres + fn run_next_jumpdest_table_address(&mut self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + + if self.jumpdest_addresses.is_none() { + let mut state: GenerationState = self.soft_clone(); + + let mut jumpdest_addresses = vec![]; + // Generate the jumpdest table + let code = (0..code_len) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i, + })) + }) + .collect::, _>>()?; + let mut i = 0; + while i < code_len { + if code[i] == get_opcode("JUMPDEST") { + jumpdest_addresses.push(i); + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: i, + }, + U256::one(), + ); + log::debug!("jumpdest at {i}"); + } + i += if code[i] >= get_push_opcode(1) && code[i] <= get_push_opcode(32) { + (code[i] - get_push_opcode(1) + 2).into() + } else { + 1 + } + } + + // We need to skip the validate table call + self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + &mut state, + ) + .ok(); + log::debug!("code len = {code_len}"); + log::debug!("all jumpdest addresses = {:?}", jumpdest_addresses); + log::debug!("user's jumdest addresses = {:?}", self.jumpdest_addresses); + // self.jumpdest_addresses = Some(jumpdest_addresses); + } + + let Some(jumpdest_table) = &mut self.jumpdest_addresses else { + // TODO: Add another error + return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); + }; + + if let Some(next_jumpdest_address) = jumpdest_table.pop() { + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) + } else { + self.jumpdest_addresses = None; + Ok(U256::zero()) + } + } + + /// Return the proof for the last jump adddress + fn run_next_jumpdest_table_proof(&mut self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + + let mut address = MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: 0, + }; + let mut proof = 0; + let mut prefix_size = 0; + + // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem + // a problem because is done natively. + + // Search the closest address to last_jumpdest_address for which none of + // the previous 32 bytes in the code (including opcodes and pushed bytes) + // are PUSHXX and the address is in its range + while address.virt < self.last_jumpdest_address { + let opcode = u256_to_u8(self.memory.get(address))?; + let is_push = + opcode >= get_push_opcode(1).into() && opcode <= get_push_opcode(32).into(); + + address.virt += if is_push { + (opcode - get_push_opcode(1) + 2).into() + } else { + 1 + }; + // Check if the new address has a prefix of size >= 32 + let mut has_prefix = true; + for i in address.virt as i32 - 32..address.virt as i32 { + let opcode = u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i as usize, + }))?; + if i < 0 + || (opcode >= get_push_opcode(1) + && opcode <= get_push_opcode(32) + && i + (opcode - get_push_opcode(1)) as i32 + 1 >= address.virt as i32) + { + has_prefix = false; + break; + } + } + if has_prefix { + proof = address.virt - 32; + } + } + if address.virt > self.last_jumpdest_address { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpDestination, + )); + } + Ok(proof.into()) + } } enum EvmField { diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 89ff0c5af9..79dd94fb2f 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -50,6 +50,9 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, + + pub(crate) last_jumpdest_address: usize, + pub(crate) jumpdest_addresses: Option>, } impl GenerationState { @@ -91,6 +94,8 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, + last_jumpdest_address: 0, + jumpdest_addresses: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -167,6 +172,27 @@ impl GenerationState { .map(|i| stack_peek(self, i).unwrap()) .collect() } + + /// Clone everything but the traces + pub(crate) fn soft_clone(&self) -> GenerationState { + Self { + inputs: self.inputs.clone(), + registers: self.registers.clone(), + memory: self.memory.clone(), + traces: Traces::default(), + rlp_prover_inputs: self.rlp_prover_inputs.clone(), + state_key_to_address: self.state_key_to_address.clone(), + bignum_modmul_result_limbs: self.bignum_modmul_result_limbs.clone(), + withdrawal_prover_inputs: self.withdrawal_prover_inputs.clone(), + trie_root_ptrs: TrieRootPtrs { + state_root_ptr: 0, + txn_root_ptr: 0, + receipt_root_ptr: 0, + }, + last_jumpdest_address: 0, + jumpdest_addresses: None, + } + } } /// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. diff --git a/evm/src/util.rs b/evm/src/util.rs index 3d9564b5ed..bbbd8af126 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,6 +70,11 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } +/// Safe alternative to `U256::as_u8()`, which errors in case of overflow instead of panicking. +pub(crate) fn u256_to_u8(u256: U256) -> Result { + u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) +} + /// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 5a0fcbfb32..1b266aefde 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -36,4 +36,6 @@ pub enum ProverInputError { InvalidInput, InvalidFunction, NumBitsError, + InvalidJumpDestination, + InvalidJumpdestSimulation, } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..0fa14321f2 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,7 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?}", op); + log::debug!("User instruction: {:?} stack = {:?}", op, state.stack()); } fill_op_flag(op, &mut row); From ff3dc2e51615229c054e53f570e91576363cd73f Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 14:11:43 +0100 Subject: [PATCH 041/175] Refactor run_next_jumpdest_table_proof --- evm/src/cpu/kernel/asm/core/call.asm | 7 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 114 +++------- evm/src/cpu/kernel/interpreter.rs | 9 + .../kernel/tests/core/jumpdest_analysis.rs | 70 +++--- evm/src/generation/mod.rs | 6 +- evm/src/generation/prover_input.rs | 207 +++++++++++------- evm/src/witness/transition.rs | 7 +- 7 files changed, 206 insertions(+), 214 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 5a2a14c45f..46765954c4 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -367,13 +367,10 @@ call_too_deep: %checkpoint // Checkpoint %increment_call_depth // Perform jumpdest analyis - // PUSH %%after - // %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - // GET_CONTEXT + GET_CONTEXT // stack: ctx, code_size, retdest - // %jump(jumpdest_analysis) %validate_jumpdest_table -%%after: + PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 09bb35fad4..76c25fa07d 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -3,13 +3,13 @@ // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) global verify_path: -loop_new: +loop: // stack: i, ctx, final_pos, retdest // Ideally we would break if i >= final_pos, but checking i > final_pos is // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is // a no-op. DUP3 DUP2 EQ // i == final_pos - %jumpi(return_new) + %jumpi(return) DUP3 DUP2 GT // i > final_pos %jumpi(panic) @@ -18,51 +18,6 @@ loop_new: MLOAD_GENERAL // stack: opcode, i, ctx, final_pos, retdest - DUP1 - // Slightly more efficient than `%eq_const(0x5b) ISZERO` - PUSH 0x5b - SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest - %jumpi(continue_new) - - // stack: JUMPDEST, i, ctx, code_len, retdest - %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) - MSTORE_GENERAL - -continue_new: - // stack: opcode, i, ctx, code_len, retdest - %add_const(code_bytes_to_skip) - %mload_kernel_code - // stack: bytes_to_skip, i, ctx, code_len, retdest - ADD - // stack: i, ctx, code_len, retdest - %jump(loop_new) - -return_new: - // stack: i, ctx, code_len, retdest - %pop3 - JUMP - -// Populates @SEGMENT_JUMPDEST_BITS for the given context's code. -// Pre stack: ctx, code_len, retdest -// Post stack: (empty) -global jumpdest_analysis: - // stack: ctx, code_len, retdest - PUSH 0 // i = 0 - -loop: - // stack: i, ctx, code_len, retdest - // Ideally we would break if i >= code_len, but checking i > code_len is - // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is - // a no-op. - DUP3 DUP2 GT // i > code_len - %jumpi(return) - - // stack: i, ctx, code_len, retdest - %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) - MLOAD_GENERAL - // stack: opcode, i, ctx, code_len, retdest - DUP1 // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b @@ -144,13 +99,17 @@ code_bytes_to_skip: // - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, // - we can go from opcode i+32 to jumpdest, // - code[jumpdest] = 0x5b. -// stack: proof_prefix_addr, jumpdest, retdest +// stack: proof_prefix_addr, jumpdest, ctx, retdest // stack: (empty) abort if jumpdest is not a valid destination global is_jumpdest: - GET_CONTEXT - // stack: ctx, proof_prefix_addr, jumpdest, retdest + // stack: proof_prefix_addr, jumpdest, ctx, retdest + //%stack + // (proof_prefix_addr, jumpdest, ctx) -> + // (ctx, @SEGMENT_JUMPDEST_BITS, jumpdest, proof_prefix_addr, jumpdest, ctx) + //MLOAD_GENERAL + //%jumpi(return_is_jumpdest) %stack - (ctx, proof_prefix_addr, jumpdest) -> + (proof_prefix_addr, jumpdest, ctx) -> (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest @@ -182,8 +141,8 @@ global is_jumpdest: %jump(verify_path) return_is_jumpdest: - //stack: proof_prefix_addr, jumpdest, retdest - %pop2 + //stack: proof_prefix_addr, jumpdest, ctx, retdest + %pop3 JUMP @@ -194,7 +153,7 @@ return_is_jumpdest: (proof_prefix_addr, ctx, jumpdest) -> (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) MLOAD_GENERAL - // stack: opcode, proof_prefix_addr, ctx, jumpdest + // stack: opcode, ctx, proof_prefix_addr, jumpdest DUP1 %gt_const(127) %jumpi(%%ok) @@ -207,7 +166,7 @@ return_is_jumpdest: %endmacro %macro is_jumpdest - %stack (proof, addr) -> (proof, addr, %%after) + %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) %jump(is_jumpdest) %%after: %endmacro @@ -216,58 +175,41 @@ return_is_jumpdest: // non-deterministically guessing the sequence of jumpdest // addresses used during program execution within the current context. // For each jumpdest address we also non-deterministically guess -// a proof, which is another address in the code, such that -// is_jumpdest don't abort when the proof is on the top of the stack +// a proof, which is another address in the code such that +// is_jumpdest don't abort, when the proof is at the top of the stack // an the jumpdest address below. If that's the case we set the // corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. // -// stack: retdest +// stack: ctx, retdest // stack: (empty) global validate_jumpdest_table: - // If address > 0 it is interpreted as address' = address - 1 + // If address > 0 then address is interpreted as address' + 1 // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) DUP1 %jumpi(check_proof) // If proof == 0 there are no more jump destionations to check POP +// This is just a hook used for avoiding verification of the jumpdest +// table in another contexts. It is useful during proof generation, +// allowing the avoidance of table verification when simulating user code. global validate_jumpdest_table_end: + POP JUMP - // were set to 0 - //%mload_context_metadata(@CTX_METADATA_CODE_SIZE) - // get the code length in bytes - //%add_const(31) - //%div_const(32) - //GET_CONTEXT - //SWAP2 -//verify_chunk: - // stack: i (= proof), code_len, ctx = 0 - //%stack (i, code_len, ctx) -> (code_len, i, ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx) - //GT - //%jumpi(valid_table) - //%mload_packing - // stack: packed_bits, code_len, i, ctx - //%assert_eq_const(0) - //%increment - //%jump(verify_chunk) - check_proof: %sub_const(1) - DUP1 + DUP2 DUP2 + // stack: address, ctx, address, ctx // We read the proof PROVER_INPUT(jumpdest_table::next_proof) - // stack: proof, address + // stack: proof, address, ctx, address, ctx %is_jumpdest - GET_CONTEXT - %stack (ctx, address) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address) + %stack (address, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address, ctx) MSTORE_GENERAL + %jump(validate_jumpdest_table) -valid_table: - // stack: ctx, @SEGMENT_JUMPDEST_BITS, i, 32, i, code_len, ctx, retdest - %pop7 - JUMP %macro validate_jumpdest_table - PUSH %%after + %stack (ctx) -> (ctx, %%after) %jump(validate_jumpdest_table) %%after: %endmacro diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c437672113..1efaa71da5 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -10,6 +10,7 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use super::assembler::BYTES_PER_OFFSET; +use super::utils::u256_from_bool; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -413,6 +414,14 @@ impl<'a> Interpreter<'a> { .collect() } + pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] + .content = jumpdest_bits + .into_iter() + .map(|x| u256_from_bool(x)) + .collect(); + } + fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 022a18d729..d3edc17bfc 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -4,39 +4,37 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -#[test] -fn test_jumpdest_analysis() -> Result<()> { - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; - const CONTEXT: usize = 3; // arbitrary - - let add = get_opcode("ADD"); - let push2 = get_push_opcode(2); - let jumpdest = get_opcode("JUMPDEST"); - - #[rustfmt::skip] - let code: Vec = vec![ - add, - jumpdest, - push2, - jumpdest, // part of PUSH2 - jumpdest, // part of PUSH2 - jumpdest, - add, - jumpdest, - ]; - - let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true]; - - // Contract creation transaction. - let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()]; - let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); - interpreter.set_code(CONTEXT, code); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![]); - assert_eq!( - interpreter.get_jumpdest_bits(CONTEXT), - expected_jumpdest_bits - ); - - Ok(()) -} +// #[test] +// fn test_jumpdest_analysis() -> Result<()> { +// let jumpdest_analysis = KERNEL.global_labels["validate_jumpdest_table"]; +// const CONTEXT: usize = 3; // arbitrary + +// let add = get_opcode("ADD"); +// let push2 = get_push_opcode(2); +// let jumpdest = get_opcode("JUMPDEST"); + +// #[rustfmt::skip] +// let code: Vec = vec![ +// add, +// jumpdest, +// push2, +// jumpdest, // part of PUSH2 +// jumpdest, // part of PUSH2 +// jumpdest, +// add, +// jumpdest, +// ]; + +// let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + +// // Contract creation transaction. +// let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; +// let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); +// interpreter.set_code(CONTEXT, code); +// interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + +// interpreter.run()?; +// assert_eq!(interpreter.stack(), vec![]); + +// Ok(()) +// } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 2c3ee9001f..995e067b2b 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -345,6 +345,8 @@ fn simulate_cpu_between_labels_and_get_user_jumps( state.registers.program_counter = KERNEL.global_labels[initial_label]; let context = state.registers.context; + log::debug!("Simulating CPU for jumpdest analysis "); + loop { if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] @@ -382,7 +384,9 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } if halt { log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - return Ok(jumpdest_addresses.into_iter().collect()); + let mut jumpdest_addresses: Vec = jumpdest_addresses.into_iter().collect(); + jumpdest_addresses.sort(); + return Ok(jumpdest_addresses); } transition(state)?; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 852cd77da5..2435f4ebf7 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -257,51 +257,7 @@ impl GenerationState { }))?; if self.jumpdest_addresses.is_none() { - let mut state: GenerationState = self.soft_clone(); - - let mut jumpdest_addresses = vec![]; - // Generate the jumpdest table - let code = (0..code_len) - .map(|i| { - u256_to_u8(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: i, - })) - }) - .collect::, _>>()?; - let mut i = 0; - while i < code_len { - if code[i] == get_opcode("JUMPDEST") { - jumpdest_addresses.push(i); - state.memory.set( - MemoryAddress { - context: state.registers.context, - segment: Segment::JumpdestBits as usize, - virt: i, - }, - U256::one(), - ); - log::debug!("jumpdest at {i}"); - } - i += if code[i] >= get_push_opcode(1) && code[i] <= get_push_opcode(32) { - (code[i] - get_push_opcode(1) + 2).into() - } else { - 1 - } - } - - // We need to skip the validate table call - self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", - "terminate_common", - &mut state, - ) - .ok(); - log::debug!("code len = {code_len}"); - log::debug!("all jumpdest addresses = {:?}", jumpdest_addresses); - log::debug!("user's jumdest addresses = {:?}", self.jumpdest_addresses); - // self.jumpdest_addresses = Some(jumpdest_addresses); + self.generate_jumpdest_table()?; } let Some(jumpdest_table) = &mut self.jumpdest_addresses else { @@ -326,57 +282,138 @@ impl GenerationState { virt: ContextMetadata::CodeSize as usize, }))?; - let mut address = MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: 0, - }; - let mut proof = 0; - let mut prefix_size = 0; + let code = (0..self.last_jumpdest_address) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i, + })) + }) + .collect::, _>>()?; // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem - // a problem because is done natively. + // a problem as is done natively. // Search the closest address to last_jumpdest_address for which none of // the previous 32 bytes in the code (including opcodes and pushed bytes) // are PUSHXX and the address is in its range - while address.virt < self.last_jumpdest_address { - let opcode = u256_to_u8(self.memory.get(address))?; - let is_push = - opcode >= get_push_opcode(1).into() && opcode <= get_push_opcode(32).into(); - - address.virt += if is_push { - (opcode - get_push_opcode(1) + 2).into() - } else { - 1 - }; - // Check if the new address has a prefix of size >= 32 - let mut has_prefix = true; - for i in address.virt as i32 - 32..address.virt as i32 { - let opcode = u256_to_u8(self.memory.get(MemoryAddress { + + let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( + 0, + |acc, (pos, opcode)| { + let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { + code[prefix_start..pos].iter().enumerate().fold( + true, + |acc, (prefix_pos, &byte)| { + acc && (byte > get_push_opcode(32) + || (prefix_start + prefix_pos) as i32 + + (byte as i32 - get_push_opcode(1) as i32) + + 1 + < pos as i32) + }, + ) + } else { + false + }; + if has_prefix { + pos - 32 + } else { + acc + } + }, + ); + Ok(proof.into()) + } +} + +impl GenerationState { + fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { + let mut state = self.soft_clone(); + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + // Generate the jumpdest table + let code = (0..code_len) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { context: self.registers.context, segment: Segment::Code as usize, - virt: i as usize, - }))?; - if i < 0 - || (opcode >= get_push_opcode(1) - && opcode <= get_push_opcode(32) - && i + (opcode - get_push_opcode(1)) as i32 + 1 >= address.virt as i32) - { - has_prefix = false; - break; - } - } - if has_prefix { - proof = address.virt - 32; + virt: i, + })) + }) + .collect::, _>>()?; + + // We need to set the the simulated jumpdest bits to one as otherwise + // the simulation will fail + let mut jumpdest_table = vec![]; + for (pos, opcode) in CodeIterator::new(&code) { + jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); + if opcode == get_opcode("JUMPDEST") { + state.memory.set( + MemoryAddress { + context: state.registers.context, + segment: Segment::JumpdestBits as usize, + virt: pos, + }, + U256::one(), + ); } } - if address.virt > self.last_jumpdest_address { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidJumpDestination, - )); + + // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call + self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + &mut state, + ) + .ok(); + + Ok(()) + } +} + +struct CodeIterator<'a> { + code: &'a Vec, + pos: usize, + end: usize, +} + +impl<'a> CodeIterator<'a> { + fn new(code: &'a Vec) -> Self { + CodeIterator { + end: code.len(), + code, + pos: 0, + } + } + fn until(code: &'a Vec, end: usize) -> Self { + CodeIterator { + end: std::cmp::min(code.len(), end), + code, + pos: 0, } - Ok(proof.into()) + } +} + +impl<'a> Iterator for CodeIterator<'a> { + type Item = (usize, u8); + + fn next(&mut self) -> Option { + let CodeIterator { code, pos, end } = self; + if *pos >= *end { + return None; + } + let opcode = code[*pos]; + let old_pos = *pos; + *pos += if opcode >= get_push_opcode(1) && opcode <= get_push_opcode(32) { + (opcode - get_push_opcode(1) + 2).into() + } else { + 1 + }; + Some((old_pos, opcode)) } } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 0fa14321f2..046885439e 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,12 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?} stack = {:?}", op, state.stack()); + log::debug!( + "User instruction: {:?} ctx = {:?} stack = {:?}", + op, + state.registers.context, + state.stack() + ); } fill_op_flag(op, &mut row); From ed260980b2983f6fb34ed94b93900887195543f6 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 17:06:42 +0100 Subject: [PATCH 042/175] Fix jumpdest analisys test --- evm/src/cpu/kernel/interpreter.rs | 13 ++-- .../kernel/tests/core/jumpdest_analysis.rs | 68 +++++++++---------- evm/src/generation/prover_input.rs | 6 -- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 1efaa71da5..c67d793d3a 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -416,10 +416,15 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] - .content = jumpdest_bits - .into_iter() - .map(|x| u256_from_bool(x)) - .collect(); + .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); + self.generation_state.jumpdest_addresses = Some( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i) + .collect(), + ) } fn incr(&mut self, n: usize) { diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index d3edc17bfc..58e9f936b5 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -4,37 +4,37 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -// #[test] -// fn test_jumpdest_analysis() -> Result<()> { -// let jumpdest_analysis = KERNEL.global_labels["validate_jumpdest_table"]; -// const CONTEXT: usize = 3; // arbitrary - -// let add = get_opcode("ADD"); -// let push2 = get_push_opcode(2); -// let jumpdest = get_opcode("JUMPDEST"); - -// #[rustfmt::skip] -// let code: Vec = vec![ -// add, -// jumpdest, -// push2, -// jumpdest, // part of PUSH2 -// jumpdest, // part of PUSH2 -// jumpdest, -// add, -// jumpdest, -// ]; - -// let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; - -// // Contract creation transaction. -// let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; -// let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); -// interpreter.set_code(CONTEXT, code); -// interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); - -// interpreter.run()?; -// assert_eq!(interpreter.stack(), vec![]); - -// Ok(()) -// } +#[test] +fn test_validate_jumpdest_table() -> Result<()> { + let validate_jumpdest_table = KERNEL.global_labels["validate_jumpdest_table"]; + const CONTEXT: usize = 3; // arbitrary + + let add = get_opcode("ADD"); + let push2 = get_push_opcode(2); + let jumpdest = get_opcode("JUMPDEST"); + + #[rustfmt::skip] + let code: Vec = vec![ + add, + jumpdest, + push2, + jumpdest, // part of PUSH2 + jumpdest, // part of PUSH2 + jumpdest, + add, + jumpdest, + ]; + + let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + + // Contract creation transaction. + let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; + let mut interpreter = Interpreter::new_with_kernel(validate_jumpdest_table, initial_stack); + interpreter.set_code(CONTEXT, code); + interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + Ok(()) +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 2435f4ebf7..53e25db4b2 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -276,12 +276,6 @@ impl GenerationState { /// Return the proof for the last jump adddress fn run_next_jumpdest_table_proof(&mut self) -> Result { - let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::CodeSize as usize, - }))?; - let code = (0..self.last_jumpdest_address) .map(|i| { u256_to_u8(self.memory.get(MemoryAddress { From 0bec6278996adeb3035372dd11013f8184bcf5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Fri, 15 Dec 2023 09:49:19 +0100 Subject: [PATCH 043/175] Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- evm/src/cpu/kernel/asm/core/call.asm | 1 - evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 10 +++++----- evm/src/cpu/kernel/interpreter.rs | 2 +- evm/src/generation/mod.rs | 4 ++-- evm/src/generation/prover_input.rs | 14 +++++++------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 46765954c4..5173d35822 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -370,7 +370,6 @@ call_too_deep: GET_CONTEXT // stack: ctx, code_size, retdest %validate_jumpdest_table - PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 76c25fa07d..79475b3789 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -122,9 +122,9 @@ global is_jumpdest: //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest - %eq_const(0) + IS_ZERO %jumpi(verify_path) - //stack: proof_prefix_addr, ctx, jumpdest, retdest + // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, // or larger than 127 @@ -141,7 +141,7 @@ global is_jumpdest: %jump(verify_path) return_is_jumpdest: - //stack: proof_prefix_addr, jumpdest, ctx, retdest + // stack: proof_prefix_addr, jumpdest, ctx, retdest %pop3 JUMP @@ -187,7 +187,7 @@ global validate_jumpdest_table: // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) DUP1 %jumpi(check_proof) - // If proof == 0 there are no more jump destionations to check + // If proof == 0 there are no more jump destinations to check POP // This is just a hook used for avoiding verification of the jumpdest // table in another contexts. It is useful during proof generation, @@ -196,7 +196,7 @@ global validate_jumpdest_table_end: POP JUMP check_proof: - %sub_const(1) + %decrement DUP2 DUP2 // stack: address, ctx, address, ctx // We read the proof diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c67d793d3a..5645045cfc 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -427,7 +427,7 @@ impl<'a> Interpreter<'a> { ) } - fn incr(&mut self, n: usize) { + const fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 995e067b2b..b6146260ef 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -345,7 +345,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( state.registers.program_counter = KERNEL.global_labels[initial_label]; let context = state.registers.context; - log::debug!("Simulating CPU for jumpdest analysis "); + log::debug!("Simulating CPU for jumpdest analysis."); loop { if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { @@ -369,7 +369,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( { // TODO: hotfix for avoiding deeper calls to abort let jumpdest = u256_to_usize(state.registers.stack_top) - .map_err(|_| anyhow::Error::msg("Not a valid jump destination"))?; + .map_err(|_| anyhow!("Not a valid jump destination"))?; state.memory.set( MemoryAddress { context: state.registers.context, diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 53e25db4b2..1808f3f3b2 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -274,7 +274,7 @@ impl GenerationState { } } - /// Return the proof for the last jump adddress + /// Returns the proof for the last jump address. fn run_next_jumpdest_table_proof(&mut self) -> Result { let code = (0..self.last_jumpdest_address) .map(|i| { @@ -286,12 +286,12 @@ impl GenerationState { }) .collect::, _>>()?; - // TODO: The proof searching algorithm is not very eficient. But luckyly it doesn't seem + // TODO: The proof searching algorithm is not very efficient. But luckily it doesn't seem // a problem as is done natively. - // Search the closest address to last_jumpdest_address for which none of + // Search the closest address to `last_jumpdest_address` for which none of // the previous 32 bytes in the code (including opcodes and pushed bytes) - // are PUSHXX and the address is in its range + // are PUSHXX and the address is in its range. let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( 0, @@ -340,9 +340,9 @@ impl GenerationState { }) .collect::, _>>()?; - // We need to set the the simulated jumpdest bits to one as otherwise - // the simulation will fail - let mut jumpdest_table = vec![]; + // We need to set the simulated jumpdest bits to one as otherwise + // the simulation will fail. + let mut jumpdest_table = Vec::with_capacity(code.len()); for (pos, opcode) in CodeIterator::new(&code) { jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); if opcode == get_opcode("JUMPDEST") { From 5acabad72d31d244cb68a4214e473b9616b03e59 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 17:11:00 +0100 Subject: [PATCH 044/175] Eliminate nested simulations --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 7 +- evm/src/cpu/kernel/interpreter.rs | 22 ++-- evm/src/generation/mod.rs | 109 ++++++++++-------- evm/src/generation/prover_input.rs | 45 ++++---- evm/src/generation/state.rs | 4 +- evm/src/witness/transition.rs | 7 +- 6 files changed, 103 insertions(+), 91 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 79475b3789..cfc3575b14 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -114,15 +114,12 @@ global is_jumpdest: MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - // Slightly more efficient than `%eq_const(0x5b) ISZERO` - PUSH 0x5b - SUB - %jumpi(panic) + %assert_eq_const(0x5b) //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest - IS_ZERO + ISZERO %jumpi(verify_path) // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5645045cfc..177ac4f0f7 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1,7 +1,7 @@ //! An EVM interpreter for testing and debugging purposes. use core::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::ops::Range; use anyhow::bail; @@ -417,17 +417,19 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); - self.generation_state.jumpdest_addresses = Some( - jumpdest_bits - .into_iter() - .enumerate() - .filter(|&(_, x)| x) - .map(|(i, _)| i) - .collect(), - ) + self.generation_state.jumpdest_addresses = Some(HashMap::from([( + context, + BTreeSet::from_iter( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i), + ), + )])); } - const fn incr(&mut self, n: usize) { + pub(crate) fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index b6146260ef..1919b40dac 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap, HashMap, HashSet}; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -31,6 +31,7 @@ use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::prover::check_abort_signal; use crate::util::{h2u, u256_to_u8, u256_to_usize}; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -339,56 +340,68 @@ fn simulate_cpu_between_labels_and_get_user_jumps( initial_label: &str, final_label: &str, state: &mut GenerationState, -) -> anyhow::Result> { - let halt_pc = KERNEL.global_labels[final_label]; - let mut jumpdest_addresses = HashSet::new(); - state.registers.program_counter = KERNEL.global_labels[initial_label]; - let context = state.registers.context; +) -> Result<(), ProgramError> { + if let Some(_) = state.jumpdest_addresses { + Ok(()) + } else { + const JUMP_OPCODE: u8 = 0x56; + const JUMPI_OPCODE: u8 = 0x57; - log::debug!("Simulating CPU for jumpdest analysis."); + let halt_pc = KERNEL.global_labels[final_label]; + let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); - loop { - if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { - state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] - } - let pc = state.registers.program_counter; - let halt = state.registers.is_kernel && pc == halt_pc && state.registers.context == context; - let opcode = u256_to_u8(state.memory.get(MemoryAddress { - context: state.registers.context, - segment: Segment::Code as usize, - virt: state.registers.program_counter, - })) - .map_err(|_| anyhow::Error::msg("Invalid opcode."))?; - let cond = if let Ok(cond) = stack_peek(state, 1) { - cond != U256::zero() - } else { - false - }; - if !state.registers.is_kernel - && (opcode == get_opcode("JUMP") || (opcode == get_opcode("JUMPI") && cond)) - { - // TODO: hotfix for avoiding deeper calls to abort - let jumpdest = u256_to_usize(state.registers.stack_top) - .map_err(|_| anyhow!("Not a valid jump destination"))?; - state.memory.set( - MemoryAddress { - context: state.registers.context, - segment: Segment::JumpdestBits as usize, - virt: jumpdest, - }, - U256::one(), - ); - if (state.registers.context == context) { - jumpdest_addresses.insert(jumpdest); + state.registers.program_counter = KERNEL.global_labels[initial_label]; + let initial_context = state.registers.context; + + log::debug!("Simulating CPU for jumpdest analysis."); + + loop { + if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { + state.registers.program_counter = + KERNEL.global_labels["validate_jumpdest_table_end"] } + let pc = state.registers.program_counter; + let context = state.registers.context; + let halt = state.registers.is_kernel + && pc == halt_pc + && state.registers.context == initial_context; + let opcode = u256_to_u8(state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: state.registers.program_counter, + }))?; + let cond = if let Ok(cond) = stack_peek(state, 1) { + cond != U256::zero() + } else { + false + }; + if !state.registers.is_kernel + && (opcode == JUMP_OPCODE || (opcode == JUMPI_OPCODE && cond)) + { + // Avoid deeper calls to abort + let jumpdest = u256_to_usize(state.registers.stack_top)?; + state.memory.set( + MemoryAddress { + context, + segment: Segment::JumpdestBits as usize, + virt: jumpdest, + }, + U256::one(), + ); + if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { + ctx_addresses.insert(jumpdest); + } else { + jumpdest_addresses.insert(context, BTreeSet::from([jumpdest])); + } + } + if halt { + log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + state.jumpdest_addresses = Some(jumpdest_addresses); + return Ok(()); + } + transition(state).map_err(|_| { + ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation) + })?; } - if halt { - log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - let mut jumpdest_addresses: Vec = jumpdest_addresses.into_iter().collect(); - jumpdest_addresses.sort(); - return Ok(jumpdest_addresses); - } - - transition(state)?; } } diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 1808f3f3b2..35571dcb43 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -16,7 +16,6 @@ use serde::{Deserialize, Serialize}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, @@ -250,8 +249,9 @@ impl GenerationState { } /// Return the next used jump addres fn run_next_jumpdest_table_address(&mut self) -> Result { + let context = self.registers.context; let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, + context, segment: Segment::ContextMetadata as usize, virt: ContextMetadata::CodeSize as usize, }))?; @@ -260,14 +260,14 @@ impl GenerationState { self.generate_jumpdest_table()?; } - let Some(jumpdest_table) = &mut self.jumpdest_addresses else { - // TODO: Add another error + let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); }; - if let Some(next_jumpdest_address) = jumpdest_table.pop() { - self.last_jumpdest_address = next_jumpdest_address; - Ok((next_jumpdest_address + 1).into()) + if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + { + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) } else { self.jumpdest_addresses = None; Ok(U256::zero()) @@ -293,6 +293,9 @@ impl GenerationState { // the previous 32 bytes in the code (including opcodes and pushed bytes) // are PUSHXX and the address is in its range. + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x7f; + let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( 0, |acc, (pos, opcode)| { @@ -300,9 +303,9 @@ impl GenerationState { code[prefix_start..pos].iter().enumerate().fold( true, |acc, (prefix_pos, &byte)| { - acc && (byte > get_push_opcode(32) + acc && (byte > PUSH32_OPCODE || (prefix_start + prefix_pos) as i32 - + (byte as i32 - get_push_opcode(1) as i32) + + (byte as i32 - PUSH1_OPCODE as i32) + 1 < pos as i32) }, @@ -323,6 +326,7 @@ impl GenerationState { impl GenerationState { fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { + const JUMPDEST_OPCODE: u8 = 0x5b; let mut state = self.soft_clone(); let code_len = u256_to_usize(self.memory.get(MemoryAddress { context: self.registers.context, @@ -344,8 +348,8 @@ impl GenerationState { // the simulation will fail. let mut jumpdest_table = Vec::with_capacity(code.len()); for (pos, opcode) in CodeIterator::new(&code) { - jumpdest_table.push((pos, opcode == get_opcode("JUMPDEST"))); - if opcode == get_opcode("JUMPDEST") { + jumpdest_table.push((pos, opcode == JUMPDEST_OPCODE)); + if opcode == JUMPDEST_OPCODE { state.memory.set( MemoryAddress { context: state.registers.context, @@ -358,32 +362,31 @@ impl GenerationState { } // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - self.jumpdest_addresses = simulate_cpu_between_labels_and_get_user_jumps( + simulate_cpu_between_labels_and_get_user_jumps( "validate_jumpdest_table_end", "terminate_common", &mut state, - ) - .ok(); - + )?; + self.jumpdest_addresses = state.jumpdest_addresses; Ok(()) } } struct CodeIterator<'a> { - code: &'a Vec, + code: &'a [u8], pos: usize, end: usize, } impl<'a> CodeIterator<'a> { - fn new(code: &'a Vec) -> Self { + fn new(code: &'a [u8]) -> Self { CodeIterator { end: code.len(), code, pos: 0, } } - fn until(code: &'a Vec, end: usize) -> Self { + fn until(code: &'a [u8], end: usize) -> Self { CodeIterator { end: std::cmp::min(code.len(), end), code, @@ -396,14 +399,16 @@ impl<'a> Iterator for CodeIterator<'a> { type Item = (usize, u8); fn next(&mut self) -> Option { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x70; let CodeIterator { code, pos, end } = self; if *pos >= *end { return None; } let opcode = code[*pos]; let old_pos = *pos; - *pos += if opcode >= get_push_opcode(1) && opcode <= get_push_opcode(32) { - (opcode - get_push_opcode(1) + 2).into() + *pos += if opcode >= PUSH1_OPCODE && opcode <= PUSH32_OPCODE { + (opcode - PUSH1_OPCODE + 2).into() } else { 1 }; diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 79dd94fb2f..de07c942a3 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; @@ -52,7 +52,7 @@ pub(crate) struct GenerationState { pub(crate) trie_root_ptrs: TrieRootPtrs, pub(crate) last_jumpdest_address: usize, - pub(crate) jumpdest_addresses: Option>, + pub(crate) jumpdest_addresses: Option>>, } impl GenerationState { diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 046885439e..cf2e3bbeba 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,12 +395,7 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!( - "User instruction: {:?} ctx = {:?} stack = {:?}", - op, - state.registers.context, - state.stack() - ); + log::debug!("User instruction: {:?}", op); } fill_op_flag(op, &mut row); From 08982498d6e75174826a941a9f277920b47d1149 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 17:13:52 +0100 Subject: [PATCH 045/175] Remove U256::as_u8 in comment --- evm/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/util.rs b/evm/src/util.rs index bbbd8af126..fa19ef0998 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,7 +70,7 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } -/// Safe alternative to `U256::as_u8()`, which errors in case of overflow instead of panicking. +/// Safe conversion from U256 to u8, which errors in case of overflow instead of panicking. pub(crate) fn u256_to_u8(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } From aaa38b33ba898599007c71ed865c9b3b4edb2e41 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 18:14:47 +0100 Subject: [PATCH 046/175] Fix fmt --- evm/src/generation/prover_input.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 35571dcb43..3a298e8179 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -261,13 +261,16 @@ impl GenerationState { } let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { - return Err(ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation)); + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); }; - if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) + && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() { - self.last_jumpdest_address = next_jumpdest_address; - Ok((next_jumpdest_address + 1).into()) + self.last_jumpdest_address = next_jumpdest_address; + Ok((next_jumpdest_address + 1).into()) } else { self.jumpdest_addresses = None; Ok(U256::zero()) From c4025063dedfbf9bdc7a6e032bdc0fefd20a4c93 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 15 Dec 2023 18:52:40 +0100 Subject: [PATCH 047/175] Clippy --- evm/src/generation/mod.rs | 2 +- evm/src/generation/prover_input.rs | 2 +- evm/src/generation/state.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 1919b40dac..8f568d90cb 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -341,7 +341,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( final_label: &str, state: &mut GenerationState, ) -> Result<(), ProgramError> { - if let Some(_) = state.jumpdest_addresses { + if state.jumpdest_addresses.is_some() { Ok(()) } else { const JUMP_OPCODE: u8 = 0x56; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 3a298e8179..926b876da5 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -410,7 +410,7 @@ impl<'a> Iterator for CodeIterator<'a> { } let opcode = code[*pos]; let old_pos = *pos; - *pos += if opcode >= PUSH1_OPCODE && opcode <= PUSH32_OPCODE { + *pos += if (PUSH1_OPCODE..=PUSH32_OPCODE).contains(&opcode) { (opcode - PUSH1_OPCODE + 2).into() } else { 1 diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index de07c942a3..1c50cc294c 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -177,7 +177,7 @@ impl GenerationState { pub(crate) fn soft_clone(&self) -> GenerationState { Self { inputs: self.inputs.clone(), - registers: self.registers.clone(), + registers: self.registers, memory: self.memory.clone(), traces: Traces::default(), rlp_prover_inputs: self.rlp_prover_inputs.clone(), From 4e569484c2c4e1b15a19bb24fd1ac01a16b4a9ad Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Tue, 19 Dec 2023 14:05:51 +0100 Subject: [PATCH 048/175] Improve proof generation --- evm/src/cpu/kernel/interpreter.rs | 21 +-- evm/src/generation/mod.rs | 10 +- evm/src/generation/prover_input.rs | 199 ++++++++++++++++++----------- evm/src/generation/state.rs | 9 +- 4 files changed, 144 insertions(+), 95 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 177ac4f0f7..30f862cdd2 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -417,16 +417,17 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); - self.generation_state.jumpdest_addresses = Some(HashMap::from([( - context, - BTreeSet::from_iter( - jumpdest_bits - .into_iter() - .enumerate() - .filter(|&(_, x)| x) - .map(|(i, _)| i), - ), - )])); + self.generation_state + .set_proofs_and_jumpdests(HashMap::from([( + context, + BTreeSet::from_iter( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i), + ), + )])); } pub(crate) fn incr(&mut self, n: usize) { diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 8f568d90cb..81bacb75fa 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -340,9 +340,9 @@ fn simulate_cpu_between_labels_and_get_user_jumps( initial_label: &str, final_label: &str, state: &mut GenerationState, -) -> Result<(), ProgramError> { - if state.jumpdest_addresses.is_some() { - Ok(()) +) -> Result>>, ProgramError> { + if state.jumpdest_proofs.is_some() { + Ok(None) } else { const JUMP_OPCODE: u8 = 0x56; const JUMPI_OPCODE: u8 = 0x57; @@ -356,6 +356,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( log::debug!("Simulating CPU for jumpdest analysis."); loop { + // skip jumdest table validations in simulations if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { state.registers.program_counter = KERNEL.global_labels["validate_jumpdest_table_end"] @@ -396,8 +397,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } if halt { log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); - state.jumpdest_addresses = Some(jumpdest_addresses); - return Ok(()); + return Ok(Some(jumpdest_addresses)); } transition(state).map_err(|_| { ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 926b876da5..a5f73ae687 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,11 +1,10 @@ use std::cmp::min; -use std::collections::HashSet; +use std::collections::HashMap; use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; -use hashbrown::HashMap; use itertools::{enumerate, Itertools}; use num_bigint::BigUint; use plonky2::field::extension::Extendable; @@ -256,87 +255,98 @@ impl GenerationState { virt: ContextMetadata::CodeSize as usize, }))?; - if self.jumpdest_addresses.is_none() { - self.generate_jumpdest_table()?; + if self.jumpdest_proofs.is_none() { + self.generate_jumpdest_proofs()?; } - let Some(jumpdest_tables) = &mut self.jumpdest_addresses else { + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { return Err(ProgramError::ProverInputError( ProverInputError::InvalidJumpdestSimulation, )); }; - if let Some(ctx_jumpdest_table) = jumpdest_tables.get_mut(&context) - && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop_last() + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_address) = ctx_jumpdest_proofs.pop() { - self.last_jumpdest_address = next_jumpdest_address; Ok((next_jumpdest_address + 1).into()) } else { - self.jumpdest_addresses = None; + self.jumpdest_proofs = None; Ok(U256::zero()) } } /// Returns the proof for the last jump address. fn run_next_jumpdest_table_proof(&mut self) -> Result { - let code = (0..self.last_jumpdest_address) - .map(|i| { - u256_to_u8(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::Code as usize, - virt: i, - })) - }) - .collect::, _>>()?; + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); + }; + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_proof) = ctx_jumpdest_proofs.pop() + { + Ok(next_jumpdest_proof.into()) + } else { + Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )) + } + } +} - // TODO: The proof searching algorithm is not very efficient. But luckily it doesn't seem - // a problem as is done natively. +impl GenerationState { + fn generate_jumpdest_proofs(&mut self) -> Result<(), ProgramError> { + let checkpoint = self.checkpoint(); + let memory = self.memory.clone(); - // Search the closest address to `last_jumpdest_address` for which none of - // the previous 32 bytes in the code (including opcodes and pushed bytes) - // are PUSHXX and the address is in its range. + let code = self.get_current_code()?; + // We need to set the simulated jumpdest bits to one as otherwise + // the simulation will fail. + self.set_jumpdest_bits(&code); - const PUSH1_OPCODE: u8 = 0x60; - const PUSH32_OPCODE: u8 = 0x7f; - - let proof = CodeIterator::until(&code, self.last_jumpdest_address + 1).fold( - 0, - |acc, (pos, opcode)| { - let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { - code[prefix_start..pos].iter().enumerate().fold( - true, - |acc, (prefix_pos, &byte)| { - acc && (byte > PUSH32_OPCODE - || (prefix_start + prefix_pos) as i32 - + (byte as i32 - PUSH1_OPCODE as i32) - + 1 - < pos as i32) - }, - ) - } else { - false - }; - if has_prefix { - pos - 32 + // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call + let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( + "validate_jumpdest_table_end", + "terminate_common", + self, + )? + else { + return Ok(()); + }; + + // Return to the state before starting the simulation + self.rollback(checkpoint); + self.memory = memory; + + // Find proofs for all context + self.set_proofs_and_jumpdests(jumpdest_table); + + Ok(()) + } + + pub(crate) fn set_proofs_and_jumpdests( + &mut self, + jumpdest_table: HashMap>, + ) { + self.jumpdest_proofs = Some(HashMap::from_iter(jumpdest_table.into_iter().map( + |(ctx, jumpdest_table)| { + let code = self.get_code(ctx).unwrap(); + if let Some(&largest_address) = jumpdest_table.last() { + let proofs = get_proofs_and_jumpdests(&code, largest_address, jumpdest_table); + (ctx, proofs) } else { - acc + (ctx, vec![]) } }, - ); - Ok(proof.into()) + ))); } -} -impl GenerationState { - fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { - const JUMPDEST_OPCODE: u8 = 0x5b; - let mut state = self.soft_clone(); - let code_len = u256_to_usize(self.memory.get(MemoryAddress { - context: self.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::CodeSize as usize, - }))?; - // Generate the jumpdest table + fn get_current_code(&self) -> Result, ProgramError> { + self.get_code(self.registers.context) + } + + fn get_code(&self, context: usize) -> Result, ProgramError> { + let code_len = self.get_code_len()?; let code = (0..code_len) .map(|i| { u256_to_u8(self.memory.get(MemoryAddress { @@ -346,16 +356,25 @@ impl GenerationState { })) }) .collect::, _>>()?; + Ok(code) + } - // We need to set the simulated jumpdest bits to one as otherwise - // the simulation will fail. - let mut jumpdest_table = Vec::with_capacity(code.len()); + fn get_code_len(&self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + Ok(code_len) + } + + fn set_jumpdest_bits<'a>(&mut self, code: &'a Vec) { + const JUMPDEST_OPCODE: u8 = 0x5b; for (pos, opcode) in CodeIterator::new(&code) { - jumpdest_table.push((pos, opcode == JUMPDEST_OPCODE)); if opcode == JUMPDEST_OPCODE { - state.memory.set( + self.memory.set( MemoryAddress { - context: state.registers.context, + context: self.registers.context, segment: Segment::JumpdestBits as usize, virt: pos, }, @@ -363,18 +382,50 @@ impl GenerationState { ); } } - - // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", - "terminate_common", - &mut state, - )?; - self.jumpdest_addresses = state.jumpdest_addresses; - Ok(()) } } +/// For each address in `jumpdest_table` it search a proof, that is the closest address +/// for which none of the previous 32 bytes in the code (including opcodes +/// and pushed bytes are PUSHXX and the address is in its range. It returns +/// a vector of even size containing proofs followed by their addresses +fn get_proofs_and_jumpdests<'a>( + code: &'a Vec, + largest_address: usize, + jumpdest_table: std::collections::BTreeSet, +) -> Vec { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x7f; + let (proofs, _) = CodeIterator::until(&code, largest_address + 1).fold( + (vec![], 0), + |(mut proofs, acc), (pos, opcode)| { + let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { + code[prefix_start..pos] + .iter() + .enumerate() + .fold(true, |acc, (prefix_pos, &byte)| { + acc && (byte > PUSH32_OPCODE + || (prefix_start + prefix_pos) as i32 + + (byte as i32 - PUSH1_OPCODE as i32) + + 1 + < pos as i32) + }) + } else { + false + }; + let acc = if has_prefix { pos - 32 } else { acc }; + if jumpdest_table.contains(&pos) { + // Push the proof + proofs.push(acc); + // Push the address + proofs.push(pos); + } + (proofs, acc) + }, + ); + proofs +} + struct CodeIterator<'a> { code: &'a [u8], pos: usize, diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 1c50cc294c..cc1df0919d 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -51,8 +51,7 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, - pub(crate) last_jumpdest_address: usize, - pub(crate) jumpdest_addresses: Option>>, + pub(crate) jumpdest_proofs: Option>>, } impl GenerationState { @@ -94,8 +93,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - last_jumpdest_address: 0, - jumpdest_addresses: None, + jumpdest_proofs: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -189,8 +187,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - last_jumpdest_address: 0, - jumpdest_addresses: None, + jumpdest_proofs: None, } } } From 11d668f5e6ceabd3ef10d93123a033a320a347b0 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 20 Dec 2023 14:13:36 +0100 Subject: [PATCH 049/175] Remove aborts for invalid jumps --- evm/src/cpu/kernel/asm/core/call.asm | 2 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 74 +++++++++---------- evm/src/cpu/kernel/asm/util/basic_macros.asm | 13 ++++ .../kernel/tests/core/jumpdest_analysis.rs | 6 +- evm/src/generation/mod.rs | 16 +++- evm/src/generation/prover_input.rs | 5 +- evm/src/witness/transition.rs | 7 +- 7 files changed, 72 insertions(+), 51 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 5173d35822..fcb4eb32f9 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -369,7 +369,7 @@ call_too_deep: // Perform jumpdest analyis GET_CONTEXT // stack: ctx, code_size, retdest - %validate_jumpdest_table + %jumpdest_analisys PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index cfc3575b14..97224b3ebc 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -1,17 +1,14 @@ // Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], -// for the given context's code. Panics if we never hit final_pos +// for the given context's code. // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) -global verify_path: +global verify_path_and_write_table: loop: // stack: i, ctx, final_pos, retdest - // Ideally we would break if i >= final_pos, but checking i > final_pos is - // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is - // a no-op. DUP3 DUP2 EQ // i == final_pos - %jumpi(return) + %jumpi(proof_ok) DUP3 DUP2 GT // i > final_pos - %jumpi(panic) + %jumpi(proof_not_ok) // stack: i, ctx, final_pos, retdest %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) @@ -22,24 +19,29 @@ loop: // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest + // stack: opcode != JUMPDEST, opcode, i, ctx, final_pos, retdest %jumpi(continue) - // stack: JUMPDEST, i, ctx, code_len, retdest + // stack: JUMPDEST, i, ctx, final_pos, retdest %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) MSTORE_GENERAL continue: - // stack: opcode, i, ctx, code_len, retdest + // stack: opcode, i, ctx, final_pos, retdest %add_const(code_bytes_to_skip) %mload_kernel_code - // stack: bytes_to_skip, i, ctx, code_len, retdest + // stack: bytes_to_skip, i, ctx, final_pos, retdest ADD - // stack: i, ctx, code_len, retdest + // stack: i, ctx, final_pos, retdest %jump(loop) -return: - // stack: i, ctx, code_len, retdest +proof_ok: + // stack: i, ctx, final_pos, retdest + // We already know final pos is a jumpdest + %stack (i, ctx, final_pos) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i) + MSTORE_GENERAL + JUMP +proof_not_ok: %pop3 JUMP @@ -101,26 +103,21 @@ code_bytes_to_skip: // - code[jumpdest] = 0x5b. // stack: proof_prefix_addr, jumpdest, ctx, retdest // stack: (empty) abort if jumpdest is not a valid destination -global is_jumpdest: +global write_table_if_jumpdest: // stack: proof_prefix_addr, jumpdest, ctx, retdest - //%stack - // (proof_prefix_addr, jumpdest, ctx) -> - // (ctx, @SEGMENT_JUMPDEST_BITS, jumpdest, proof_prefix_addr, jumpdest, ctx) - //MLOAD_GENERAL - //%jumpi(return_is_jumpdest) %stack (proof_prefix_addr, jumpdest, ctx) -> (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - %assert_eq_const(0x5b) + %jump_eq_const(0x5b, return) //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest ISZERO - %jumpi(verify_path) + %jumpi(verify_path_and_write_table) // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, @@ -135,9 +132,8 @@ global is_jumpdest: %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) // check the remaining path - %jump(verify_path) - -return_is_jumpdest: + %jump(verify_path_and_write_table) +return: // stack: proof_prefix_addr, jumpdest, ctx, retdest %pop3 JUMP @@ -154,7 +150,7 @@ return_is_jumpdest: DUP1 %gt_const(127) %jumpi(%%ok) - %assert_lt_const($max) + %jumpi_lt_const($max, return) // stack: proof_prefix_addr, ctx, jumpdest PUSH 0 // We need something to pop %%ok: @@ -162,13 +158,13 @@ return_is_jumpdest: %increment %endmacro -%macro is_jumpdest +%macro write_table_if_jumpdest %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) - %jump(is_jumpdest) + %jump(write_table_if_jumpdest) %%after: %endmacro -// Check if the jumpdest table is correct. This is done by +// Write the jumpdest table. This is done by // non-deterministically guessing the sequence of jumpdest // addresses used during program execution within the current context. // For each jumpdest address we also non-deterministically guess @@ -179,7 +175,7 @@ return_is_jumpdest: // // stack: ctx, retdest // stack: (empty) -global validate_jumpdest_table: +global jumpdest_analisys: // If address > 0 then address is interpreted as address' + 1 // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) @@ -189,24 +185,22 @@ global validate_jumpdest_table: // This is just a hook used for avoiding verification of the jumpdest // table in another contexts. It is useful during proof generation, // allowing the avoidance of table verification when simulating user code. -global validate_jumpdest_table_end: +global jumpdest_analisys_end: POP JUMP check_proof: %decrement - DUP2 DUP2 - // stack: address, ctx, address, ctx + DUP2 SWAP1 + // stack: address, ctx, ctx // We read the proof PROVER_INPUT(jumpdest_table::next_proof) - // stack: proof, address, ctx, address, ctx - %is_jumpdest - %stack (address, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, address, ctx) - MSTORE_GENERAL + // stack: proof, address, ctx, ctx + %write_table_if_jumpdest - %jump(validate_jumpdest_table) + %jump(jumpdest_analisys) -%macro validate_jumpdest_table +%macro jumpdest_analisys %stack (ctx) -> (ctx, %%after) - %jump(validate_jumpdest_table) + %jump(jumpdest_analisys) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index fc2472b3b8..d62dc27ec7 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -8,6 +8,19 @@ jumpi %endmacro +%macro jump_eq_const(c, jumpdest) + PUSH $c + SUB + %jumpi($jumpdest) +%endmacro + +%macro jumpi_lt_const(c, jumpdest) + // %assert_zero is cheaper than %assert_nonzero, so we will leverage the + // fact that (x < c) == !(x >= c). + %ge_const($c) + %jumpi($jumpdest) +%endmacro + %macro pop2 %rep 2 POP diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 58e9f936b5..3d97251cd2 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -5,8 +5,8 @@ use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; #[test] -fn test_validate_jumpdest_table() -> Result<()> { - let validate_jumpdest_table = KERNEL.global_labels["validate_jumpdest_table"]; +fn test_jumpdest_analisys() -> Result<()> { + let jumpdest_analisys = KERNEL.global_labels["jumpdest_analisys"]; const CONTEXT: usize = 3; // arbitrary let add = get_opcode("ADD"); @@ -29,7 +29,7 @@ fn test_validate_jumpdest_table() -> Result<()> { // Contract creation transaction. let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; - let mut interpreter = Interpreter::new_with_kernel(validate_jumpdest_table, initial_stack); + let mut interpreter = Interpreter::new_with_kernel(jumpdest_analisys, initial_stack); interpreter.set_code(CONTEXT, code); interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 81bacb75fa..72a0dca973 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -351,15 +351,15 @@ fn simulate_cpu_between_labels_and_get_user_jumps( let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); state.registers.program_counter = KERNEL.global_labels[initial_label]; + let initial_clock = state.traces.clock(); let initial_context = state.registers.context; log::debug!("Simulating CPU for jumpdest analysis."); loop { // skip jumdest table validations in simulations - if state.registers.program_counter == KERNEL.global_labels["validate_jumpdest_table"] { - state.registers.program_counter = - KERNEL.global_labels["validate_jumpdest_table_end"] + if state.registers.program_counter == KERNEL.global_labels["jumpdest_analisys"] { + state.registers.program_counter = KERNEL.global_labels["jumpdest_analisys_end"] } let pc = state.registers.program_counter; let context = state.registers.context; @@ -389,6 +389,11 @@ fn simulate_cpu_between_labels_and_get_user_jumps( }, U256::one(), ); + let jumpdest_opcode = state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: jumpdest, + }); if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { ctx_addresses.insert(jumpdest); } else { @@ -396,7 +401,10 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } } if halt { - log::debug!("Simulated CPU halted after {} cycles", state.traces.clock()); + log::debug!( + "Simulated CPU halted after {} cycles", + state.traces.clock() - initial_clock + ); return Ok(Some(jumpdest_addresses)); } transition(state).map_err(|_| { diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index a5f73ae687..ca9208ea4a 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -306,7 +306,7 @@ impl GenerationState { // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( - "validate_jumpdest_table_end", + "jumpdest_analisys_end", "terminate_common", self, )? @@ -385,7 +385,8 @@ impl GenerationState { } } -/// For each address in `jumpdest_table` it search a proof, that is the closest address +/// For each address in `jumpdest_table`, each bounded by larges_address, +/// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes /// and pushed bytes are PUSHXX and the address is in its range. It returns /// a vector of even size containing proofs followed by their addresses diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..b8f962e74f 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,12 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?}", op); + log::debug!( + "User instruction: {:?}, ctx = {:?}, stack = {:?}", + op, + state.registers.context, + state.stack() + ); } fill_op_flag(op, &mut row); From 3e78865d644218836768a811dce902cc43b26a6a Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 13 Dec 2023 17:33:53 +0100 Subject: [PATCH 050/175] Remove aborts for invalid jumps and Rebase --- evm/src/cpu/kernel/asm/core/call.asm | 5 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 159 ++++++++++-- evm/src/cpu/kernel/asm/util/basic_macros.asm | 13 + evm/src/cpu/kernel/interpreter.rs | 21 +- .../kernel/tests/core/jumpdest_analysis.rs | 16 +- evm/src/generation/mod.rs | 92 ++++++- evm/src/generation/prover_input.rs | 244 +++++++++++++++++- evm/src/generation/state.rs | 25 +- evm/src/util.rs | 5 + evm/src/witness/errors.rs | 2 + evm/src/witness/transition.rs | 7 +- 11 files changed, 542 insertions(+), 47 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 2e7d1d7345..fcb4eb32f9 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -367,12 +367,9 @@ call_too_deep: %checkpoint // Checkpoint %increment_call_depth // Perform jumpdest analyis - PUSH %%after - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) GET_CONTEXT // stack: ctx, code_size, retdest - %jump(jumpdest_analysis) -%%after: + %jumpdest_analisys PUSH 0 // jump dest EXIT_KERNEL // (Old context) stack: new_ctx diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e63..97224b3ebc 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -1,45 +1,47 @@ -// Populates @SEGMENT_JUMPDEST_BITS for the given context's code. -// Pre stack: ctx, code_len, retdest +// Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], +// for the given context's code. +// Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) -global jumpdest_analysis: - // stack: ctx, code_len, retdest - PUSH 0 // i = 0 - +global verify_path_and_write_table: loop: - // stack: i, ctx, code_len, retdest - // Ideally we would break if i >= code_len, but checking i > code_len is - // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is - // a no-op. - DUP3 DUP2 GT // i > code_len - %jumpi(return) - - // stack: i, ctx, code_len, retdest + // stack: i, ctx, final_pos, retdest + DUP3 DUP2 EQ // i == final_pos + %jumpi(proof_ok) + DUP3 DUP2 GT // i > final_pos + %jumpi(proof_not_ok) + + // stack: i, ctx, final_pos, retdest %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) MLOAD_GENERAL - // stack: opcode, i, ctx, code_len, retdest + // stack: opcode, i, ctx, final_pos, retdest DUP1 // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, code_len, retdest + // stack: opcode != JUMPDEST, opcode, i, ctx, final_pos, retdest %jumpi(continue) - // stack: JUMPDEST, i, ctx, code_len, retdest + // stack: JUMPDEST, i, ctx, final_pos, retdest %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) MSTORE_GENERAL continue: - // stack: opcode, i, ctx, code_len, retdest + // stack: opcode, i, ctx, final_pos, retdest %add_const(code_bytes_to_skip) %mload_kernel_code - // stack: bytes_to_skip, i, ctx, code_len, retdest + // stack: bytes_to_skip, i, ctx, final_pos, retdest ADD - // stack: i, ctx, code_len, retdest + // stack: i, ctx, final_pos, retdest %jump(loop) -return: - // stack: i, ctx, code_len, retdest +proof_ok: + // stack: i, ctx, final_pos, retdest + // We already know final pos is a jumpdest + %stack (i, ctx, final_pos) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i) + MSTORE_GENERAL + JUMP +proof_not_ok: %pop3 JUMP @@ -89,3 +91,116 @@ code_bytes_to_skip: %rep 128 BYTES 1 // 0x80-0xff %endrep + + +// A proof attesting that jumpdest is a valid jump destinations is +// either 0 or an index 0 < i <= jumpdest - 32. +// A proof is valid if: +// - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b +// - i > 0 and: +// - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, +// - we can go from opcode i+32 to jumpdest, +// - code[jumpdest] = 0x5b. +// stack: proof_prefix_addr, jumpdest, ctx, retdest +// stack: (empty) abort if jumpdest is not a valid destination +global write_table_if_jumpdest: + // stack: proof_prefix_addr, jumpdest, ctx, retdest + %stack + (proof_prefix_addr, jumpdest, ctx) -> + (ctx, @SEGMENT_CODE, jumpdest, jumpdest, ctx, proof_prefix_addr) + MLOAD_GENERAL + // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest + + %jump_eq_const(0x5b, return) + + //stack: jumpdest, ctx, proof_prefix_addr, retdest + SWAP2 DUP1 + // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest + ISZERO + %jumpi(verify_path_and_write_table) + // stack: proof_prefix_addr, ctx, jumpdest, retdest + // If we are here we need to check that the next 32 bytes are less + // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, + // or larger than 127 + %check_and_step(127) %check_and_step(126) %check_and_step(125) %check_and_step(124) + %check_and_step(123) %check_and_step(122) %check_and_step(121) %check_and_step(120) + %check_and_step(119) %check_and_step(118) %check_and_step(117) %check_and_step(116) + %check_and_step(115) %check_and_step(114) %check_and_step(113) %check_and_step(112) + %check_and_step(111) %check_and_step(110) %check_and_step(109) %check_and_step(108) + %check_and_step(107) %check_and_step(106) %check_and_step(105) %check_and_step(104) + %check_and_step(103) %check_and_step(102) %check_and_step(101) %check_and_step(100) + %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) + + // check the remaining path + %jump(verify_path_and_write_table) +return: + // stack: proof_prefix_addr, jumpdest, ctx, retdest + %pop3 + JUMP + + +// Chek if the opcode pointed by proof_prefix address is +// less than max and increment proof_prefix_addr +%macro check_and_step(max) + %stack + (proof_prefix_addr, ctx, jumpdest) -> + (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) + MLOAD_GENERAL + // stack: opcode, ctx, proof_prefix_addr, jumpdest + DUP1 + %gt_const(127) + %jumpi(%%ok) + %jumpi_lt_const($max, return) + // stack: proof_prefix_addr, ctx, jumpdest + PUSH 0 // We need something to pop +%%ok: + POP + %increment +%endmacro + +%macro write_table_if_jumpdest + %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) + %jump(write_table_if_jumpdest) +%%after: +%endmacro + +// Write the jumpdest table. This is done by +// non-deterministically guessing the sequence of jumpdest +// addresses used during program execution within the current context. +// For each jumpdest address we also non-deterministically guess +// a proof, which is another address in the code such that +// is_jumpdest don't abort, when the proof is at the top of the stack +// an the jumpdest address below. If that's the case we set the +// corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. +// +// stack: ctx, retdest +// stack: (empty) +global jumpdest_analisys: + // If address > 0 then address is interpreted as address' + 1 + // and the next prover input should contain a proof for address'. + PROVER_INPUT(jumpdest_table::next_address) + DUP1 %jumpi(check_proof) + // If proof == 0 there are no more jump destinations to check + POP +// This is just a hook used for avoiding verification of the jumpdest +// table in another contexts. It is useful during proof generation, +// allowing the avoidance of table verification when simulating user code. +global jumpdest_analisys_end: + POP + JUMP +check_proof: + %decrement + DUP2 SWAP1 + // stack: address, ctx, ctx + // We read the proof + PROVER_INPUT(jumpdest_table::next_proof) + // stack: proof, address, ctx, ctx + %write_table_if_jumpdest + + %jump(jumpdest_analisys) + +%macro jumpdest_analisys + %stack (ctx) -> (ctx, %%after) + %jump(jumpdest_analisys) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index fc2472b3b8..d62dc27ec7 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -8,6 +8,19 @@ jumpi %endmacro +%macro jump_eq_const(c, jumpdest) + PUSH $c + SUB + %jumpi($jumpdest) +%endmacro + +%macro jumpi_lt_const(c, jumpdest) + // %assert_zero is cheaper than %assert_nonzero, so we will leverage the + // fact that (x < c) == !(x >= c). + %ge_const($c) + %jumpi($jumpdest) +%endmacro + %macro pop2 %rep 2 POP diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c437672113..30f862cdd2 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1,7 +1,7 @@ //! An EVM interpreter for testing and debugging purposes. use core::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::ops::Range; use anyhow::bail; @@ -10,6 +10,7 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use super::assembler::BYTES_PER_OFFSET; +use super::utils::u256_from_bool; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -413,7 +414,23 @@ impl<'a> Interpreter<'a> { .collect() } - fn incr(&mut self, n: usize) { + pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] + .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); + self.generation_state + .set_proofs_and_jumpdests(HashMap::from([( + context, + BTreeSet::from_iter( + jumpdest_bits + .into_iter() + .enumerate() + .filter(|&(_, x)| x) + .map(|(i, _)| i), + ), + )])); + } + + pub(crate) fn incr(&mut self, n: usize) { self.generation_state.registers.program_counter += n; } diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 022a18d729..3d97251cd2 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -5,8 +5,8 @@ use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; #[test] -fn test_jumpdest_analysis() -> Result<()> { - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; +fn test_jumpdest_analisys() -> Result<()> { + let jumpdest_analisys = KERNEL.global_labels["jumpdest_analisys"]; const CONTEXT: usize = 3; // arbitrary let add = get_opcode("ADD"); @@ -25,18 +25,16 @@ fn test_jumpdest_analysis() -> Result<()> { jumpdest, ]; - let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; // Contract creation transaction. - let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()]; - let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); + let initial_stack = vec![0xDEADBEEFu32.into(), CONTEXT.into()]; + let mut interpreter = Interpreter::new_with_kernel(jumpdest_analisys, initial_stack); interpreter.set_code(CONTEXT, code); + interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + interpreter.run()?; assert_eq!(interpreter.stack(), vec![]); - assert_eq!( - interpreter.get_jumpdest_bits(CONTEXT), - expected_jumpdest_bits - ); Ok(()) } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d691d34e61..4aa35afb4b 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -8,6 +8,7 @@ use ethereum_types::{Address, BigEndianHash, H256, U256}; use itertools::enumerate; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -21,13 +22,16 @@ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::opcodes::get_opcode; use crate::generation::state::GenerationState; use crate::generation::trie_extractor::{get_receipt_trie, get_state_trie, get_txn_trie}; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; use crate::prover::check_abort_signal; -use crate::util::{h2u, u256_to_usize}; +use crate::util::{h2u, u256_to_u8, u256_to_usize}; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -38,7 +42,7 @@ pub(crate) mod state; mod trie_extractor; use self::mpt::{load_all_mpts, TrieRootPtrs}; -use crate::witness::util::mem_write_log; +use crate::witness::util::{mem_write_log, stack_peek}; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] @@ -296,9 +300,7 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn simulate_cpu, const D: usize>( - state: &mut GenerationState, -) -> anyhow::Result<()> { +fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { let halt_pc = KERNEL.global_labels["halt"]; loop { @@ -333,3 +335,81 @@ fn simulate_cpu, const D: usize>( transition(state)?; } } + +fn simulate_cpu_between_labels_and_get_user_jumps( + initial_label: &str, + final_label: &str, + state: &mut GenerationState, +) -> Result>>, ProgramError> { + if state.jumpdest_proofs.is_some() { + Ok(None) + } else { + const JUMP_OPCODE: u8 = 0x56; + const JUMPI_OPCODE: u8 = 0x57; + + let halt_pc = KERNEL.global_labels[final_label]; + let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); + + state.registers.program_counter = KERNEL.global_labels[initial_label]; + let initial_clock = state.traces.clock(); + let initial_context = state.registers.context; + + log::debug!("Simulating CPU for jumpdest analysis."); + + loop { + // skip jumdest table validations in simulations + if state.registers.program_counter == KERNEL.global_labels["jumpdest_analisys"] { + state.registers.program_counter = KERNEL.global_labels["jumpdest_analisys_end"] + } + let pc = state.registers.program_counter; + let context = state.registers.context; + let halt = state.registers.is_kernel + && pc == halt_pc + && state.registers.context == initial_context; + let opcode = u256_to_u8(state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: state.registers.program_counter, + }))?; + let cond = if let Ok(cond) = stack_peek(state, 1) { + cond != U256::zero() + } else { + false + }; + if !state.registers.is_kernel + && (opcode == JUMP_OPCODE || (opcode == JUMPI_OPCODE && cond)) + { + // Avoid deeper calls to abort + let jumpdest = u256_to_usize(state.registers.stack_top)?; + state.memory.set( + MemoryAddress { + context, + segment: Segment::JumpdestBits as usize, + virt: jumpdest, + }, + U256::one(), + ); + let jumpdest_opcode = state.memory.get(MemoryAddress { + context, + segment: Segment::Code as usize, + virt: jumpdest, + }); + if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { + ctx_addresses.insert(jumpdest); + } else { + jumpdest_addresses.insert(context, BTreeSet::from([jumpdest])); + } + } + if halt { + log::debug!( + "Simulated CPU halted after {} cycles", + state.traces.clock() - initial_clock + ); + return Ok(Some(jumpdest_addresses)); + } + transition(state).map_err(|_| { + ProgramError::ProverInputError(ProverInputError::InvalidJumpdestSimulation) + })?; + } + } +} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b2a8f0cea0..ca9208ea4a 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,3 +1,5 @@ +use std::cmp::min; +use std::collections::HashMap; use std::mem::transmute; use std::str::FromStr; @@ -5,20 +7,26 @@ use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; use itertools::{enumerate, Itertools}; use num_bigint::BigUint; +use plonky2::field::extension::Extendable; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; +use crate::generation::simulate_cpu_between_labels_and_get_user_jumps; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::memory::segments::Segment::BnPairing; -use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; -use crate::witness::errors::ProgramError; +use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_u8, u256_to_usize}; use crate::witness::errors::ProverInputError::*; +use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::MemoryAddress; use crate::witness::util::{current_context_peek, stack_peek}; @@ -47,6 +55,7 @@ impl GenerationState { "bignum_modmul" => self.run_bignum_modmul(), "withdrawal" => self.run_withdrawal(), "num_bits" => self.run_num_bits(), + "jumpdest_table" => self.run_jumpdest_table(input_fn), _ => Err(ProgramError::ProverInputError(InvalidFunction)), } } @@ -229,6 +238,237 @@ impl GenerationState { Ok(num_bits.into()) } } + + fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { + match input_fn.0[1].as_str() { + "next_address" => self.run_next_jumpdest_table_address(), + "next_proof" => self.run_next_jumpdest_table_proof(), + _ => Err(ProgramError::ProverInputError(InvalidInput)), + } + } + /// Return the next used jump addres + fn run_next_jumpdest_table_address(&mut self) -> Result { + let context = self.registers.context; + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + + if self.jumpdest_proofs.is_none() { + self.generate_jumpdest_proofs()?; + } + + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); + }; + + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_address) = ctx_jumpdest_proofs.pop() + { + Ok((next_jumpdest_address + 1).into()) + } else { + self.jumpdest_proofs = None; + Ok(U256::zero()) + } + } + + /// Returns the proof for the last jump address. + fn run_next_jumpdest_table_proof(&mut self) -> Result { + let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + return Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )); + }; + if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) + && let Some(next_jumpdest_proof) = ctx_jumpdest_proofs.pop() + { + Ok(next_jumpdest_proof.into()) + } else { + Err(ProgramError::ProverInputError( + ProverInputError::InvalidJumpdestSimulation, + )) + } + } +} + +impl GenerationState { + fn generate_jumpdest_proofs(&mut self) -> Result<(), ProgramError> { + let checkpoint = self.checkpoint(); + let memory = self.memory.clone(); + + let code = self.get_current_code()?; + // We need to set the simulated jumpdest bits to one as otherwise + // the simulation will fail. + self.set_jumpdest_bits(&code); + + // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call + let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( + "jumpdest_analisys_end", + "terminate_common", + self, + )? + else { + return Ok(()); + }; + + // Return to the state before starting the simulation + self.rollback(checkpoint); + self.memory = memory; + + // Find proofs for all context + self.set_proofs_and_jumpdests(jumpdest_table); + + Ok(()) + } + + pub(crate) fn set_proofs_and_jumpdests( + &mut self, + jumpdest_table: HashMap>, + ) { + self.jumpdest_proofs = Some(HashMap::from_iter(jumpdest_table.into_iter().map( + |(ctx, jumpdest_table)| { + let code = self.get_code(ctx).unwrap(); + if let Some(&largest_address) = jumpdest_table.last() { + let proofs = get_proofs_and_jumpdests(&code, largest_address, jumpdest_table); + (ctx, proofs) + } else { + (ctx, vec![]) + } + }, + ))); + } + + fn get_current_code(&self) -> Result, ProgramError> { + self.get_code(self.registers.context) + } + + fn get_code(&self, context: usize) -> Result, ProgramError> { + let code_len = self.get_code_len()?; + let code = (0..code_len) + .map(|i| { + u256_to_u8(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::Code as usize, + virt: i, + })) + }) + .collect::, _>>()?; + Ok(code) + } + + fn get_code_len(&self) -> Result { + let code_len = u256_to_usize(self.memory.get(MemoryAddress { + context: self.registers.context, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::CodeSize as usize, + }))?; + Ok(code_len) + } + + fn set_jumpdest_bits<'a>(&mut self, code: &'a Vec) { + const JUMPDEST_OPCODE: u8 = 0x5b; + for (pos, opcode) in CodeIterator::new(&code) { + if opcode == JUMPDEST_OPCODE { + self.memory.set( + MemoryAddress { + context: self.registers.context, + segment: Segment::JumpdestBits as usize, + virt: pos, + }, + U256::one(), + ); + } + } + } +} + +/// For each address in `jumpdest_table`, each bounded by larges_address, +/// this function searches for a proof. A proof is the closest address +/// for which none of the previous 32 bytes in the code (including opcodes +/// and pushed bytes are PUSHXX and the address is in its range. It returns +/// a vector of even size containing proofs followed by their addresses +fn get_proofs_and_jumpdests<'a>( + code: &'a Vec, + largest_address: usize, + jumpdest_table: std::collections::BTreeSet, +) -> Vec { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x7f; + let (proofs, _) = CodeIterator::until(&code, largest_address + 1).fold( + (vec![], 0), + |(mut proofs, acc), (pos, opcode)| { + let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { + code[prefix_start..pos] + .iter() + .enumerate() + .fold(true, |acc, (prefix_pos, &byte)| { + acc && (byte > PUSH32_OPCODE + || (prefix_start + prefix_pos) as i32 + + (byte as i32 - PUSH1_OPCODE as i32) + + 1 + < pos as i32) + }) + } else { + false + }; + let acc = if has_prefix { pos - 32 } else { acc }; + if jumpdest_table.contains(&pos) { + // Push the proof + proofs.push(acc); + // Push the address + proofs.push(pos); + } + (proofs, acc) + }, + ); + proofs +} + +struct CodeIterator<'a> { + code: &'a [u8], + pos: usize, + end: usize, +} + +impl<'a> CodeIterator<'a> { + fn new(code: &'a [u8]) -> Self { + CodeIterator { + end: code.len(), + code, + pos: 0, + } + } + fn until(code: &'a [u8], end: usize) -> Self { + CodeIterator { + end: std::cmp::min(code.len(), end), + code, + pos: 0, + } + } +} + +impl<'a> Iterator for CodeIterator<'a> { + type Item = (usize, u8); + + fn next(&mut self) -> Option { + const PUSH1_OPCODE: u8 = 0x60; + const PUSH32_OPCODE: u8 = 0x70; + let CodeIterator { code, pos, end } = self; + if *pos >= *end { + return None; + } + let opcode = code[*pos]; + let old_pos = *pos; + *pos += if (PUSH1_OPCODE..=PUSH32_OPCODE).contains(&opcode) { + (opcode - PUSH1_OPCODE + 2).into() + } else { + 1 + }; + Some((old_pos, opcode)) + } } enum EvmField { diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 89ff0c5af9..cc1df0919d 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; @@ -50,6 +50,8 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, + + pub(crate) jumpdest_proofs: Option>>, } impl GenerationState { @@ -91,6 +93,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, + jumpdest_proofs: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -167,6 +170,26 @@ impl GenerationState { .map(|i| stack_peek(self, i).unwrap()) .collect() } + + /// Clone everything but the traces + pub(crate) fn soft_clone(&self) -> GenerationState { + Self { + inputs: self.inputs.clone(), + registers: self.registers, + memory: self.memory.clone(), + traces: Traces::default(), + rlp_prover_inputs: self.rlp_prover_inputs.clone(), + state_key_to_address: self.state_key_to_address.clone(), + bignum_modmul_result_limbs: self.bignum_modmul_result_limbs.clone(), + withdrawal_prover_inputs: self.withdrawal_prover_inputs.clone(), + trie_root_ptrs: TrieRootPtrs { + state_root_ptr: 0, + txn_root_ptr: 0, + receipt_root_ptr: 0, + }, + jumpdest_proofs: None, + } + } } /// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. diff --git a/evm/src/util.rs b/evm/src/util.rs index 3d9564b5ed..fa19ef0998 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,6 +70,11 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } +/// Safe conversion from U256 to u8, which errors in case of overflow instead of panicking. +pub(crate) fn u256_to_u8(u256: U256) -> Result { + u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) +} + /// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 5a0fcbfb32..1b266aefde 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -36,4 +36,6 @@ pub enum ProverInputError { InvalidInput, InvalidFunction, NumBitsError, + InvalidJumpDestination, + InvalidJumpdestSimulation, } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..b8f962e74f 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -395,7 +395,12 @@ fn try_perform_instruction( if state.registers.is_kernel { log_kernel_instruction(state, op); } else { - log::debug!("User instruction: {:?}", op); + log::debug!( + "User instruction: {:?}, ctx = {:?}, stack = {:?}", + op, + state.registers.context, + state.stack() + ); } fill_op_flag(op, &mut row); From 24ae0d9de09a380fc2b981090f309acda0f27eed Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 20 Dec 2023 15:27:27 +0100 Subject: [PATCH 051/175] Clippy --- evm/src/generation/prover_input.rs | 10 +++++----- evm/src/util.rs | 7 +------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index ca9208ea4a..21b7c5725f 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -368,9 +368,9 @@ impl GenerationState { Ok(code_len) } - fn set_jumpdest_bits<'a>(&mut self, code: &'a Vec) { + fn set_jumpdest_bits(&mut self, code: &[u8]) { const JUMPDEST_OPCODE: u8 = 0x5b; - for (pos, opcode) in CodeIterator::new(&code) { + for (pos, opcode) in CodeIterator::new(code) { if opcode == JUMPDEST_OPCODE { self.memory.set( MemoryAddress { @@ -390,14 +390,14 @@ impl GenerationState { /// for which none of the previous 32 bytes in the code (including opcodes /// and pushed bytes are PUSHXX and the address is in its range. It returns /// a vector of even size containing proofs followed by their addresses -fn get_proofs_and_jumpdests<'a>( - code: &'a Vec, +fn get_proofs_and_jumpdests( + code: &[u8], largest_address: usize, jumpdest_table: std::collections::BTreeSet, ) -> Vec { const PUSH1_OPCODE: u8 = 0x60; const PUSH32_OPCODE: u8 = 0x7f; - let (proofs, _) = CodeIterator::until(&code, largest_address + 1).fold( + let (proofs, _) = CodeIterator::until(code, largest_address + 1).fold( (vec![], 0), |(mut proofs, acc), (pos, opcode)| { let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { diff --git a/evm/src/util.rs b/evm/src/util.rs index fa19ef0998..29be65dd0a 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,7 +70,7 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } -/// Safe conversion from U256 to u8, which errors in case of overflow instead of panicking. +/// Safe conversion from U256 to u8, which errors in case of overflow. pub(crate) fn u256_to_u8(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } @@ -80,11 +80,6 @@ pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } -/// Converts a `U256` to a `u8`, erroring in case of overlow instead of panicking. -pub(crate) fn u256_to_u8(u256: U256) -> Result { - u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) -} - /// Converts a `U256` to a `bool`, erroring in case of overlow instead of panicking. pub(crate) fn u256_to_bool(u256: U256) -> Result { if u256 == U256::zero() { From cb3f91a003497886f6da944aca1a0d1bfd1e4269 Mon Sep 17 00:00:00 2001 From: Icer Date: Thu, 21 Dec 2023 16:33:42 +0800 Subject: [PATCH 052/175] add Debug trait to PartitionWitness to enable trace information output (#1437) --- plonky2/src/iop/witness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index d5b8dd04da..cf74be512c 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -297,7 +297,7 @@ impl Witness for PartialWitness { /// `PartitionWitness` holds a disjoint-set forest of the targets respecting a circuit's copy constraints. /// The value of a target is defined to be the value of its root in the forest. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PartitionWitness<'a, F: Field> { pub values: Vec>, pub representative_map: &'a [usize], From dfcf276dab2470abebe915b102d735ccc34fbdb4 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 30 Nov 2023 13:27:15 +0100 Subject: [PATCH 053/175] Refactor encode_empty_node and encode_branch_node Clean code Not important Restore jumpdets_analysis.asm Refactor encode_empty_node and encode_branch_node --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 29 +- evm/src/cpu/kernel/asm/main.asm | 3 + evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 111 ++- evm/src/cpu/kernel/asm/mpt/util.asm | 10 + evm/src/generation/mod.rs | 776 +++++++++++++++++- 5 files changed, 904 insertions(+), 25 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e63..56d9e12621 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -4,8 +4,9 @@ global jumpdest_analysis: // stack: ctx, code_len, retdest PUSH 0 // i = 0 + %stack (i, ctx, code_len, retdest) -> (i, ctx, code_len, retdest, 0) // ctr -loop: +global loop: // stack: i, ctx, code_len, retdest // Ideally we would break if i >= code_len, but checking i > code_len is // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is @@ -13,6 +14,20 @@ loop: DUP3 DUP2 GT // i > code_len %jumpi(return) + %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, 32, i, ctx) + %mload_packing + // stack: packed_opcodes + DUP1 + PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 + AND +global debug_before_as_dad: + %jumpi(as_dad) +global debug_wuau: +as_dad: + POP +global debug_not_wuau: + + // stack: i, ctx, code_len, retdest %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) MLOAD_GENERAL @@ -28,8 +43,11 @@ loop: // stack: JUMPDEST, i, ctx, code_len, retdest %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) MSTORE_GENERAL + %stack (opcode, i, ctx, code_len, retdest, ctr) -> (ctr, opcode, i, ctx, code_len, retdest) + %increment + %stack (ctr, opcode, i, ctx, code_len, retdest) -> (opcode, i, ctx, code_len, retdest, ctr) -continue: +global continue: // stack: opcode, i, ctx, code_len, retdest %add_const(code_bytes_to_skip) %mload_kernel_code @@ -38,9 +56,12 @@ continue: // stack: i, ctx, code_len, retdest %jump(loop) -return: +global return: // stack: i, ctx, code_len, retdest %pop3 + SWAP1 +global debug_ctr: + POP JUMP // Determines how many bytes away is the next opcode, based on the opcode we read. @@ -48,7 +69,7 @@ return: // // Note that the range of PUSH opcodes is [0x60, 0x80). I.e. PUSH1 is 0x60 // and PUSH32 is 0x7f. -code_bytes_to_skip: +global code_bytes_to_skip: %rep 96 BYTES 1 // 0x00-0x5f %endrep diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index b495d49947..f6cd03bc8f 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -13,6 +13,9 @@ global main: // Initialise the shift table %shift_table_init + + // Encode constant nodes + %initialize_rlp_segment // Initialize the state, transaction and receipt trie root pointers. PROVER_INPUT(trie_ptr::state) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index df7e1d741d..3bbb84f38f 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -92,9 +92,9 @@ maybe_hash_node: KECCAK_GENERAL %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP -pack_small_rlp: - // stack: result_ptr, result_len, cur_len, retdest - %stack (result_ptr, result_len, cur_len) +global pack_small_rlp: + // stack: result_ptr, result_len, retdest + %stack (result_ptr, result_len) -> (0, @SEGMENT_RLP_RAW, result_ptr, result_len, after_packed_small_rlp, result_len, cur_len) %jump(mload_packing) @@ -105,10 +105,10 @@ after_packed_small_rlp: // RLP encode the given trie node, and return an (pointer, length) pair // indicating where the data lives within @SEGMENT_RLP_RAW. // -// Pre stack: node_type, node_ptr, encode_value, cur_len, retdest -// Post stack: result_ptr, result_len, cur_len -encode_node: - // stack: node_type, node_ptr, encode_value, cur_len, retdest +// Pre stack: node_type, node_ptr, encode_value, retdest +// Post stack: result_ptr, result_len +global encode_node: + // stack: node_type, node_ptr, encode_value, retdest // Increment node_ptr, so it points to the node payload instead of its type. SWAP1 %increment SWAP1 // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest @@ -123,20 +123,13 @@ encode_node: PANIC global encode_node_empty: - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - // Then length of `TrieData` is unchanged here. + // stack: node_type, node_payload_ptr, encode_value, retdest %pop3 - // stack: cur_len, retdest - // An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string. - // TODO: Write this byte just once to RLP memory, then we can always return (0, 1). - %alloc_rlp_block - // stack: rlp_pos, cur_len, retdest - PUSH 0x80 - // stack: 0x80, rlp_pos, cur_len, retdest - DUP2 - // stack: rlp_pos, 0x80, rlp_pos, cur_len, retdest - %mstore_rlp - %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, 1, cur_len) + PUSH 0x1000 + %mload_kernel(@SEGMENT_RLP_RAW) +global debug_it_should_be_128: + POP + %stack (retdest) -> (retdest, 0x1000, 1) JUMP global encode_node_branch: @@ -329,3 +322,81 @@ encode_node_leaf_after_encode_value: %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) JUMP + +global encode_node_branch_new: + // stack: node_type, node_payload_ptr, encode_value, retdest + POP + // stack: node_payload_ptr, encode_value, retdest + + //Allocate a block of RLP memory + %alloc_rlp_block DUP1 + // stack: rlp_pos, node_payload_ptr, encode_value, retdest + + // Call encode_or_hash_node on each child + %encode_child_new(0) %encode_child_new(1) %encode_child_new(2) %encode_child_new(3) + %encode_child_new(4) %encode_child_new(5) %encode_child_new(6) %encode_child_new(7) + %encode_child_new(8) %encode_child_new(9) %encode_child_new(10) %encode_child_new(11) + %encode_child_new(12) %encode_child_new(13) %encode_child_new(14) %encode_child_new(15) + + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, retdest + + %stack (rlp_pos, rlp_start, node_payload_ptr) + -> (node_payload_ptr, rlp_pos, rlp_start) + %add_const(16) + // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, retdest + %mload_trie_data + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + DUP1 %jumpi(encode_node_branch_with_value_new) + + // No value; append the empty string (0x80). + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start) + %mstore_rlp + // stack: rlp_pos', rlp_start, retdest + %increment + // stack: rlp_pos'', rlp_start, retdest + %jump(encode_node_branch_prepend_prefix_new) +encode_node_branch_with_value_new: + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + %stack (value_ptr, rlp_pos, rlp_start, encode_value) + -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix, rlp_start) + JUMP // call encode_value +encode_node_branch_prepend_prefix_new: + // stack: rlp_pos'', rlp_start, retdest + %prepend_rlp_list_prefix + // stack: rlp_prefix_start, rlp_len, retdest + %stack (rlp_prefix_start, rlp_len, retdest) + -> (retdest, rlp_prefix_start, rlp_len) + JUMP + + +// Part of the encode_node_branch function. Encodes the i'th child. +// Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in +// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. +%macro encode_child_new(i) + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + PUSH %%after_encode + DUP5 DUP5 + // stack: node_payload_ptr, encode_value, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + %add_const($i) %mload_trie_data + // stack: child_i_ptr, encode_value, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + %jump(encode_or_hash_node) +%%after_encode: + // stack: result, result_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. + SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) + // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. + // stack: result_len, result, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + PUSH 160 + DUP4 // rlp_pos + %mstore_rlp + SWAP2 %increment SWAP2 // rlp_pos += 1 +%%unpack: + %stack (result_len, result, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) + -> (rlp_pos, result, result_len, %%after_unpacking, + rlp_start, node_payload_ptr, encode_value, retdest) + %jump(mstore_unpacking_rlp) +%%after_unpacking: + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, retdest +%endmacro + diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index 80e5c6f7c5..b492a0d055 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -10,6 +10,16 @@ // stack: (empty) %endmacro +%macro initialize_rlp_segment + // Write the encoding of the empty node to address 0 leaving 9 bytes for a prefix + // TODO: Do we need a prefix? + PUSH 0x80 + PUSH 0x1000 + %mstore_rlp + PUSH 0x1000 // TODO: use 10? + %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) +%endmacro + %macro alloc_rlp_block // stack: (empty) %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d691d34e61..a3ba330244 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -296,14 +296,63 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn simulate_cpu, const D: usize>( +fn _simulate_cpu, const D: usize>( + state: &mut GenerationState, +) -> anyhow::Result<()> { + let halt_pc = KERNEL.global_labels["halt"]; + + loop { + // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. + let pc = state.registers.program_counter; + let halt = state.registers.is_kernel && pc == halt_pc; + if halt { + log::info!("CPU halted after {} cycles", state.traces.clock()); + + // Padding + let mut row = CpuColumnsView::::default(); + row.clock = F::from_canonical_usize(state.traces.clock()); + row.context = F::from_canonical_usize(state.registers.context); + row.program_counter = F::from_canonical_usize(pc); + row.is_kernel_mode = F::ONE; + row.gas = F::from_canonical_u64(state.registers.gas_used); + row.stack_len = F::from_canonical_usize(state.registers.stack_len); + + loop { + state.traces.push_cpu(row); + row.clock += F::ONE; + if state.traces.clock().is_power_of_two() { + break; + } + } + + log::info!("CPU trace padded to {} cycles", state.traces.clock()); + + return Ok(()); + } + + transition(state)?; + } +} + +fn __simulate_cpu, const D: usize>( state: &mut GenerationState, ) -> anyhow::Result<()> { + let mut profiling_map = HashMap::::new(); + let halt_pc = KERNEL.global_labels["halt"]; loop { // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. let pc = state.registers.program_counter; + if let Ok(idx) = KERNEL + .ordered_labels + .binary_search_by_key(&pc, |label| KERNEL.global_labels[label]) + { + profiling_map + .entry(KERNEL.ordered_labels[idx].clone()) + .and_modify(|counter| *counter += 1) + .or_insert(1); + } let halt = state.registers.is_kernel && pc == halt_pc; if halt { log::info!("CPU halted after {} cycles", state.traces.clock()); @@ -324,12 +373,737 @@ fn simulate_cpu, const D: usize>( break; } } + log::info!("CPU trace padded to {} cycles", state.traces.clock()); + + let mut sorted_labels: Vec<_> = profiling_map.iter().collect(); + sorted_labels.sort_unstable_by_key(|item| item.1); + sorted_labels.reverse(); + log::info!("Offsets: {:?}", sorted_labels); + return Ok(()); + } + + transition(state)?; + } +} + +fn simulate_cpu, const D: usize>( + state: &mut GenerationState, +) -> anyhow::Result<()> { + let mut profiling_map = HashMap::::new(); + + let halt_pc = KERNEL.global_labels["halt"]; + + loop { + // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. + let pc = state.registers.program_counter; + let idx = match KERNEL + .ordered_labels + .binary_search_by_key(&pc, |label| KERNEL.global_labels[label]) + { + Ok(idx) => Some(idx), + Err(0) => None, + Err(idx) => Some(idx - 1), + }; + if let Some(idx) = idx { + profiling_map + .entry(KERNEL.ordered_labels[idx].clone()) + .and_modify(|counter| *counter += 1) + .or_insert(1); + } + let halt = state.registers.is_kernel && pc == halt_pc; + if halt { + log::info!("CPU halted after {} cycles", state.traces.clock()); + + // Padding + let mut row = CpuColumnsView::::default(); + row.clock = F::from_canonical_usize(state.traces.clock()); + row.context = F::from_canonical_usize(state.registers.context); + row.program_counter = F::from_canonical_usize(pc); + row.is_kernel_mode = F::ONE; + row.gas = F::from_canonical_u64(state.registers.gas_used); + row.stack_len = F::from_canonical_usize(state.registers.stack_len); + + loop { + state.traces.push_cpu(row); + row.clock += F::ONE; + if state.traces.clock().is_power_of_two() { + break; + } + } log::info!("CPU trace padded to {} cycles", state.traces.clock()); + let mut sorted_labels: Vec<_> = profiling_map.iter().collect(); + sorted_labels.sort_unstable_by_key(|item| item.1); + sorted_labels.reverse(); + log::info!("Offsets: {:?}", sorted_labels); + return Ok(()); } transition(state)?; + { + let _ = [ + ("secp_add_valid_points_no_edge_case", 10980), + ("ecrecover", 9009), + ("num_bytes", 7306), + ("hex_prefix_rlp", 5820), + ("secp_double", 4076), + ("encode_node_branch", 3408), + ("mstore_unpacking", 2368), + ("main", 2320), + ("secp_add_valid_points", 2281), + ("insert_accessed_addresses", 2238), + ("decode_int_given_len", 1809), + ("read_rlp_to_memory", 1626), + ("load_mpt", 1355), + ("encode_or_hash_node", 1160), + ("mpt_read_branch", 1152), + ("mpt_read", 1065), + ("encode_node", 803), + ("memcpy_bytes", 731), + ("encode_account", 662), + ("prepend_rlp_list_prefix", 602), + ("pack_small_rlp", 590), + ("secp_precompute_table", 477), + ("encode_node_leaf", 459), + ("mstore_unpacking_rlp", 448), + ("maybe_hash_node", 438), + ("encode_node_empty", 413), + ("encode_rlp_fixed", 380), + ("insert_touched_addresses", 368), + ("mpt_read_extension_not_found", 340), + ("mpt_read_state_trie", 323), + ("sys_sstore", 313), + ("debug_it_should_be_128", 295), + ("process_receipt", 292), + ("process_type_0_txn", 283), + ("encode_rlp_scalar", 271), + ("check_bloom_loop", 269), + ("mpt_insert_hash_node", 252), + ("initialize_block_bloom", 247), + ("encode_rlp_list_prefix", 221), + ("encode_rlp_multi_byte_string_prefix", 216), + ("mpt_load_state_trie_value", 213), + ("mload_packing", 204), + ("mpt_hash", 198), + ("decode_rlp_string_len", 197), + ("jumpdest_analysis", 164), + ("load_code", 155), + ("process_normalized_txn", 154), + ("secp_glv_decompose", 148), + ("process_message_txn_code_loaded", 145), + ("insert_accessed_storage_keys", 135), + ("delete_all_touched_addresses", 128), + ("mpt_read_leaf_not_found", 119), + ("encode_receipt", 113), + ("increment_nonce", 108), + ("add_eth", 93), + ("process_message_txn", 82), + ("process_message_txn_after_call", 77), + ("doubly_encode_rlp_scalar", 74), + ("deduct_eth", 72), + ("intrinsic_gas", 64), + ("hash_final_tries", 58), + ("update_txn_trie", 58), + ("terminate_common", 53), + ("extcodehash", 45), + ("warm_precompiles", 45), + ("sys_stop", 42), + ("start_txn", 41), + ("mpt_insert", 39), + ("load_all_mpts", 38), + ("sload_current", 36), + ("encode_txn", 36), + ("scalar_to_rlp", 35), + ("encode_rlp_string", 34), + ("hash_initial_tries", 33), + ("encode_node_branch_prepend_prefix", 32), + ("decode_rlp_scalar", 28), + ("encode_rlp_string_small", 24), + ("route_txn", 24), + ("mpt_hash_storage_trie", 24), + ("encode_rlp_string_large", 23), + ("buy_gas", 20), + ("mpt_insert_receipt_trie", 17), + ("transfer_eth", 17), + ("decode_rlp_list_len", 17), + ("mpt_insert_txn_trie", 16), + ("balance", 15), + ("mpt_hash_txn_trie", 14), + ("mpt_hash_receipt_trie", 14), + ("mpt_hash_state_trie", 14), + ("logs_bloom", 13), + ("delete_all_selfdestructed_addresses", 13), + ("encode_rlp_string_large_after_writing_len", 13), + ("txn_after", 12), + ("encode_rlp_256", 12), + ("encode_storage_value", 12), + ("increment_bounded_rlp", 11), + ("withdrawals", 10), + ("warm_coinbase", 9), + ("nonce", 9), + ("increment_sender_nonce", 9), + ("process_based_on_type", 8), + ("after_storage_read", 7), + ("mpt_read_empty", 7), + ("ec_double_retself", 6), + ("warm_origin", 5), + ("add_bignum", 5), + ("check_bloom_loop_end", 4), + ("execute_withdrawals", 3), + ("encode_rlp_160", 3), + ("charge_gas_hook", 2), + ("halt", 1), + ("jumped_to_0", 1), + ]; + let _ = [ + ("secp_add_valid_points_no_edge_case", 10980), + ("ecrecover", 9009), + ("num_bytes", 7306), + ("hex_prefix_rlp", 5820), + ("secp_double", 4076), + ("encode_node_branch", 3408), + ("mstore_unpacking", 2368), + ("main", 2306), + ("secp_add_valid_points", 2281), + ("insert_accessed_addresses", 2238), + ("decode_int_given_len", 1809), + ("encode_node_empty", 1652), + ("read_rlp_to_memory", 1626), + ("load_mpt", 1355), + ("encode_or_hash_node", 1160), + ("mpt_read_branch", 1152), + ("mpt_read", 1065), + ("encode_node", 803), + ("memcpy_bytes", 731), + ("encode_account", 662), + ("prepend_rlp_list_prefix", 602), + ("pack_small_rlp", 590), + ("secp_precompute_table", 477), + ("encode_node_leaf", 459), + ("mstore_unpacking_rlp", 448), + ("maybe_hash_node", 438), + ("encode_rlp_fixed", 380), + ("insert_touched_addresses", 368), + ("mpt_read_extension_not_found", 340), + ("mpt_read_state_trie", 323), + ("sys_sstore", 313), + ("hash_final_tries", 305), + ("process_receipt", 292), + ("process_type_0_txn", 283), + ("encode_rlp_scalar", 271), + ("check_bloom_loop", 269), + ("mpt_insert_hash_node", 252), + ("encode_rlp_list_prefix", 221), + ("encode_rlp_multi_byte_string_prefix", 216), + ("mpt_load_state_trie_value", 213), + ("mload_packing", 204), + ("mpt_hash", 198), + ("decode_rlp_string_len", 197), + ("jumpdest_analysis", 164), + ("load_code", 155), + ("process_normalized_txn", 154), + ("secp_glv_decompose", 148), + ("process_message_txn_code_loaded", 145), + ("insert_accessed_storage_keys", 135), + ("delete_all_touched_addresses", 128), + ("mpt_read_leaf_not_found", 119), + ("encode_receipt", 113), + ("increment_nonce", 108), + ("add_eth", 93), + ("process_message_txn", 82), + ("process_message_txn_after_call", 77), + ("doubly_encode_rlp_scalar", 74), + ("deduct_eth", 72), + ("intrinsic_gas", 64), + ("update_txn_trie", 58), + ("terminate_common", 53), + ("warm_precompiles", 45), + ("extcodehash", 45), + ("sys_stop", 42), + ("start_txn", 41), + ("mpt_insert", 39), + ("load_all_mpts", 38), + ("sload_current", 36), + ("encode_txn", 36), + ("scalar_to_rlp", 35), + ("encode_rlp_string", 34), + ("hash_initial_tries", 33), + ("encode_node_branch_prepend_prefix", 32), + ("decode_rlp_scalar", 28), + ("route_txn", 24), + ("mpt_hash_storage_trie", 24), + ("encode_rlp_string_small", 24), + ("encode_rlp_string_large", 23), + ("buy_gas", 20), + ("decode_rlp_list_len", 17), + ("transfer_eth", 17), + ("mpt_insert_receipt_trie", 17), + ("mpt_insert_txn_trie", 16), + ("balance", 15), + ("mpt_hash_receipt_trie", 14), + ("mpt_hash_txn_trie", 14), + ("mpt_hash_state_trie", 14), + ("delete_all_selfdestructed_addresses", 13), + ("logs_bloom", 13), + ("encode_rlp_string_large_after_writing_len", 13), + ("encode_storage_value", 12), + ("encode_rlp_256", 12), + ("txn_after", 12), + ("increment_bounded_rlp", 11), + ("withdrawals", 10), + ("nonce", 9), + ("increment_sender_nonce", 9), + ("warm_coinbase", 9), + ("process_based_on_type", 8), + ("after_storage_read", 7), + ("mpt_read_empty", 7), + ("ec_double_retself", 6), + ("add_bignum", 5), + ("warm_origin", 5), + ("check_bloom_loop_end", 4), + ("encode_rlp_160", 3), + ("execute_withdrawals", 3), + ("charge_gas_hook", 2), + ("halt", 1), + ("jumped_to_0", 1), + ]; + + let _ = [ + ("mstore_unpacking", 148), + ("secp_add_valid_points", 139), + ("secp_add_valid_points_no_edge_case", 132), + ("secp_double", 129), + ("mstore_unpacking_rlp", 112), + ("encode_or_hash_node", 76), + ("encode_node", 72), + ("maybe_hash_node", 72), + ("mload_packing", 68), + ("pack_small_rlp", 59), + ("encode_node_empty", 59), + ("mpt_read", 50), + ("num_bytes", 48), + ("load_mpt", 38), + ("mpt_read_branch", 32), + ("memcpy_bytes", 27), + ("encode_rlp_fixed", 20), + ("encode_rlp_scalar", 19), + ("mpt_read_state_trie", 17), + ("prepend_rlp_list_prefix", 14), + ("encode_rlp_256", 12), + ("mpt_hash", 12), + ("insert_accessed_addresses", 12), + ("decode_rlp_string_len", 9), + ("hex_prefix_rlp", 9), + ("encode_node_leaf", 9), + ("decode_int_given_len", 9), + ("encode_rlp_multi_byte_string_prefix", 8), + ("encode_rlp_list_prefix", 8), + ("decode_rlp_scalar", 7), + ("mpt_hash_storage_trie", 6), + ("encode_account", 6), + ("insert_touched_addresses", 5), + ("encode_node_branch_prepend_prefix", 4), + ("encode_node_branch", 4), + ("mpt_load_state_trie_value", 3), + ("extcodehash", 3), + ("mpt_insert", 3), + ("add_eth", 3), + ("mpt_hash_state_trie", 2), + ("encode_rlp_string", 2), + ("deduct_eth", 2), + ("mpt_hash_txn_trie", 2), + ("ec_double_retself", 2), + ("mpt_hash_receipt_trie", 2), + ("secp_glv_decompose", 2), + ("charge_gas_hook", 2), + ("sys_sstore", 1), + ("increment_sender_nonce", 1), + ("buy_gas", 1), + ("main", 1), + ("process_type_0_txn", 1), + ("jumpdest_analysis", 1), + ("txn_after", 1), + ("encode_rlp_160", 1), + ("intrinsic_gas", 1), + ("delete_all_selfdestructed_addresses", 1), + ("encode_rlp_string_large_after_writing_len", 1), + ("jumped_to_0", 1), + ("decode_rlp_list_len", 1), + ("mpt_read_empty", 1), + ("hash_final_tries", 1), + ("sload_current", 1), + ("encode_txn", 1), + ("start_txn", 1), + ("encode_rlp_string_large", 1), + ("load_code", 1), + ("increment_bounded_rlp", 1), + ("encode_receipt", 1), + ("process_message_txn", 1), + ("ecrecover", 1), + ("warm_origin", 1), + ("encode_rlp_string_small", 1), + ("process_based_on_type", 1), + ("secp_precompute_table", 1), + ("halt", 1), + ("update_txn_trie", 1), + ("transfer_eth", 1), + ("logs_bloom", 1), + ("read_rlp_to_memory", 1), + ("encode_storage_value", 1), + ("process_receipt", 1), + ("process_message_txn_code_loaded", 1), + ("increment_nonce", 1), + ("delete_all_touched_addresses", 1), + ("terminate_common", 1), + ("balance", 1), + ("withdrawals", 1), + ("sys_stop", 1), + ("after_storage_read", 1), + ("mpt_insert_receipt_trie", 1), + ("hash_initial_tries", 1), + ("doubly_encode_rlp_scalar", 1), + ("route_txn", 1), + ("mpt_insert_txn_trie", 1), + ("warm_coinbase", 1), + ("load_all_mpts", 1), + ("warm_precompiles", 1), + ("add_bignum", 1), + ("insert_accessed_storage_keys", 1), + ("process_normalized_txn", 1), + ("scalar_to_rlp", 1), + ("nonce", 1), + ("process_message_txn_after_call", 1), + ("execute_withdrawals", 1), + ]; + let _ = [ + ("secp_add_valid_points_no_edge_case", 10980), + ("ecrecover", 9009), + ("num_bytes", 7306), + ("hex_prefix_rlp", 5820), + ("secp_double", 4076), + ("encode_node_branch", 3440), + ("encode_or_hash_node", 2991), + ("mstore_unpacking", 2368), + ("main", 2306), + ("secp_add_valid_points", 2281), + ("insert_accessed_addresses", 2238), + ("decode_int_given_len", 1809), + ("encode_node_empty", 1652), + ("read_rlp_to_memory", 1626), + ("load_mpt", 1355), + ("mpt_read_branch", 1152), + ("mpt_read", 1065), + ("memcpy_bytes", 731), + ("encode_account", 662), + ("prepend_rlp_list_prefix", 602), + ("hash_final_tries", 578), + ("secp_precompute_table", 477), + ("encode_node_leaf", 459), + ("mstore_unpacking_rlp", 448), + ("encode_rlp_fixed", 380), + ("insert_touched_addresses", 368), + ("mpt_read_extension_not_found", 340), + ("mpt_read_state_trie", 323), + ("sys_sstore", 313), + ("process_receipt", 292), + ("process_type_0_txn", 283), + ("encode_rlp_scalar", 271), + ("mpt_insert_hash_node", 252), + ("encode_rlp_list_prefix", 221), + ("encode_rlp_multi_byte_string_prefix", 216), + ("mpt_load_state_trie_value", 213), + ("mload_packing", 204), + ("mpt_hash", 198), + ("decode_rlp_string_len", 197), + ("jumpdest_analysis", 164), + ("load_code", 155), + ("process_normalized_txn", 154), + ("secp_glv_decompose", 148), + ("process_message_txn_code_loaded", 145), + ("insert_accessed_storage_keys", 135), + ("delete_all_touched_addresses", 128), + ("mpt_read_leaf_not_found", 119), + ("encode_receipt", 113), + ("increment_nonce", 108), + ("add_eth", 93), + ("process_message_txn", 82), + ("process_message_txn_after_call", 77), + ("doubly_encode_rlp_scalar", 74), + ("deduct_eth", 72), + ("intrinsic_gas", 64), + ("update_txn_trie", 58), + ("terminate_common", 53), + ("warm_precompiles", 45), + ("extcodehash", 45), + ("sys_stop", 42), + ("start_txn", 41), + ("mpt_insert", 39), + ("load_all_mpts", 38), + ("encode_txn", 36), + ("sload_current", 36), + ("scalar_to_rlp", 35), + ("encode_rlp_string", 34), + ("hash_initial_tries", 33), + ("decode_rlp_scalar", 28), + ("route_txn", 24), + ("mpt_hash_storage_trie", 24), + ("encode_rlp_string_small", 24), + ("encode_rlp_string_large", 23), + ("buy_gas", 20), + ("transfer_eth", 17), + ("mpt_insert_receipt_trie", 17), + ("decode_rlp_list_len", 17), + ("mpt_insert_txn_trie", 16), + ("balance", 15), + ("mpt_hash_txn_trie", 14), + ("mpt_hash_receipt_trie", 14), + ("mpt_hash_state_trie", 14), + ("delete_all_selfdestructed_addresses", 13), + ("logs_bloom", 13), + ("encode_rlp_string_large_after_writing_len", 13), + ("encode_storage_value", 12), + ("encode_rlp_256", 12), + ("txn_after", 12), + ("increment_bounded_rlp", 11), + ("withdrawals", 10), + ("increment_sender_nonce", 9), + ("warm_coinbase", 9), + ("nonce", 9), + ("process_based_on_type", 8), + ("after_storage_read", 7), + ("mpt_read_empty", 7), + ("ec_double_retself", 6), + ("add_bignum", 5), + ("warm_origin", 5), + ("encode_rlp_160", 3), + ("execute_withdrawals", 3), + ("charge_gas_hook", 2), + ("jumped_to_0", 1), + ("halt", 1), + ]; + + let _ = [ + ("secp_add_valid_points_no_edge_case", 10980), + ("ecrecover", 9009), + ("num_bytes", 7306), + ("hex_prefix_rlp", 5820), + ("secp_double", 4076), + ("encode_node_branch", 3440), + ("mstore_unpacking", 2368), + ("main", 2306), + ("secp_add_valid_points", 2281), + ("insert_accessed_addresses", 2238), + ("decode_int_given_len", 1809), + ("encode_node_empty", 1652), + ("read_rlp_to_memory", 1626), + ("load_mpt", 1355), + ("encode_or_hash_node", 1160), + ("mpt_read_branch", 1152), + ("mpt_read", 1065), + ("encode_node", 803), + ("memcpy_bytes", 731), + ("encode_account", 662), + ("prepend_rlp_list_prefix", 602), + ("pack_small_rlp", 590), + ("hash_final_tries", 578), + ("secp_precompute_table", 477), + ("encode_node_leaf", 459), + ("mstore_unpacking_rlp", 448), + ("maybe_hash_node", 438), + ("encode_rlp_fixed", 380), + ("insert_touched_addresses", 368), + ("mpt_read_extension_not_found", 340), + ("mpt_read_state_trie", 323), + ("sys_sstore", 313), + ("process_receipt", 292), + ("process_type_0_txn", 283), + ("encode_rlp_scalar", 271), + ("mpt_insert_hash_node", 252), + ("encode_rlp_list_prefix", 221), + ("encode_rlp_multi_byte_string_prefix", 216), + ("mpt_load_state_trie_value", 213), + ("mload_packing", 204), + ("mpt_hash", 198), + ("decode_rlp_string_len", 197), + ("jumpdest_analysis", 164), + ("load_code", 155), + ("process_normalized_txn", 154), + ("secp_glv_decompose", 148), + ("process_message_txn_code_loaded", 145), + ("insert_accessed_storage_keys", 135), + ("delete_all_touched_addresses", 128), + ("mpt_read_leaf_not_found", 119), + ("encode_receipt", 113), + ("increment_nonce", 108), + ("add_eth", 93), + ("process_message_txn", 82), + ("process_message_txn_after_call", 77), + ("doubly_encode_rlp_scalar", 74), + ("deduct_eth", 72), + ("intrinsic_gas", 64), + ("update_txn_trie", 58), + ("terminate_common", 53), + ("extcodehash", 45), + ("warm_precompiles", 45), + ("sys_stop", 42), + ("start_txn", 41), + ("mpt_insert", 39), + ("load_all_mpts", 38), + ("encode_txn", 36), + ("sload_current", 36), + ("scalar_to_rlp", 35), + ("encode_rlp_string", 34), + ("hash_initial_tries", 33), + ("decode_rlp_scalar", 28), + ("route_txn", 24), + ("mpt_hash_storage_trie", 24), + ("encode_rlp_string_small", 24), + ("encode_rlp_string_large", 23), + ("buy_gas", 20), + ("decode_rlp_list_len", 17), + ("transfer_eth", 17), + ("mpt_insert_receipt_trie", 17), + ("mpt_insert_txn_trie", 16), + ("balance", 15), + ("mpt_hash_txn_trie", 14), + ("mpt_hash_receipt_trie", 14), + ("mpt_hash_state_trie", 14), + ("encode_rlp_string_large_after_writing_len", 13), + ("logs_bloom", 13), + ("delete_all_selfdestructed_addresses", 13), + ("txn_after", 12), + ("encode_storage_value", 12), + ("encode_rlp_256", 12), + ("increment_bounded_rlp", 11), + ("withdrawals", 10), + ("nonce", 9), + ("increment_sender_nonce", 9), + ("warm_coinbase", 9), + ("process_based_on_type", 8), + ("mpt_read_empty", 7), + ("after_storage_read", 7), + ("ec_double_retself", 6), + ("add_bignum", 5), + ("warm_origin", 5), + ("encode_rlp_160", 3), + ("execute_withdrawals", 3), + ("charge_gas_hook", 2), + ("halt", 1), + ("jumped_to_0", 1), + ]; + + let _ = [ + ("secp_add_valid_points_no_edge_case", 10980), + ("ecrecover", 9009), + ("num_bytes", 7306), + ("hex_prefix_rlp", 5820), + ("secp_double", 4076), + ("encode_node_branch", 3408), + ("mstore_unpacking", 2368), + ("main", 2306), + ("secp_add_valid_points", 2281), + ("insert_accessed_addresses", 2238), + ("decode_int_given_len", 1809), + ("encode_node_empty", 1652), + ("read_rlp_to_memory", 1626), + ("load_mpt", 1355), + ("encode_or_hash_node", 1160), + ("mpt_read_branch", 1152), + ("mpt_read", 1065), + ("encode_node", 803), + ("memcpy_bytes", 731), + ("encode_account", 662), + ("prepend_rlp_list_prefix", 602), + ("pack_small_rlp", 590), + ("hash_final_tries", 578), + ("secp_precompute_table", 477), + ("encode_node_leaf", 459), + ("mstore_unpacking_rlp", 448), + ("maybe_hash_node", 438), + ("encode_rlp_fixed", 380), + ("insert_touched_addresses", 368), + ("mpt_read_extension_not_found", 340), + ("mpt_read_state_trie", 323), + ("sys_sstore", 313), + ("process_receipt", 292), + ("process_type_0_txn", 283), + ("encode_rlp_scalar", 271), + ("mpt_insert_hash_node", 252), + ("encode_rlp_list_prefix", 221), + ("encode_rlp_multi_byte_string_prefix", 216), + ("mpt_load_state_trie_value", 213), + ("mload_packing", 204), + ("mpt_hash", 198), + ("decode_rlp_string_len", 197), + ("jumpdest_analysis", 164), + ("load_code", 155), + ("process_normalized_txn", 154), + ("secp_glv_decompose", 148), + ("process_message_txn_code_loaded", 145), + ("insert_accessed_storage_keys", 135), + ("delete_all_touched_addresses", 128), + ("mpt_read_leaf_not_found", 119), + ("encode_receipt", 113), + ("increment_nonce", 108), + ("add_eth", 93), + ("process_message_txn", 82), + ("process_message_txn_after_call", 77), + ("doubly_encode_rlp_scalar", 74), + ("deduct_eth", 72), + ("intrinsic_gas", 64), + ("update_txn_trie", 58), + ("terminate_common", 53), + ("extcodehash", 45), + ("warm_precompiles", 45), + ("sys_stop", 42), + ("start_txn", 41), + ("mpt_insert", 39), + ("load_all_mpts", 38), + ("encode_txn", 36), + ("sload_current", 36), + ("scalar_to_rlp", 35), + ("encode_rlp_string", 34), + ("hash_initial_tries", 33), + ("encode_node_branch_prepend_prefix", 32), + ("decode_rlp_scalar", 28), + ("mpt_hash_storage_trie", 24), + ("route_txn", 24), + ("encode_rlp_string_small", 24), + ("encode_rlp_string_large", 23), + ("buy_gas", 20), + ("transfer_eth", 17), + ("mpt_insert_receipt_trie", 17), + ("decode_rlp_list_len", 17), + ("mpt_insert_txn_trie", 16), + ("balance", 15), + ("mpt_hash_txn_trie", 14), + ("mpt_hash_state_trie", 14), + ("mpt_hash_receipt_trie", 14), + ("delete_all_selfdestructed_addresses", 13), + ("logs_bloom", 13), + ("encode_rlp_string_large_after_writing_len", 13), + ("txn_after", 12), + ("encode_rlp_256", 12), + ("encode_storage_value", 12), + ("increment_bounded_rlp", 11), + ("withdrawals", 10), + ("warm_coinbase", 9), + ("increment_sender_nonce", 9), + ("nonce", 9), + ("process_based_on_type", 8), + ("mpt_read_empty", 7), + ("after_storage_read", 7), + ("ec_double_retself", 6), + ("add_bignum", 5), + ("warm_origin", 5), + ("encode_rlp_160", 3), + ("execute_withdrawals", 3), + ("charge_gas_hook", 2), + ("halt", 1), + ("jumped_to_0", 1), + ]; + } } } From c3d707c1265abdb2fddf64cf74475471fd67a68d Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Thu, 21 Dec 2023 15:59:16 -0500 Subject: [PATCH 054/175] Constrain partial_channel (#1436) --- evm/src/cpu/dup_swap.rs | 9 +++++++++ evm/src/cpu/jumps.rs | 6 ++++++ evm/src/cpu/memio.rs | 5 +++++ evm/src/cpu/stack.rs | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 2ab6435237..78e5891a90 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -335,6 +335,9 @@ pub(crate) fn eval_packed( eval_packed_dup(n, lv, nv, yield_constr); eval_packed_swap(n, lv, nv, yield_constr); + + // For both, disable the partial channel. + yield_constr.constraint(lv.op.dup_swap * lv.partial_channel.used); } /// Circuit version of `eval_packed`. @@ -354,4 +357,10 @@ pub(crate) fn eval_ext_circuit, const D: usize>( eval_ext_circuit_dup(builder, n, lv, nv, yield_constr); eval_ext_circuit_swap(builder, n, lv, nv, yield_constr); + + // For both, disable the partial channel. + { + let constr = builder.mul_extension(lv.op.dup_swap, lv.partial_channel.used); + yield_constr.constraint(builder, constr); + } } diff --git a/evm/src/cpu/jumps.rs b/evm/src/cpu/jumps.rs index f2285c0a46..f2fd544c67 100644 --- a/evm/src/cpu/jumps.rs +++ b/evm/src/cpu/jumps.rs @@ -142,6 +142,8 @@ pub(crate) fn eval_packed_jump_jumpi( for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] { yield_constr.constraint(filter * channel.used); } + yield_constr.constraint(filter * lv.partial_channel.used); + // Channel 1 is unused by the `JUMP` instruction. yield_constr.constraint(is_jump * lv.mem_channels[1].used); @@ -324,6 +326,10 @@ pub(crate) fn eval_ext_circuit_jump_jumpi, const D: let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } + { + let constr = builder.mul_extension(filter, lv.partial_channel.used); + yield_constr.constraint(builder, constr); + } // Channel 1 is unused by the `JUMP` instruction. { let constr = builder.mul_extension(is_jump, lv.mem_channels[1].used); diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 6ea8208989..304bb3de1c 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -56,6 +56,7 @@ fn eval_packed_load( for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS] { yield_constr.constraint(filter * channel.used); } + yield_constr.constraint(filter * lv.partial_channel.used); // Stack constraints stack::eval_packed_one( @@ -120,6 +121,10 @@ fn eval_ext_circuit_load, const D: usize>( let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } + { + let constr = builder.mul_extension(filter, lv.partial_channel.used); + yield_constr.constraint(builder, constr); + } // Stack constraints stack::eval_ext_circuit_one( diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 71dc7a2414..0497b228a4 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -357,6 +357,8 @@ pub(crate) fn eval_packed( for &channel in &lv.mem_channels[1..] { yield_constr.constraint(lv.op.not_pop * (lv.opcode_bits[0] - P::ONES) * channel.used); } + yield_constr + .constraint(lv.op.not_pop * (lv.opcode_bits[0] - P::ONES) * lv.partial_channel.used); // Constrain the new stack length for POP. yield_constr.constraint_transition( @@ -700,6 +702,10 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } + { + let constr = builder.mul_extension(filter, lv.partial_channel.used); + yield_constr.constraint(builder, constr); + } // Constrain the new stack length for POP. let diff = builder.sub_extension(nv.stack_len, lv.stack_len); From ae3003a9d7eec22384328079fd8b413ce7acb153 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:23:22 +0100 Subject: [PATCH 055/175] Add alternative method to prove txs without pre-loaded table circuits (#1438) --- evm/src/cross_table_lookup.rs | 2 +- evm/src/fixed_recursive_verifier.rs | 209 ++++++++++++---------------- evm/src/proof.rs | 10 +- evm/src/prover.rs | 2 +- evm/src/recursive_verifier.rs | 2 +- evm/tests/empty_txn_list.rs | 23 +-- 6 files changed, 103 insertions(+), 145 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 65b27b1392..21f94126d3 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -566,7 +566,7 @@ impl GrandProductChallenge { /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone, Eq, PartialEq, Debug)] -pub(crate) struct GrandProductChallengeSet { +pub struct GrandProductChallengeSet { pub(crate) challenges: Vec>, } diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 063f479a28..3f405e5246 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -1,9 +1,11 @@ use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; +use std::path::Path; use std::sync::atomic::AtomicBool; use std::sync::Arc; +use anyhow::anyhow; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; @@ -23,6 +25,7 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; +use plonky2::util::serialization::gate_serialization::default; use plonky2::util::serialization::{ Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, }; @@ -38,8 +41,8 @@ use crate::cross_table_lookup::{ use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; use crate::proof::{ - BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, - PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, + AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, + PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, }; use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ @@ -70,7 +73,7 @@ where /// The block circuit, which verifies an aggregation root proof and a previous block proof. pub block: BlockCircuitData, /// Holds chains of circuits for each table and for each initial `degree_bits`. - by_table: [RecursiveCircuitsForTable; NUM_TABLES], + pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], } /// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof @@ -297,6 +300,7 @@ where { pub fn to_bytes( &self, + skip_tables: bool, gate_serializer: &dyn GateSerializer, generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult> { @@ -308,14 +312,17 @@ where .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; self.block .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; - for table in &self.by_table { - table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + if !skip_tables { + for table in &self.by_table { + table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?; + } } Ok(buffer) } pub fn from_bytes( bytes: &[u8], + skip_tables: bool, gate_serializer: &dyn GateSerializer, generator_serializer: &dyn WitnessGeneratorSerializer, ) -> IoResult { @@ -330,21 +337,30 @@ where let block = BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - // Tricky use of MaybeUninit to remove the need for implementing Debug - // for all underlying types, necessary to convert a by_table Vec to an array. - let by_table = { - let mut by_table: [MaybeUninit>; NUM_TABLES] = - unsafe { MaybeUninit::uninit().assume_init() }; - for table in &mut by_table[..] { - let value = RecursiveCircuitsForTable::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; - *table = MaybeUninit::new(value); - } - unsafe { - mem::transmute::<_, [RecursiveCircuitsForTable; NUM_TABLES]>(by_table) + let by_table = match skip_tables { + true => (0..NUM_TABLES) + .map(|_| RecursiveCircuitsForTable { + by_stark_size: BTreeMap::default(), + }) + .collect_vec() + .try_into() + .unwrap(), + false => { + // Tricky use of MaybeUninit to remove the need for implementing Debug + // for all underlying types, necessary to convert a by_table Vec to an array. + let mut by_table: [MaybeUninit>; NUM_TABLES] = + unsafe { MaybeUninit::uninit().assume_init() }; + for table in &mut by_table[..] { + let value = RecursiveCircuitsForTable::from_buffer( + &mut buffer, + gate_serializer, + generator_serializer, + )?; + *table = MaybeUninit::new(value); + } + unsafe { + mem::transmute::<_, [RecursiveCircuitsForTable; NUM_TABLES]>(by_table) + } } }; @@ -432,72 +448,6 @@ where } } - /// Expand the preprocessed STARK table circuits with the provided ranges. - /// - /// If a range for a given table is contained within the current one, this will be a no-op. - /// Otherwise, it will add the circuits for the missing table sizes, and regenerate the upper circuits. - pub fn expand( - &mut self, - all_stark: &AllStark, - degree_bits_ranges: &[Range; NUM_TABLES], - stark_config: &StarkConfig, - ) { - self.by_table[Table::Arithmetic as usize].expand( - Table::Arithmetic, - &all_stark.arithmetic_stark, - degree_bits_ranges[Table::Arithmetic as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::BytePacking as usize].expand( - Table::BytePacking, - &all_stark.byte_packing_stark, - degree_bits_ranges[Table::BytePacking as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::Cpu as usize].expand( - Table::Cpu, - &all_stark.cpu_stark, - degree_bits_ranges[Table::Cpu as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::Keccak as usize].expand( - Table::Keccak, - &all_stark.keccak_stark, - degree_bits_ranges[Table::Keccak as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::KeccakSponge as usize].expand( - Table::KeccakSponge, - &all_stark.keccak_sponge_stark, - degree_bits_ranges[Table::KeccakSponge as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::Logic as usize].expand( - Table::Logic, - &all_stark.logic_stark, - degree_bits_ranges[Table::Logic as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - self.by_table[Table::Memory as usize].expand( - Table::Memory, - &all_stark.memory_stark, - degree_bits_ranges[Table::Memory as usize].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - - // Regenerate the upper circuits. - self.root = Self::create_root_circuit(&self.by_table, stark_config); - self.aggregation = Self::create_aggregation_circuit(&self.root); - self.block = Self::create_block_circuit(&self.aggregation); - } - /// Outputs the `VerifierCircuitData` needed to verify any block proof /// generated by an honest prover. pub fn final_verifier_data(&self) -> VerifierCircuitData { @@ -988,7 +938,7 @@ where .by_stark_size .get(&original_degree_bits) .ok_or_else(|| { - anyhow::Error::msg(format!( + anyhow!(format!( "Missing preprocessed circuits for {:?} table with size {}.", Table::all()[table], original_degree_bits, @@ -1028,6 +978,55 @@ where Ok((root_proof, all_proof.public_values)) } + /// From an initial set of STARK proofs passed with their associated recursive table circuits, + /// generate a recursive transaction proof. + /// It is aimed at being used when preprocessed table circuits have not been loaded to memory. + pub fn prove_root_after_initial_stark( + &self, + all_stark: &AllStark, + config: &StarkConfig, + all_proof: AllProof, + table_circuits: &[(RecursiveCircuitsForTableSize, u8); NUM_TABLES], + timing: &mut TimingTree, + abort_signal: Option>, + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { + let mut root_inputs = PartialWitness::new(); + + for table in 0..NUM_TABLES { + let (table_circuit, index_verifier_data) = &table_circuits[table]; + + let stark_proof = &all_proof.stark_proofs[table]; + let original_degree_bits = stark_proof.proof.recover_degree_bits(config); + + let shrunk_proof = table_circuit.shrink(stark_proof, &all_proof.ctl_challenges)?; + root_inputs.set_target( + self.root.index_verifier_data[table], + F::from_canonical_u8(*index_verifier_data), + ); + root_inputs.set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof); + + check_abort_signal(abort_signal.clone())?; + } + + root_inputs.set_verifier_data_target( + &self.root.cyclic_vk, + &self.aggregation.circuit.verifier_only, + ); + + set_public_value_targets( + &mut root_inputs, + &self.root.public_values, + &all_proof.public_values, + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + + let root_proof = self.root.circuit.prove(root_inputs)?; + + Ok((root_proof, all_proof.public_values)) + } + pub fn verify_root(&self, agg_proof: ProofWithPublicInputs) -> anyhow::Result<()> { self.root.circuit.verify(agg_proof) } @@ -1255,7 +1254,7 @@ where { /// A map from `log_2(height)` to a chain of shrinking recursion circuits starting at that /// height. - by_stark_size: BTreeMap>, + pub by_stark_size: BTreeMap>, } impl RecursiveCircuitsForTable @@ -1321,32 +1320,6 @@ where Self { by_stark_size } } - fn expand>( - &mut self, - table: Table, - stark: &S, - degree_bits_range: Range, - all_ctls: &[CrossTableLookup], - stark_config: &StarkConfig, - ) { - let new_ranges = degree_bits_range - .filter(|degree_bits| !self.by_stark_size.contains_key(degree_bits)) - .collect_vec(); - - for degree_bits in new_ranges { - self.by_stark_size.insert( - degree_bits, - RecursiveCircuitsForTableSize::new::( - table, - stark, - degree_bits, - all_ctls, - stark_config, - ), - ); - } - } - /// For each initial `degree_bits`, get the final circuit at the end of that shrinking chain. /// Each of these final circuits should have degree `THRESHOLD_DEGREE_BITS`. fn final_circuits(&self) -> Vec<&CircuitData> { @@ -1366,7 +1339,7 @@ where /// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits` /// `THRESHOLD_DEGREE_BITS`. #[derive(Eq, PartialEq, Debug)] -struct RecursiveCircuitsForTableSize +pub struct RecursiveCircuitsForTableSize where F: RichField + Extendable, C: GenericConfig, @@ -1382,7 +1355,7 @@ where C: GenericConfig, C::Hasher: AlgebraicHasher, { - fn to_buffer( + pub fn to_buffer( &self, buffer: &mut Vec, gate_serializer: &dyn GateSerializer, @@ -1409,7 +1382,7 @@ where Ok(()) } - fn from_buffer( + pub fn from_buffer( buffer: &mut Buffer, gate_serializer: &dyn GateSerializer, generator_serializer: &dyn WitnessGeneratorSerializer, @@ -1500,7 +1473,7 @@ where } } - fn shrink( + pub fn shrink( &self, stark_proof_with_metadata: &StarkProofWithMetadata, ctl_challenges: &GrandProductChallengeSet, diff --git a/evm/src/proof.rs b/evm/src/proof.rs index a5bf57566e..3768f98f90 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -270,7 +270,7 @@ impl ExtraBlockData { /// Memory values which are public. /// Note: All the larger integers are encoded with 32-bit limbs in little-endian order. #[derive(Eq, PartialEq, Debug)] -pub(crate) struct PublicValuesTarget { +pub struct PublicValuesTarget { /// Trie hashes before the execution of the local state transition. pub trie_roots_before: TrieRootsTarget, /// Trie hashes after the execution of the local state transition. @@ -485,7 +485,7 @@ impl PublicValuesTarget { /// Circuit version of `TrieRoots`. /// `Target`s for trie hashes. Since a `Target` holds a 32-bit limb, each hash requires 8 `Target`s. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub(crate) struct TrieRootsTarget { +pub struct TrieRootsTarget { /// Targets for the state trie hash. pub(crate) state_root: [Target; 8], /// Targets for the transactions trie hash. @@ -556,7 +556,7 @@ impl TrieRootsTarget { /// Metadata contained in a block header. Those are identical between /// all state transition proofs within the same block. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub(crate) struct BlockMetadataTarget { +pub struct BlockMetadataTarget { /// `Target`s for the address of this block's producer. pub(crate) block_beneficiary: [Target; 5], /// `Target` for the timestamp of this block. @@ -681,7 +681,7 @@ impl BlockMetadataTarget { /// When the block number is less than 256, dummy values, i.e. `H256::default()`, /// should be used for the additional block hashes. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub(crate) struct BlockHashesTarget { +pub struct BlockHashesTarget { /// `Target`s for the previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0..8]`, /// is the oldest, and the rightmost, i.e. `prev_hashes[255 * 7..255 * 8]` is the hash of the parent block. pub(crate) prev_hashes: [Target; 2048], @@ -739,7 +739,7 @@ impl BlockHashesTarget { /// Additional block data that are specific to the local transaction being proven, /// unlike `BlockMetadata`. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub(crate) struct ExtraBlockDataTarget { +pub struct ExtraBlockDataTarget { /// `Target`s for the state trie digest of the checkpoint block. pub checkpoint_state_trie_root: [Target; 8], /// `Target` for the transaction count prior execution of the local state transition, starting diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 4d2daaf5eb..c90490b8a0 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -668,7 +668,7 @@ where /// Utility method that checks whether a kill signal has been emitted by one of the workers, /// which will result in an early abort for all the other processes involved in the same set /// of transactions. -pub(crate) fn check_abort_signal(abort_signal: Option>) -> Result<()> { +pub fn check_abort_signal(abort_signal: Option>) -> Result<()> { if let Some(signal) = abort_signal { if signal.load(Ordering::Relaxed) { return Err(anyhow!("Stopping job from abort signal.")); diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 3103dd49b9..633a8d331d 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -803,7 +803,7 @@ pub(crate) fn set_stark_proof_target, W, const D: set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof); } -pub(crate) fn set_public_value_targets( +pub fn set_public_value_targets( witness: &mut W, public_values_target: &PublicValuesTarget, public_values: &PublicValues, diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 16486677ef..5904b8a9a8 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -75,11 +75,9 @@ fn test_empty_txn_list() -> anyhow::Result<()> { }; // Initialize the preprocessed circuits for the zkEVM. - // The provided ranges are the minimal ones to prove an empty list, except the one of the CPU - // that is wrong for testing purposes, see below. - let mut all_circuits = AllRecursiveCircuits::::new( + let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 10..11, 11..12, 14..15, 9..11, 12..13, 17..18], // Minimal ranges to prove an empty list + &[16..17, 10..11, 12..13, 14..15, 9..11, 12..13, 17..18], // Minimal ranges to prove an empty list &config, ); @@ -91,7 +89,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let timing = TimingTree::new("serialize AllRecursiveCircuits", log::Level::Info); let all_circuits_bytes = all_circuits - .to_bytes(&gate_serializer, &generator_serializer) + .to_bytes(false, &gate_serializer, &generator_serializer) .map_err(|_| anyhow::Error::msg("AllRecursiveCircuits serialization failed."))?; timing.filter(Duration::from_millis(100)).print(); info!( @@ -102,6 +100,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let timing = TimingTree::new("deserialize AllRecursiveCircuits", log::Level::Info); let all_circuits_from_bytes = AllRecursiveCircuits::::from_bytes( &all_circuits_bytes, + false, &gate_serializer, &generator_serializer, ) @@ -111,20 +110,6 @@ fn test_empty_txn_list() -> anyhow::Result<()> { assert_eq!(all_circuits, all_circuits_from_bytes); } - let mut timing = TimingTree::new("prove", log::Level::Info); - // We're missing some preprocessed circuits. - assert!(all_circuits - .prove_root(&all_stark, &config, inputs.clone(), &mut timing, None) - .is_err()); - - // Expand the preprocessed circuits. - // We pass an empty range if we don't want to add different table sizes. - all_circuits.expand( - &all_stark, - &[0..0, 0..0, 12..13, 0..0, 0..0, 0..0, 0..0], - &StarkConfig::standard_fast_config(), - ); - let mut timing = TimingTree::new("prove", log::Level::Info); let (root_proof, public_values) = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None)?; From a83404966e9f4c3bab21a0d7ef4bdfa7f56c25c7 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 30 Nov 2023 13:27:15 +0100 Subject: [PATCH 056/175] Rebase to main Refactor encode_empty_node and encode_branch_node Add constant and store encoded empty node in an other position Remove child segment Clean code Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> Remive global label Move encoded empty nodes --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 29 +- evm/src/cpu/kernel/asm/main.asm | 2 + evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 179 +--- .../asm/mpt/hash/hash_trie_specific.asm | 7 + evm/src/cpu/kernel/asm/mpt/util.asm | 8 +- .../cpu/kernel/constants/global_metadata.rs | 76 +- evm/src/cpu/kernel/constants/mod.rs | 14 +- evm/src/cpu/kernel/interpreter.rs | 18 +- evm/src/cpu/kernel/tests/account_code.rs | 64 +- evm/src/cpu/kernel/tests/add11.rs | 11 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 4 +- evm/src/generation/mod.rs | 778 +----------------- evm/src/memory/segments.rs | 67 +- evm/src/witness/memory.rs | 14 + 14 files changed, 226 insertions(+), 1045 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 56d9e12621..bda6f96e63 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -4,9 +4,8 @@ global jumpdest_analysis: // stack: ctx, code_len, retdest PUSH 0 // i = 0 - %stack (i, ctx, code_len, retdest) -> (i, ctx, code_len, retdest, 0) // ctr -global loop: +loop: // stack: i, ctx, code_len, retdest // Ideally we would break if i >= code_len, but checking i > code_len is // cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is @@ -14,20 +13,6 @@ global loop: DUP3 DUP2 GT // i > code_len %jumpi(return) - %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, 32, i, ctx) - %mload_packing - // stack: packed_opcodes - DUP1 - PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 - AND -global debug_before_as_dad: - %jumpi(as_dad) -global debug_wuau: -as_dad: - POP -global debug_not_wuau: - - // stack: i, ctx, code_len, retdest %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) MLOAD_GENERAL @@ -43,11 +28,8 @@ global debug_not_wuau: // stack: JUMPDEST, i, ctx, code_len, retdest %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) MSTORE_GENERAL - %stack (opcode, i, ctx, code_len, retdest, ctr) -> (ctr, opcode, i, ctx, code_len, retdest) - %increment - %stack (ctr, opcode, i, ctx, code_len, retdest) -> (opcode, i, ctx, code_len, retdest, ctr) -global continue: +continue: // stack: opcode, i, ctx, code_len, retdest %add_const(code_bytes_to_skip) %mload_kernel_code @@ -56,12 +38,9 @@ global continue: // stack: i, ctx, code_len, retdest %jump(loop) -global return: +return: // stack: i, ctx, code_len, retdest %pop3 - SWAP1 -global debug_ctr: - POP JUMP // Determines how many bytes away is the next opcode, based on the opcode we read. @@ -69,7 +48,7 @@ global debug_ctr: // // Note that the range of PUSH opcodes is [0x60, 0x80). I.e. PUSH1 is 0x60 // and PUSH32 is 0x7f. -global code_bytes_to_skip: +code_bytes_to_skip: %rep 96 BYTES 1 // 0x00-0x5f %endrep diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index f6cd03bc8f..d1e06b6633 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -13,6 +13,8 @@ global main: // Initialise the shift table %shift_table_init + // Encode constant nodes + %initialize_rlp_segment // Encode constant nodes %initialize_rlp_segment diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 3bbb84f38f..cf0634fd22 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -49,10 +49,9 @@ mpt_hash_hash_rlp_after_unpacking: // The result is given as a (value, length) pair, where the length is given // in bytes. // -// Pre stack: node_ptr, encode_value, retdest -// Post stack: result, result_len +// Pre stack: node_ptr, encode_value, cur_len, retdest +// Post stack: result, result_len, cur_len global encode_or_hash_node: - // stack: node_ptr, encode_value, cur_len, retdest DUP1 %mload_trie_data // Check if we're dealing with a concrete node, i.e. not a hash node. @@ -92,11 +91,11 @@ maybe_hash_node: KECCAK_GENERAL %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP -global pack_small_rlp: - // stack: result_ptr, result_len, retdest +pack_small_rlp: + // stack: result_ptr, result_len, cur_len, retdest %stack (result_ptr, result_len) -> (0, @SEGMENT_RLP_RAW, result_ptr, result_len, - after_packed_small_rlp, result_len, cur_len) + after_packed_small_rlp, result_len) %jump(mload_packing) after_packed_small_rlp: %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) @@ -105,10 +104,10 @@ after_packed_small_rlp: // RLP encode the given trie node, and return an (pointer, length) pair // indicating where the data lives within @SEGMENT_RLP_RAW. // -// Pre stack: node_type, node_ptr, encode_value, retdest +// Pre stack: node_type, node_ptr, encode_value, cur_len, retdest // Post stack: result_ptr, result_len -global encode_node: - // stack: node_type, node_ptr, encode_value, retdest +encode_node: + // stack: node_type, node_ptr, encode_value, cur_len, retdest // Increment node_ptr, so it points to the node payload instead of its type. SWAP1 %increment SWAP1 // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest @@ -122,14 +121,16 @@ global encode_node: // been handled earlier in encode_or_hash_node, or something invalid. PANIC +global encode_node_empty: + // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest + %pop3 + %stack (cur_len, retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1, cur_len) + JUMP + global encode_node_empty: // stack: node_type, node_payload_ptr, encode_value, retdest %pop3 - PUSH 0x1000 - %mload_kernel(@SEGMENT_RLP_RAW) -global debug_it_should_be_128: - POP - %stack (retdest) -> (retdest, 0x1000, 1) + %stack (retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1) JUMP global encode_node_branch: @@ -140,33 +141,19 @@ global encode_node_branch: SWAP2 %add_const(18) SWAP2 // stack: node_payload_ptr, encode_value, cur_len, retdest - // Get the next unused offset within the encoded child buffers. - // Then immediately increment the next unused offset by 16, so any - // recursive calls will use nonoverlapping offsets. - // TODO: Allocate a block of RLP memory instead? - %mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) - DUP1 %add_const(16) - %mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest - // We will call encode_or_hash_node on each child. For the i'th child, we - // will store the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in - // SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. + // Allocate a block of RLP memory + %alloc_rlp_block DUP1 + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len retdest + + // Call encode_or_hash_node on each child %encode_child(0) %encode_child(1) %encode_child(2) %encode_child(3) %encode_child(4) %encode_child(5) %encode_child(6) %encode_child(7) %encode_child(8) %encode_child(9) %encode_child(10) %encode_child(11) %encode_child(12) %encode_child(13) %encode_child(14) %encode_child(15) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest - // Now, append each child to our RLP tape. - %alloc_rlp_block DUP1 - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest - %append_child(0) %append_child(1) %append_child(2) %append_child(3) - %append_child(4) %append_child(5) %append_child(6) %append_child(7) - %append_child(8) %append_child(9) %append_child(10) %append_child(11) - %append_child(12) %append_child(13) %append_child(14) %append_child(15) - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %stack (rlp_pos, rlp_start, base_offset, node_payload_ptr) + %stack (rlp_pos, rlp_start, node_payload_ptr) -> (node_payload_ptr, rlp_pos, rlp_start) %add_const(16) // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest @@ -198,48 +185,36 @@ encode_node_branch_prepend_prefix: -> (retdest, rlp_prefix_start, rlp_len, cur_len) JUMP + // Part of the encode_node_branch function. Encodes the i'th child. -// Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in -// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. %macro encode_child(i) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest PUSH %%after_encode - DUP4 DUP4 - // stack: node_payload_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest + DUP6 DUP6 DUP6 + // stack: node_payload_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest %add_const($i) %mload_trie_data - // stack: child_i_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest - %stack(child_i_ptr, encode_value, after_encode, base_offset, node_payload_ptr, encode_value, cur_len) -> (child_i_ptr, encode_value, cur_len, after_encode, base_offset, node_payload_ptr, encode_value) + // stack: child_i_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest + %stack + (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest) -> + (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) %jump(encode_or_hash_node) %%after_encode: - // stack: result, result_len, cur_len, base_offset, node_payload_ptr, encode_value, retdest - %stack(result, result_len, cur_len, base_offset, node_payload_ptr, encode_value) -> (result, result_len, base_offset, node_payload_ptr, encode_value, cur_len) - DUP3 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD) - // stack: result_len, base_offset, node_payload_ptr, encode_value, cur_len, retdest - DUP2 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest -%endmacro - -// Part of the encode_node_branch function. Appends the i'th child's RLP. -%macro append_child(i) - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest - DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result - DUP4 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - DUP1 %sub_const(32) %jumpi(%%unpack) + SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest PUSH 160 - DUP4 // rlp_pos + DUP5 // rlp_pos %mstore_rlp - SWAP2 %increment SWAP2 // rlp_pos += 1 + SWAP3 %increment SWAP3 // rlp_pos += 1 %%unpack: - %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) + %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, - rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) + rlp_start, node_payload_ptr, encode_value, cur_len, retdest) %jump(mstore_unpacking_rlp) %%after_unpacking: - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest %endmacro global encode_node_extension: @@ -322,81 +297,3 @@ encode_node_leaf_after_encode_value: %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) JUMP - -global encode_node_branch_new: - // stack: node_type, node_payload_ptr, encode_value, retdest - POP - // stack: node_payload_ptr, encode_value, retdest - - //Allocate a block of RLP memory - %alloc_rlp_block DUP1 - // stack: rlp_pos, node_payload_ptr, encode_value, retdest - - // Call encode_or_hash_node on each child - %encode_child_new(0) %encode_child_new(1) %encode_child_new(2) %encode_child_new(3) - %encode_child_new(4) %encode_child_new(5) %encode_child_new(6) %encode_child_new(7) - %encode_child_new(8) %encode_child_new(9) %encode_child_new(10) %encode_child_new(11) - %encode_child_new(12) %encode_child_new(13) %encode_child_new(14) %encode_child_new(15) - - // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, retdest - - %stack (rlp_pos, rlp_start, node_payload_ptr) - -> (node_payload_ptr, rlp_pos, rlp_start) - %add_const(16) - // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, retdest - %mload_trie_data - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest - DUP1 %jumpi(encode_node_branch_with_value_new) - - // No value; append the empty string (0x80). - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start) - %mstore_rlp - // stack: rlp_pos', rlp_start, retdest - %increment - // stack: rlp_pos'', rlp_start, retdest - %jump(encode_node_branch_prepend_prefix_new) -encode_node_branch_with_value_new: - // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value) - -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix, rlp_start) - JUMP // call encode_value -encode_node_branch_prepend_prefix_new: - // stack: rlp_pos'', rlp_start, retdest - %prepend_rlp_list_prefix - // stack: rlp_prefix_start, rlp_len, retdest - %stack (rlp_prefix_start, rlp_len, retdest) - -> (retdest, rlp_prefix_start, rlp_len) - JUMP - - -// Part of the encode_node_branch function. Encodes the i'th child. -// Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in -// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. -%macro encode_child_new(i) - // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest - PUSH %%after_encode - DUP5 DUP5 - // stack: node_payload_ptr, encode_value, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest - %add_const($i) %mload_trie_data - // stack: child_i_ptr, encode_value, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest - %jump(encode_or_hash_node) -%%after_encode: - // stack: result, result_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest - // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) - // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest - PUSH 160 - DUP4 // rlp_pos - %mstore_rlp - SWAP2 %increment SWAP2 // rlp_pos += 1 -%%unpack: - %stack (result_len, result, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) - -> (rlp_pos, result, result_len, %%after_unpacking, - rlp_start, node_payload_ptr, encode_value, retdest) - %jump(mstore_unpacking_rlp) -%%after_unpacking: - // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, retdest -%endmacro - diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 2ffefb7d5a..f3ee000def 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -3,6 +3,9 @@ global mpt_hash_state_trie: // stack: cur_len, retdest PUSH encode_account + PUSH debug_before_encoding_child + PUSH mpt_delete + %pop2 %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // stack: node_ptr, encode_account, cur_len, retdest %jump(mpt_hash) @@ -101,6 +104,10 @@ global encode_account: DUP3 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] // stack: storage_root_ptr, cur_len, rlp_pos_5, value_ptr, cur_len, retdest + + PUSH debug_after_hash_storage_trie + POP + // Hash storage trie. %mpt_hash_storage_trie // stack: storage_root_digest, new_len, rlp_pos_5, value_ptr, cur_len, retdest diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index b492a0d055..27418b1704 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -11,13 +11,9 @@ %endmacro %macro initialize_rlp_segment - // Write the encoding of the empty node to address 0 leaving 9 bytes for a prefix - // TODO: Do we need a prefix? PUSH 0x80 - PUSH 0x1000 + PUSH @ENCODED_EMPTY_NODE_POS %mstore_rlp - PUSH 0x1000 // TODO: use 10? - %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) %endmacro %macro alloc_rlp_block @@ -27,7 +23,7 @@ // In our model it's fine to use memory in a sparse way, as long as the gaps aren't larger than // 2^16 or so. So instead of the caller specifying the size of the block they need, we'll just // allocate 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. - DUP1 %add_const(0x10000) + DUP1 %add_const(@MAX_RLP_BLOB_SIZE) // stack: block_end, block_start %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) // stack: block_start diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 8b85c0b552..b5e52e5c3c 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -30,68 +30,64 @@ pub(crate) enum GlobalMetadata { TransactionTrieRootDigestAfter = 11, ReceiptTrieRootDigestAfter = 12, - /// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the - /// next available offset in these buffers. - TrieEncodedChildSize = 13, - // Block metadata. - BlockBeneficiary = 14, - BlockTimestamp = 15, - BlockNumber = 16, - BlockDifficulty = 17, - BlockRandom = 18, - BlockGasLimit = 19, - BlockChainId = 20, - BlockBaseFee = 21, - BlockGasUsed = 22, + BlockBeneficiary = 13, + BlockTimestamp = 14, + BlockNumber = 15, + BlockDifficulty = 16, + BlockRandom = 17, + BlockGasLimit = 18, + BlockChainId = 19, + BlockBaseFee = 20, + BlockGasUsed = 21, /// Before current transactions block values. - BlockGasUsedBefore = 23, + BlockGasUsedBefore = 22, /// After current transactions block values. - BlockGasUsedAfter = 24, + BlockGasUsedAfter = 23, /// Current block header hash - BlockCurrentHash = 25, + BlockCurrentHash = 24, /// Gas to refund at the end of the transaction. - RefundCounter = 26, + RefundCounter = 25, /// Length of the addresses access list. - AccessedAddressesLen = 27, + AccessedAddressesLen = 26, /// Length of the storage keys access list. - AccessedStorageKeysLen = 28, + AccessedStorageKeysLen = 27, /// Length of the self-destruct list. - SelfDestructListLen = 29, + SelfDestructListLen = 28, /// Length of the bloom entry buffer. - BloomEntryLen = 30, + BloomEntryLen = 29, /// Length of the journal. - JournalLen = 31, + JournalLen = 30, /// Length of the `JournalData` segment. - JournalDataLen = 32, + JournalDataLen = 31, /// Current checkpoint. - CurrentCheckpoint = 33, - TouchedAddressesLen = 34, + CurrentCheckpoint = 32, + TouchedAddressesLen = 33, // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost = 35, + AccessListDataCost = 34, // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart = 36, + AccessListRlpStart = 35, // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen = 37, + AccessListRlpLen = 36, // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation = 38, - IsPrecompileFromEoa = 39, - CallStackDepth = 40, + ContractCreation = 37, + IsPrecompileFromEoa = 38, + CallStackDepth = 39, /// Transaction logs list length - LogsLen = 41, - LogsDataLen = 42, - LogsPayloadLen = 43, - TxnNumberBefore = 44, - TxnNumberAfter = 45, + LogsLen = 40, + LogsDataLen = 41, + LogsPayloadLen = 42, + TxnNumberBefore = 43, + TxnNumberAfter = 44, - KernelHash = 46, - KernelLen = 47, + KernelHash = 45, + KernelLen = 46, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 48; + pub(crate) const COUNT: usize = 47; pub(crate) const fn all() -> [Self; Self::COUNT] { [ @@ -108,7 +104,6 @@ impl GlobalMetadata { Self::StateTrieRootDigestAfter, Self::TransactionTrieRootDigestAfter, Self::ReceiptTrieRootDigestAfter, - Self::TrieEncodedChildSize, Self::BlockBeneficiary, Self::BlockTimestamp, Self::BlockNumber, @@ -162,7 +157,6 @@ impl GlobalMetadata { Self::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER", Self::TransactionTrieRootDigestAfter => "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER", Self::ReceiptTrieRootDigestAfter => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER", - Self::TrieEncodedChildSize => "GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE", Self::BlockBeneficiary => "GLOBAL_METADATA_BLOCK_BENEFICIARY", Self::BlockTimestamp => "GLOBAL_METADATA_BLOCK_TIMESTAMP", Self::BlockNumber => "GLOBAL_METADATA_BLOCK_NUMBER", diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 6e2a0015d3..fa25fa8704 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use ethereum_types::U256; use hex_literal::hex; +use static_assertions::const_assert; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -86,12 +87,23 @@ pub(crate) fn evm_constants() -> HashMap { c } -const MISC_CONSTANTS: [(&str, [u8; 32]); 1] = [ +const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ // Base for limbs used in bignum arithmetic. ( "BIGNUM_LIMB_BASE", hex!("0000000000000000000000000000000100000000000000000000000000000000"), ), + // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is + // equal to usize::MAX so that all rlp pointers all much smalled than that + ( + "ENCODED_EMPTY_NODE_POS", + hex!("00000000000000000000000000000000000000000000000000000000FFFFFFFF"), + ), + // 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. + ( + "MAX_RLP_BLOB_SIZE", + hex!("0000000000000000000000000000000000000000000000000000000000010000"), + ), ]; const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c437672113..58de9e3485 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -140,12 +140,14 @@ enum InterpreterMemOpKind { impl<'a> Interpreter<'a> { pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { - Self::new( + let mut result = Self::new( &KERNEL.code, initial_offset, initial_stack, &KERNEL.prover_inputs, - ) + ); + result.initialize_rlp_segment(); + result } pub(crate) fn new( @@ -1171,6 +1173,18 @@ impl<'a> Interpreter<'a> { } self.generation_state.registers.context = context; } + + /// Writes the encoding of 0 to position @ + pub(crate) fn initialize_rlp_segment(&mut self) { + self.generation_state.memory.set( + MemoryAddress { + context: 0, + segment: Segment::RlpRaw as usize, + virt: 0xFFFFFFFF, + }, + 128.into(), + ) + } } // Computes the two's complement of the given integer. diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 20c98bf976..f985ebd445 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -201,21 +201,36 @@ fn test_extcodecopy() -> Result<()> { prepare_interpreter(&mut interpreter, address, &account)?; let context = interpreter.context(); - interpreter.generation_state.memory.contexts[context].segments - [Segment::ContextMetadata as usize] - .set(GasLimit as usize, U256::from(1000000000000u64)); + interpreter.generation_state.memory.set( + MemoryAddress { + context, + segment: Segment::ContextMetadata as usize, + virt: GasLimit as usize, + }, + U256::from(1000000000000u64), + ); let extcodecopy = KERNEL.global_labels["sys_extcodecopy"]; // Put random data in main memory and the `KernelAccountCode` segment for realism. let mut rng = thread_rng(); for i in 0..2000 { - interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .set(i, U256::from(rng.gen::())); - interpreter.generation_state.memory.contexts[context].segments - [Segment::KernelAccountCode as usize] - .set(i, U256::from(rng.gen::())); + interpreter.generation_state.memory.set( + MemoryAddress { + context, + segment: Segment::MainMemory as usize, + virt: i, + }, + U256::from(rng.gen::()), + ); + interpreter.generation_state.memory.set( + MemoryAddress { + context, + segment: Segment::KernelAccountCode as usize, + virt: i, + }, + U256::from(rng.gen::()), + ); } // Random inputs @@ -250,9 +265,11 @@ fn test_extcodecopy() -> Result<()> { assert!(interpreter.stack().is_empty()); // Check that the code was correctly copied to memory. for i in 0..size { - let memory = interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .get(dest_offset + i); + let memory = interpreter.generation_state.memory.get(MemoryAddress { + context, + segment: Segment::MainMemory as usize, + virt: dest_offset + i, + }); assert_eq!( memory, code.get(offset + i).copied().unwrap_or_default().into() @@ -277,13 +294,22 @@ fn prepare_interpreter_all_accounts( // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = 0; interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set( - ContextMetadata::Address as usize, - U256::from_big_endian(&addr), - ); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 100_000.into()); + interpreter.generation_state.memory.set( + MemoryAddress { + context: 1, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::Address as usize, + }, + U256::from_big_endian(&addr), + ); + interpreter.generation_state.memory.set( + MemoryAddress { + context: 1, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::GasLimit as usize, + }, + 100_000.into(), + ); interpreter.set_context(1); interpreter.set_is_kernel(false); interpreter.generation_state.memory.set( diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 9ba65db2c9..b85bfe66c0 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -19,6 +19,7 @@ use crate::generation::TrieInputs; use crate::memory::segments::Segment; use crate::proof::TrieRoots; use crate::util::h2u; +use crate::witness::memory::MemoryAddress; // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by loading the initial MPTs and @@ -199,8 +200,14 @@ fn test_add11_yml() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.generation_state.memory.set( + MemoryAddress { + context: 0, + segment: Segment::ContextMetadata as usize, + virt: ContextMetadata::GasLimit as usize, + }, + 1_000_000.into(), + ); interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index 9c2fd50b24..acd42e8f3a 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -12,11 +12,12 @@ use crate::cpu::kernel::tests::mpt::{ }; use crate::generation::mpt::AccountRlp; use crate::generation::TrieInputs; +use crate::util::u256_to_usize; use crate::Node; #[test] fn mpt_insert_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) + test_state_trie(Default::default(), nibbles_64(0xDEF), test_account_2()) } #[test] @@ -233,6 +234,7 @@ fn test_state_trie( let hash = H256::from_uint(&interpreter.stack()[1]); state_trie.insert(k, rlp::encode(&account).to_vec()); + let expected_state_trie_hash = state_trie.hash(); assert_eq!(hash, expected_state_trie_hash); diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index a3ba330244..e5dbc3e7b4 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -35,7 +35,7 @@ pub mod mpt; pub(crate) mod prover_input; pub(crate) mod rlp; pub(crate) mod state; -mod trie_extractor; +pub(crate) mod trie_extractor; use self::mpt::{load_all_mpts, TrieRootPtrs}; use crate::witness::util::mem_write_log; @@ -296,121 +296,14 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn _simulate_cpu, const D: usize>( - state: &mut GenerationState, -) -> anyhow::Result<()> { - let halt_pc = KERNEL.global_labels["halt"]; - - loop { - // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. - let pc = state.registers.program_counter; - let halt = state.registers.is_kernel && pc == halt_pc; - if halt { - log::info!("CPU halted after {} cycles", state.traces.clock()); - - // Padding - let mut row = CpuColumnsView::::default(); - row.clock = F::from_canonical_usize(state.traces.clock()); - row.context = F::from_canonical_usize(state.registers.context); - row.program_counter = F::from_canonical_usize(pc); - row.is_kernel_mode = F::ONE; - row.gas = F::from_canonical_u64(state.registers.gas_used); - row.stack_len = F::from_canonical_usize(state.registers.stack_len); - - loop { - state.traces.push_cpu(row); - row.clock += F::ONE; - if state.traces.clock().is_power_of_two() { - break; - } - } - - log::info!("CPU trace padded to {} cycles", state.traces.clock()); - - return Ok(()); - } - - transition(state)?; - } -} - -fn __simulate_cpu, const D: usize>( - state: &mut GenerationState, -) -> anyhow::Result<()> { - let mut profiling_map = HashMap::::new(); - - let halt_pc = KERNEL.global_labels["halt"]; - - loop { - // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. - let pc = state.registers.program_counter; - if let Ok(idx) = KERNEL - .ordered_labels - .binary_search_by_key(&pc, |label| KERNEL.global_labels[label]) - { - profiling_map - .entry(KERNEL.ordered_labels[idx].clone()) - .and_modify(|counter| *counter += 1) - .or_insert(1); - } - let halt = state.registers.is_kernel && pc == halt_pc; - if halt { - log::info!("CPU halted after {} cycles", state.traces.clock()); - - // Padding - let mut row = CpuColumnsView::::default(); - row.clock = F::from_canonical_usize(state.traces.clock()); - row.context = F::from_canonical_usize(state.registers.context); - row.program_counter = F::from_canonical_usize(pc); - row.is_kernel_mode = F::ONE; - row.gas = F::from_canonical_u64(state.registers.gas_used); - row.stack_len = F::from_canonical_usize(state.registers.stack_len); - - loop { - state.traces.push_cpu(row); - row.clock += F::ONE; - if state.traces.clock().is_power_of_two() { - break; - } - } - log::info!("CPU trace padded to {} cycles", state.traces.clock()); - - let mut sorted_labels: Vec<_> = profiling_map.iter().collect(); - sorted_labels.sort_unstable_by_key(|item| item.1); - sorted_labels.reverse(); - log::info!("Offsets: {:?}", sorted_labels); - - return Ok(()); - } - - transition(state)?; - } -} - fn simulate_cpu, const D: usize>( state: &mut GenerationState, ) -> anyhow::Result<()> { - let mut profiling_map = HashMap::::new(); - let halt_pc = KERNEL.global_labels["halt"]; loop { // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. let pc = state.registers.program_counter; - let idx = match KERNEL - .ordered_labels - .binary_search_by_key(&pc, |label| KERNEL.global_labels[label]) - { - Ok(idx) => Some(idx), - Err(0) => None, - Err(idx) => Some(idx - 1), - }; - if let Some(idx) = idx { - profiling_map - .entry(KERNEL.ordered_labels[idx].clone()) - .and_modify(|counter| *counter += 1) - .or_insert(1); - } let halt = state.registers.is_kernel && pc == halt_pc; if halt { log::info!("CPU halted after {} cycles", state.traces.clock()); @@ -431,679 +324,12 @@ fn simulate_cpu, const D: usize>( break; } } - log::info!("CPU trace padded to {} cycles", state.traces.clock()); - let mut sorted_labels: Vec<_> = profiling_map.iter().collect(); - sorted_labels.sort_unstable_by_key(|item| item.1); - sorted_labels.reverse(); - log::info!("Offsets: {:?}", sorted_labels); + log::info!("CPU trace padded to {} cycles", state.traces.clock()); return Ok(()); } transition(state)?; - { - let _ = [ - ("secp_add_valid_points_no_edge_case", 10980), - ("ecrecover", 9009), - ("num_bytes", 7306), - ("hex_prefix_rlp", 5820), - ("secp_double", 4076), - ("encode_node_branch", 3408), - ("mstore_unpacking", 2368), - ("main", 2320), - ("secp_add_valid_points", 2281), - ("insert_accessed_addresses", 2238), - ("decode_int_given_len", 1809), - ("read_rlp_to_memory", 1626), - ("load_mpt", 1355), - ("encode_or_hash_node", 1160), - ("mpt_read_branch", 1152), - ("mpt_read", 1065), - ("encode_node", 803), - ("memcpy_bytes", 731), - ("encode_account", 662), - ("prepend_rlp_list_prefix", 602), - ("pack_small_rlp", 590), - ("secp_precompute_table", 477), - ("encode_node_leaf", 459), - ("mstore_unpacking_rlp", 448), - ("maybe_hash_node", 438), - ("encode_node_empty", 413), - ("encode_rlp_fixed", 380), - ("insert_touched_addresses", 368), - ("mpt_read_extension_not_found", 340), - ("mpt_read_state_trie", 323), - ("sys_sstore", 313), - ("debug_it_should_be_128", 295), - ("process_receipt", 292), - ("process_type_0_txn", 283), - ("encode_rlp_scalar", 271), - ("check_bloom_loop", 269), - ("mpt_insert_hash_node", 252), - ("initialize_block_bloom", 247), - ("encode_rlp_list_prefix", 221), - ("encode_rlp_multi_byte_string_prefix", 216), - ("mpt_load_state_trie_value", 213), - ("mload_packing", 204), - ("mpt_hash", 198), - ("decode_rlp_string_len", 197), - ("jumpdest_analysis", 164), - ("load_code", 155), - ("process_normalized_txn", 154), - ("secp_glv_decompose", 148), - ("process_message_txn_code_loaded", 145), - ("insert_accessed_storage_keys", 135), - ("delete_all_touched_addresses", 128), - ("mpt_read_leaf_not_found", 119), - ("encode_receipt", 113), - ("increment_nonce", 108), - ("add_eth", 93), - ("process_message_txn", 82), - ("process_message_txn_after_call", 77), - ("doubly_encode_rlp_scalar", 74), - ("deduct_eth", 72), - ("intrinsic_gas", 64), - ("hash_final_tries", 58), - ("update_txn_trie", 58), - ("terminate_common", 53), - ("extcodehash", 45), - ("warm_precompiles", 45), - ("sys_stop", 42), - ("start_txn", 41), - ("mpt_insert", 39), - ("load_all_mpts", 38), - ("sload_current", 36), - ("encode_txn", 36), - ("scalar_to_rlp", 35), - ("encode_rlp_string", 34), - ("hash_initial_tries", 33), - ("encode_node_branch_prepend_prefix", 32), - ("decode_rlp_scalar", 28), - ("encode_rlp_string_small", 24), - ("route_txn", 24), - ("mpt_hash_storage_trie", 24), - ("encode_rlp_string_large", 23), - ("buy_gas", 20), - ("mpt_insert_receipt_trie", 17), - ("transfer_eth", 17), - ("decode_rlp_list_len", 17), - ("mpt_insert_txn_trie", 16), - ("balance", 15), - ("mpt_hash_txn_trie", 14), - ("mpt_hash_receipt_trie", 14), - ("mpt_hash_state_trie", 14), - ("logs_bloom", 13), - ("delete_all_selfdestructed_addresses", 13), - ("encode_rlp_string_large_after_writing_len", 13), - ("txn_after", 12), - ("encode_rlp_256", 12), - ("encode_storage_value", 12), - ("increment_bounded_rlp", 11), - ("withdrawals", 10), - ("warm_coinbase", 9), - ("nonce", 9), - ("increment_sender_nonce", 9), - ("process_based_on_type", 8), - ("after_storage_read", 7), - ("mpt_read_empty", 7), - ("ec_double_retself", 6), - ("warm_origin", 5), - ("add_bignum", 5), - ("check_bloom_loop_end", 4), - ("execute_withdrawals", 3), - ("encode_rlp_160", 3), - ("charge_gas_hook", 2), - ("halt", 1), - ("jumped_to_0", 1), - ]; - let _ = [ - ("secp_add_valid_points_no_edge_case", 10980), - ("ecrecover", 9009), - ("num_bytes", 7306), - ("hex_prefix_rlp", 5820), - ("secp_double", 4076), - ("encode_node_branch", 3408), - ("mstore_unpacking", 2368), - ("main", 2306), - ("secp_add_valid_points", 2281), - ("insert_accessed_addresses", 2238), - ("decode_int_given_len", 1809), - ("encode_node_empty", 1652), - ("read_rlp_to_memory", 1626), - ("load_mpt", 1355), - ("encode_or_hash_node", 1160), - ("mpt_read_branch", 1152), - ("mpt_read", 1065), - ("encode_node", 803), - ("memcpy_bytes", 731), - ("encode_account", 662), - ("prepend_rlp_list_prefix", 602), - ("pack_small_rlp", 590), - ("secp_precompute_table", 477), - ("encode_node_leaf", 459), - ("mstore_unpacking_rlp", 448), - ("maybe_hash_node", 438), - ("encode_rlp_fixed", 380), - ("insert_touched_addresses", 368), - ("mpt_read_extension_not_found", 340), - ("mpt_read_state_trie", 323), - ("sys_sstore", 313), - ("hash_final_tries", 305), - ("process_receipt", 292), - ("process_type_0_txn", 283), - ("encode_rlp_scalar", 271), - ("check_bloom_loop", 269), - ("mpt_insert_hash_node", 252), - ("encode_rlp_list_prefix", 221), - ("encode_rlp_multi_byte_string_prefix", 216), - ("mpt_load_state_trie_value", 213), - ("mload_packing", 204), - ("mpt_hash", 198), - ("decode_rlp_string_len", 197), - ("jumpdest_analysis", 164), - ("load_code", 155), - ("process_normalized_txn", 154), - ("secp_glv_decompose", 148), - ("process_message_txn_code_loaded", 145), - ("insert_accessed_storage_keys", 135), - ("delete_all_touched_addresses", 128), - ("mpt_read_leaf_not_found", 119), - ("encode_receipt", 113), - ("increment_nonce", 108), - ("add_eth", 93), - ("process_message_txn", 82), - ("process_message_txn_after_call", 77), - ("doubly_encode_rlp_scalar", 74), - ("deduct_eth", 72), - ("intrinsic_gas", 64), - ("update_txn_trie", 58), - ("terminate_common", 53), - ("warm_precompiles", 45), - ("extcodehash", 45), - ("sys_stop", 42), - ("start_txn", 41), - ("mpt_insert", 39), - ("load_all_mpts", 38), - ("sload_current", 36), - ("encode_txn", 36), - ("scalar_to_rlp", 35), - ("encode_rlp_string", 34), - ("hash_initial_tries", 33), - ("encode_node_branch_prepend_prefix", 32), - ("decode_rlp_scalar", 28), - ("route_txn", 24), - ("mpt_hash_storage_trie", 24), - ("encode_rlp_string_small", 24), - ("encode_rlp_string_large", 23), - ("buy_gas", 20), - ("decode_rlp_list_len", 17), - ("transfer_eth", 17), - ("mpt_insert_receipt_trie", 17), - ("mpt_insert_txn_trie", 16), - ("balance", 15), - ("mpt_hash_receipt_trie", 14), - ("mpt_hash_txn_trie", 14), - ("mpt_hash_state_trie", 14), - ("delete_all_selfdestructed_addresses", 13), - ("logs_bloom", 13), - ("encode_rlp_string_large_after_writing_len", 13), - ("encode_storage_value", 12), - ("encode_rlp_256", 12), - ("txn_after", 12), - ("increment_bounded_rlp", 11), - ("withdrawals", 10), - ("nonce", 9), - ("increment_sender_nonce", 9), - ("warm_coinbase", 9), - ("process_based_on_type", 8), - ("after_storage_read", 7), - ("mpt_read_empty", 7), - ("ec_double_retself", 6), - ("add_bignum", 5), - ("warm_origin", 5), - ("check_bloom_loop_end", 4), - ("encode_rlp_160", 3), - ("execute_withdrawals", 3), - ("charge_gas_hook", 2), - ("halt", 1), - ("jumped_to_0", 1), - ]; - - let _ = [ - ("mstore_unpacking", 148), - ("secp_add_valid_points", 139), - ("secp_add_valid_points_no_edge_case", 132), - ("secp_double", 129), - ("mstore_unpacking_rlp", 112), - ("encode_or_hash_node", 76), - ("encode_node", 72), - ("maybe_hash_node", 72), - ("mload_packing", 68), - ("pack_small_rlp", 59), - ("encode_node_empty", 59), - ("mpt_read", 50), - ("num_bytes", 48), - ("load_mpt", 38), - ("mpt_read_branch", 32), - ("memcpy_bytes", 27), - ("encode_rlp_fixed", 20), - ("encode_rlp_scalar", 19), - ("mpt_read_state_trie", 17), - ("prepend_rlp_list_prefix", 14), - ("encode_rlp_256", 12), - ("mpt_hash", 12), - ("insert_accessed_addresses", 12), - ("decode_rlp_string_len", 9), - ("hex_prefix_rlp", 9), - ("encode_node_leaf", 9), - ("decode_int_given_len", 9), - ("encode_rlp_multi_byte_string_prefix", 8), - ("encode_rlp_list_prefix", 8), - ("decode_rlp_scalar", 7), - ("mpt_hash_storage_trie", 6), - ("encode_account", 6), - ("insert_touched_addresses", 5), - ("encode_node_branch_prepend_prefix", 4), - ("encode_node_branch", 4), - ("mpt_load_state_trie_value", 3), - ("extcodehash", 3), - ("mpt_insert", 3), - ("add_eth", 3), - ("mpt_hash_state_trie", 2), - ("encode_rlp_string", 2), - ("deduct_eth", 2), - ("mpt_hash_txn_trie", 2), - ("ec_double_retself", 2), - ("mpt_hash_receipt_trie", 2), - ("secp_glv_decompose", 2), - ("charge_gas_hook", 2), - ("sys_sstore", 1), - ("increment_sender_nonce", 1), - ("buy_gas", 1), - ("main", 1), - ("process_type_0_txn", 1), - ("jumpdest_analysis", 1), - ("txn_after", 1), - ("encode_rlp_160", 1), - ("intrinsic_gas", 1), - ("delete_all_selfdestructed_addresses", 1), - ("encode_rlp_string_large_after_writing_len", 1), - ("jumped_to_0", 1), - ("decode_rlp_list_len", 1), - ("mpt_read_empty", 1), - ("hash_final_tries", 1), - ("sload_current", 1), - ("encode_txn", 1), - ("start_txn", 1), - ("encode_rlp_string_large", 1), - ("load_code", 1), - ("increment_bounded_rlp", 1), - ("encode_receipt", 1), - ("process_message_txn", 1), - ("ecrecover", 1), - ("warm_origin", 1), - ("encode_rlp_string_small", 1), - ("process_based_on_type", 1), - ("secp_precompute_table", 1), - ("halt", 1), - ("update_txn_trie", 1), - ("transfer_eth", 1), - ("logs_bloom", 1), - ("read_rlp_to_memory", 1), - ("encode_storage_value", 1), - ("process_receipt", 1), - ("process_message_txn_code_loaded", 1), - ("increment_nonce", 1), - ("delete_all_touched_addresses", 1), - ("terminate_common", 1), - ("balance", 1), - ("withdrawals", 1), - ("sys_stop", 1), - ("after_storage_read", 1), - ("mpt_insert_receipt_trie", 1), - ("hash_initial_tries", 1), - ("doubly_encode_rlp_scalar", 1), - ("route_txn", 1), - ("mpt_insert_txn_trie", 1), - ("warm_coinbase", 1), - ("load_all_mpts", 1), - ("warm_precompiles", 1), - ("add_bignum", 1), - ("insert_accessed_storage_keys", 1), - ("process_normalized_txn", 1), - ("scalar_to_rlp", 1), - ("nonce", 1), - ("process_message_txn_after_call", 1), - ("execute_withdrawals", 1), - ]; - let _ = [ - ("secp_add_valid_points_no_edge_case", 10980), - ("ecrecover", 9009), - ("num_bytes", 7306), - ("hex_prefix_rlp", 5820), - ("secp_double", 4076), - ("encode_node_branch", 3440), - ("encode_or_hash_node", 2991), - ("mstore_unpacking", 2368), - ("main", 2306), - ("secp_add_valid_points", 2281), - ("insert_accessed_addresses", 2238), - ("decode_int_given_len", 1809), - ("encode_node_empty", 1652), - ("read_rlp_to_memory", 1626), - ("load_mpt", 1355), - ("mpt_read_branch", 1152), - ("mpt_read", 1065), - ("memcpy_bytes", 731), - ("encode_account", 662), - ("prepend_rlp_list_prefix", 602), - ("hash_final_tries", 578), - ("secp_precompute_table", 477), - ("encode_node_leaf", 459), - ("mstore_unpacking_rlp", 448), - ("encode_rlp_fixed", 380), - ("insert_touched_addresses", 368), - ("mpt_read_extension_not_found", 340), - ("mpt_read_state_trie", 323), - ("sys_sstore", 313), - ("process_receipt", 292), - ("process_type_0_txn", 283), - ("encode_rlp_scalar", 271), - ("mpt_insert_hash_node", 252), - ("encode_rlp_list_prefix", 221), - ("encode_rlp_multi_byte_string_prefix", 216), - ("mpt_load_state_trie_value", 213), - ("mload_packing", 204), - ("mpt_hash", 198), - ("decode_rlp_string_len", 197), - ("jumpdest_analysis", 164), - ("load_code", 155), - ("process_normalized_txn", 154), - ("secp_glv_decompose", 148), - ("process_message_txn_code_loaded", 145), - ("insert_accessed_storage_keys", 135), - ("delete_all_touched_addresses", 128), - ("mpt_read_leaf_not_found", 119), - ("encode_receipt", 113), - ("increment_nonce", 108), - ("add_eth", 93), - ("process_message_txn", 82), - ("process_message_txn_after_call", 77), - ("doubly_encode_rlp_scalar", 74), - ("deduct_eth", 72), - ("intrinsic_gas", 64), - ("update_txn_trie", 58), - ("terminate_common", 53), - ("warm_precompiles", 45), - ("extcodehash", 45), - ("sys_stop", 42), - ("start_txn", 41), - ("mpt_insert", 39), - ("load_all_mpts", 38), - ("encode_txn", 36), - ("sload_current", 36), - ("scalar_to_rlp", 35), - ("encode_rlp_string", 34), - ("hash_initial_tries", 33), - ("decode_rlp_scalar", 28), - ("route_txn", 24), - ("mpt_hash_storage_trie", 24), - ("encode_rlp_string_small", 24), - ("encode_rlp_string_large", 23), - ("buy_gas", 20), - ("transfer_eth", 17), - ("mpt_insert_receipt_trie", 17), - ("decode_rlp_list_len", 17), - ("mpt_insert_txn_trie", 16), - ("balance", 15), - ("mpt_hash_txn_trie", 14), - ("mpt_hash_receipt_trie", 14), - ("mpt_hash_state_trie", 14), - ("delete_all_selfdestructed_addresses", 13), - ("logs_bloom", 13), - ("encode_rlp_string_large_after_writing_len", 13), - ("encode_storage_value", 12), - ("encode_rlp_256", 12), - ("txn_after", 12), - ("increment_bounded_rlp", 11), - ("withdrawals", 10), - ("increment_sender_nonce", 9), - ("warm_coinbase", 9), - ("nonce", 9), - ("process_based_on_type", 8), - ("after_storage_read", 7), - ("mpt_read_empty", 7), - ("ec_double_retself", 6), - ("add_bignum", 5), - ("warm_origin", 5), - ("encode_rlp_160", 3), - ("execute_withdrawals", 3), - ("charge_gas_hook", 2), - ("jumped_to_0", 1), - ("halt", 1), - ]; - - let _ = [ - ("secp_add_valid_points_no_edge_case", 10980), - ("ecrecover", 9009), - ("num_bytes", 7306), - ("hex_prefix_rlp", 5820), - ("secp_double", 4076), - ("encode_node_branch", 3440), - ("mstore_unpacking", 2368), - ("main", 2306), - ("secp_add_valid_points", 2281), - ("insert_accessed_addresses", 2238), - ("decode_int_given_len", 1809), - ("encode_node_empty", 1652), - ("read_rlp_to_memory", 1626), - ("load_mpt", 1355), - ("encode_or_hash_node", 1160), - ("mpt_read_branch", 1152), - ("mpt_read", 1065), - ("encode_node", 803), - ("memcpy_bytes", 731), - ("encode_account", 662), - ("prepend_rlp_list_prefix", 602), - ("pack_small_rlp", 590), - ("hash_final_tries", 578), - ("secp_precompute_table", 477), - ("encode_node_leaf", 459), - ("mstore_unpacking_rlp", 448), - ("maybe_hash_node", 438), - ("encode_rlp_fixed", 380), - ("insert_touched_addresses", 368), - ("mpt_read_extension_not_found", 340), - ("mpt_read_state_trie", 323), - ("sys_sstore", 313), - ("process_receipt", 292), - ("process_type_0_txn", 283), - ("encode_rlp_scalar", 271), - ("mpt_insert_hash_node", 252), - ("encode_rlp_list_prefix", 221), - ("encode_rlp_multi_byte_string_prefix", 216), - ("mpt_load_state_trie_value", 213), - ("mload_packing", 204), - ("mpt_hash", 198), - ("decode_rlp_string_len", 197), - ("jumpdest_analysis", 164), - ("load_code", 155), - ("process_normalized_txn", 154), - ("secp_glv_decompose", 148), - ("process_message_txn_code_loaded", 145), - ("insert_accessed_storage_keys", 135), - ("delete_all_touched_addresses", 128), - ("mpt_read_leaf_not_found", 119), - ("encode_receipt", 113), - ("increment_nonce", 108), - ("add_eth", 93), - ("process_message_txn", 82), - ("process_message_txn_after_call", 77), - ("doubly_encode_rlp_scalar", 74), - ("deduct_eth", 72), - ("intrinsic_gas", 64), - ("update_txn_trie", 58), - ("terminate_common", 53), - ("extcodehash", 45), - ("warm_precompiles", 45), - ("sys_stop", 42), - ("start_txn", 41), - ("mpt_insert", 39), - ("load_all_mpts", 38), - ("encode_txn", 36), - ("sload_current", 36), - ("scalar_to_rlp", 35), - ("encode_rlp_string", 34), - ("hash_initial_tries", 33), - ("decode_rlp_scalar", 28), - ("route_txn", 24), - ("mpt_hash_storage_trie", 24), - ("encode_rlp_string_small", 24), - ("encode_rlp_string_large", 23), - ("buy_gas", 20), - ("decode_rlp_list_len", 17), - ("transfer_eth", 17), - ("mpt_insert_receipt_trie", 17), - ("mpt_insert_txn_trie", 16), - ("balance", 15), - ("mpt_hash_txn_trie", 14), - ("mpt_hash_receipt_trie", 14), - ("mpt_hash_state_trie", 14), - ("encode_rlp_string_large_after_writing_len", 13), - ("logs_bloom", 13), - ("delete_all_selfdestructed_addresses", 13), - ("txn_after", 12), - ("encode_storage_value", 12), - ("encode_rlp_256", 12), - ("increment_bounded_rlp", 11), - ("withdrawals", 10), - ("nonce", 9), - ("increment_sender_nonce", 9), - ("warm_coinbase", 9), - ("process_based_on_type", 8), - ("mpt_read_empty", 7), - ("after_storage_read", 7), - ("ec_double_retself", 6), - ("add_bignum", 5), - ("warm_origin", 5), - ("encode_rlp_160", 3), - ("execute_withdrawals", 3), - ("charge_gas_hook", 2), - ("halt", 1), - ("jumped_to_0", 1), - ]; - - let _ = [ - ("secp_add_valid_points_no_edge_case", 10980), - ("ecrecover", 9009), - ("num_bytes", 7306), - ("hex_prefix_rlp", 5820), - ("secp_double", 4076), - ("encode_node_branch", 3408), - ("mstore_unpacking", 2368), - ("main", 2306), - ("secp_add_valid_points", 2281), - ("insert_accessed_addresses", 2238), - ("decode_int_given_len", 1809), - ("encode_node_empty", 1652), - ("read_rlp_to_memory", 1626), - ("load_mpt", 1355), - ("encode_or_hash_node", 1160), - ("mpt_read_branch", 1152), - ("mpt_read", 1065), - ("encode_node", 803), - ("memcpy_bytes", 731), - ("encode_account", 662), - ("prepend_rlp_list_prefix", 602), - ("pack_small_rlp", 590), - ("hash_final_tries", 578), - ("secp_precompute_table", 477), - ("encode_node_leaf", 459), - ("mstore_unpacking_rlp", 448), - ("maybe_hash_node", 438), - ("encode_rlp_fixed", 380), - ("insert_touched_addresses", 368), - ("mpt_read_extension_not_found", 340), - ("mpt_read_state_trie", 323), - ("sys_sstore", 313), - ("process_receipt", 292), - ("process_type_0_txn", 283), - ("encode_rlp_scalar", 271), - ("mpt_insert_hash_node", 252), - ("encode_rlp_list_prefix", 221), - ("encode_rlp_multi_byte_string_prefix", 216), - ("mpt_load_state_trie_value", 213), - ("mload_packing", 204), - ("mpt_hash", 198), - ("decode_rlp_string_len", 197), - ("jumpdest_analysis", 164), - ("load_code", 155), - ("process_normalized_txn", 154), - ("secp_glv_decompose", 148), - ("process_message_txn_code_loaded", 145), - ("insert_accessed_storage_keys", 135), - ("delete_all_touched_addresses", 128), - ("mpt_read_leaf_not_found", 119), - ("encode_receipt", 113), - ("increment_nonce", 108), - ("add_eth", 93), - ("process_message_txn", 82), - ("process_message_txn_after_call", 77), - ("doubly_encode_rlp_scalar", 74), - ("deduct_eth", 72), - ("intrinsic_gas", 64), - ("update_txn_trie", 58), - ("terminate_common", 53), - ("extcodehash", 45), - ("warm_precompiles", 45), - ("sys_stop", 42), - ("start_txn", 41), - ("mpt_insert", 39), - ("load_all_mpts", 38), - ("encode_txn", 36), - ("sload_current", 36), - ("scalar_to_rlp", 35), - ("encode_rlp_string", 34), - ("hash_initial_tries", 33), - ("encode_node_branch_prepend_prefix", 32), - ("decode_rlp_scalar", 28), - ("mpt_hash_storage_trie", 24), - ("route_txn", 24), - ("encode_rlp_string_small", 24), - ("encode_rlp_string_large", 23), - ("buy_gas", 20), - ("transfer_eth", 17), - ("mpt_insert_receipt_trie", 17), - ("decode_rlp_list_len", 17), - ("mpt_insert_txn_trie", 16), - ("balance", 15), - ("mpt_hash_txn_trie", 14), - ("mpt_hash_state_trie", 14), - ("mpt_hash_receipt_trie", 14), - ("delete_all_selfdestructed_addresses", 13), - ("logs_bloom", 13), - ("encode_rlp_string_large_after_writing_len", 13), - ("txn_after", 12), - ("encode_rlp_256", 12), - ("encode_storage_value", 12), - ("increment_bounded_rlp", 11), - ("withdrawals", 10), - ("warm_coinbase", 9), - ("increment_sender_nonce", 9), - ("nonce", 9), - ("process_based_on_type", 8), - ("mpt_read_empty", 7), - ("after_storage_read", 7), - ("ec_double_retself", 6), - ("add_bignum", 5), - ("warm_origin", 5), - ("encode_rlp_160", 3), - ("execute_withdrawals", 3), - ("charge_gas_hook", 2), - ("halt", 1), - ("jumped_to_0", 1), - ]; - } } } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 6e67e61942..48a1722214 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -1,3 +1,5 @@ +use ethereum_types::U256; + #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum Segment { /// Contains EVM bytecode. @@ -29,46 +31,42 @@ pub(crate) enum Segment { RlpRaw = 12, /// Contains all trie data. It is owned by the kernel, so it only lives on context 0. TrieData = 13, - /// A buffer used to store the encodings of a branch node's children. - TrieEncodedChild = 14, - /// A buffer used to store the lengths of the encodings of a branch node's children. - TrieEncodedChildLen = 15, /// A table of values 2^i for i=0..255 for use with shift /// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`. - ShiftTable = 16, - JumpdestBits = 17, - EcdsaTable = 18, - BnWnafA = 19, - BnWnafB = 20, - BnTableQ = 21, - BnPairing = 22, + ShiftTable = 14, + JumpdestBits = 15, + EcdsaTable = 16, + BnWnafA = 17, + BnWnafB = 18, + BnTableQ = 19, + BnPairing = 20, /// List of addresses that have been accessed in the current transaction. - AccessedAddresses = 23, + AccessedAddresses = 21, /// List of storage keys that have been accessed in the current transaction. - AccessedStorageKeys = 24, + AccessedStorageKeys = 22, /// List of addresses that have called SELFDESTRUCT in the current transaction. - SelfDestructList = 25, + SelfDestructList = 23, /// Contains the bloom filter of a transaction. - TxnBloom = 26, + TxnBloom = 24, /// Contains the bloom filter present in the block header. - GlobalBlockBloom = 27, + GlobalBlockBloom = 25, /// List of log pointers pointing to the LogsData segment. - Logs = 28, - LogsData = 29, + Logs = 26, + LogsData = 27, /// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`. - Journal = 30, - JournalData = 31, - JournalCheckpoints = 32, + Journal = 28, + JournalData = 29, + JournalCheckpoints = 30, /// List of addresses that have been touched in the current transaction. - TouchedAddresses = 33, + TouchedAddresses = 31, /// List of checkpoints for the current context. Length in `ContextMetadata`. - ContextCheckpoints = 34, + ContextCheckpoints = 32, /// List of 256 previous block hashes. - BlockHashes = 35, + BlockHashes = 33, } impl Segment { - pub(crate) const COUNT: usize = 36; + pub(crate) const COUNT: usize = 34; pub(crate) const fn all() -> [Self; Self::COUNT] { [ @@ -86,8 +84,6 @@ impl Segment { Self::TxnData, Self::RlpRaw, Self::TrieData, - Self::TrieEncodedChild, - Self::TrieEncodedChildLen, Self::ShiftTable, Self::JumpdestBits, Self::EcdsaTable, @@ -128,8 +124,6 @@ impl Segment { Segment::TxnData => "SEGMENT_TXN_DATA", Segment::RlpRaw => "SEGMENT_RLP_RAW", Segment::TrieData => "SEGMENT_TRIE_DATA", - Segment::TrieEncodedChild => "SEGMENT_TRIE_ENCODED_CHILD", - Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN", Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", @@ -169,8 +163,6 @@ impl Segment { Segment::TxnData => 8, Segment::RlpRaw => 8, Segment::TrieData => 256, - Segment::TrieEncodedChild => 256, - Segment::TrieEncodedChildLen => 6, Segment::ShiftTable => 256, Segment::JumpdestBits => 1, Segment::EcdsaTable => 256, @@ -193,4 +185,17 @@ impl Segment { Segment::BlockHashes => 256, } } + + pub(crate) fn constant(&self, virt: usize) -> Option { + match self { + Segment::RlpRaw => { + if virt == 0xFFFFFFFF { + Some(U256::from(0x80)) + } else { + None + } + } + _ => None, + } + } } diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index 8cb7daf704..f39073c0ef 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -177,6 +177,11 @@ impl MemoryState { } let segment = Segment::all()[address.segment]; + + if let Some(constant) = Segment::constant(&segment, address.virt) { + return constant; + } + let val = self.contexts[address.context].segments[address.segment].get(address.virt); assert!( val.bits() <= segment.bit_range(), @@ -194,6 +199,15 @@ impl MemoryState { } let segment = Segment::all()[address.segment]; + + if let Some(constant) = Segment::constant(&segment, address.virt) { + assert!( + constant == val, + "Attempting to set constant {} to incorrect value", + address.virt + ); + return; + } assert!( val.bits() <= segment.bit_range(), "Value {} exceeds {:?} range of {} bits", From 48b9769e342614b978557a5d771657ca0cd7d05b Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 27 Dec 2023 12:24:12 +0100 Subject: [PATCH 057/175] Remove duplicated label --- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index cf0634fd22..9feb4e1916 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -127,12 +127,6 @@ global encode_node_empty: %stack (cur_len, retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1, cur_len) JUMP -global encode_node_empty: - // stack: node_type, node_payload_ptr, encode_value, retdest - %pop3 - %stack (retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1) - JUMP - global encode_node_branch: // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest POP From 9c573a07d4da1aa35ebb2039a443aab31fff9018 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 28 Dec 2023 14:10:36 +0100 Subject: [PATCH 058/175] Restore simple_transfer and Clippy --- evm/tests/simple_transfer.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index b5c43ff893..5fd252df45 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -154,9 +154,6 @@ fn test_simple_transfer() -> anyhow::Result<()> { }, }; - let bytes = std::fs::read("jumpi_d18g0v0_Shanghai.json").unwrap(); - let inputs = serde_json::from_slice(&bytes).unwrap(); - let mut timing = TimingTree::new("prove", log::Level::Debug); let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); From 1a95f7aa7222d30012a82c29eb241c5d2640e33d Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 28 Dec 2023 16:39:12 +0100 Subject: [PATCH 059/175] Clippy --- evm/src/generation/prover_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 60192c74fe..d7d3082c11 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -397,7 +397,7 @@ fn get_proofs_and_jumpdests( ) -> Vec { const PUSH1_OPCODE: u8 = 0x60; const PUSH32_OPCODE: u8 = 0x7f; - let (proofs, _) = CodeIterator::until(&code, largest_address + 1).fold( + let (proofs, _) = CodeIterator::until(code, largest_address + 1).fold( (vec![], 0), |(mut proofs, acc), (pos, opcode)| { let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { From ef07eabf845e36c242109dd1ba199d00ad1d8a4b Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:08:19 +0100 Subject: [PATCH 060/175] Pacify latest clippy (#1442) --- evm/src/cpu/columns/general.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/cpu/columns/general.rs b/evm/src/cpu/columns/general.rs index 1d631243ce..08b6c82dd1 100644 --- a/evm/src/cpu/columns/general.rs +++ b/evm/src/cpu/columns/general.rs @@ -76,6 +76,7 @@ impl CpuGeneralColumnsView { } impl PartialEq for CpuGeneralColumnsView { + #[allow(clippy::unconditional_recursion)] // false positive fn eq(&self, other: &Self) -> bool { let self_arr: &[T; NUM_SHARED_COLUMNS] = self.borrow(); let other_arr: &[T; NUM_SHARED_COLUMNS] = other.borrow(); From 6cf4df7da2ad9f7d3c830c2c30bddad6e12a7de0 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:11:38 +0100 Subject: [PATCH 061/175] Add initial constraint z polynomial (#1440) --- evm/src/lookup.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index 7944d78b70..8bf0c8f61b 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -210,6 +210,8 @@ pub(crate) fn eval_packed_lookups_generic Date: Tue, 2 Jan 2024 13:47:09 +0100 Subject: [PATCH 062/175] Prevent some lints from being allowed (#1443) --- evm/src/cpu/kernel/tests/blake2_f.rs | 4 +++- evm/src/lib.rs | 2 -- evm/tests/log_opcode.rs | 2 -- util/src/lib.rs | 3 --- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/evm/src/cpu/kernel/tests/blake2_f.rs b/evm/src/cpu/kernel/tests/blake2_f.rs index b12c9f32a6..c5d800c5b6 100644 --- a/evm/src/cpu/kernel/tests/blake2_f.rs +++ b/evm/src/cpu/kernel/tests/blake2_f.rs @@ -5,6 +5,8 @@ use crate::cpu::kernel::interpreter::{ }; use crate::memory::segments::Segment::KernelGeneral; +type ConvertedBlakeInputs = (u32, [u64; 8], [u64; 16], u64, u64, bool); + fn reverse_bytes_u64(input: u64) -> u64 { let mut result = 0; for i in 0..8 { @@ -13,7 +15,7 @@ fn reverse_bytes_u64(input: u64) -> u64 { result } -fn convert_input(input: &str) -> Result<(u32, [u64; 8], [u64; 16], u64, u64, bool)> { +fn convert_input(input: &str) -> Result { let rounds = u32::from_str_radix(&input[..8], 16).unwrap(); let mut h = [0u64; 8]; diff --git a/evm/src/lib.rs b/evm/src/lib.rs index f3bd0989f1..42547c3eeb 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -1,7 +1,5 @@ -#![allow(incomplete_features)] #![allow(clippy::needless_range_loop)] #![allow(clippy::too_many_arguments)] -#![allow(clippy::type_complexity)] #![allow(clippy::field_reassign_with_default)] #![allow(unused)] #![feature(let_chains)] diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 157b9fe6ad..b23b4402f9 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -1,5 +1,3 @@ -#![allow(clippy::upper_case_acronyms)] - use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; diff --git a/util/src/lib.rs b/util/src/lib.rs index 6cd7ffb021..6c8b2ed586 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -1,6 +1,3 @@ -#![allow(clippy::new_without_default)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::type_complexity)] #![allow(clippy::needless_range_loop)] #![no_std] From ab4508fc8b2cef8d93458b208269586a1e900037 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 16:07:33 +0100 Subject: [PATCH 063/175] Add packed verification --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 148 ++++++++++++++++-- evm/src/cpu/kernel/asm/util/basic_macros.asm | 2 +- 2 files changed, 139 insertions(+), 11 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 8debead3bb..8625683a4b 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -111,25 +111,151 @@ global write_table_if_jumpdest: MLOAD_GENERAL // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - %jump_eq_const(0x5b, return) + %jump_neq_const(0x5b, return) //stack: jumpdest, ctx, proof_prefix_addr, retdest SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest ISZERO %jumpi(verify_path_and_write_table) + + // stack: proof_prefix_addr, ctx, jumpdest, retdest // If we are here we need to check that the next 32 bytes are less // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, // or larger than 127 - %check_and_step(127) %check_and_step(126) %check_and_step(125) %check_and_step(124) - %check_and_step(123) %check_and_step(122) %check_and_step(121) %check_and_step(120) - %check_and_step(119) %check_and_step(118) %check_and_step(117) %check_and_step(116) - %check_and_step(115) %check_and_step(114) %check_and_step(113) %check_and_step(112) - %check_and_step(111) %check_and_step(110) %check_and_step(109) %check_and_step(108) - %check_and_step(107) %check_and_step(106) %check_and_step(105) %check_and_step(104) - %check_and_step(103) %check_and_step(102) %check_and_step(101) %check_and_step(100) - %check_and_step(99) %check_and_step(98) %check_and_step(97) %check_and_step(96) + + %stack + (proof_prefix_addr, ctx) -> + (ctx, @SEGMENT_CODE, proof_prefix_addr, 32, proof_prefix_addr, ctx) + %mload_packing + // packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP1 %shl_const(1) + DUP2 %shl_const(2) + AND + // stack: (is_1_at_pos_2_and_3|(X)⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // X denotes any value in {0,1} and Z^i is Z repeated i times + NOT + // stack: (is_0_at_2_or_3|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP2 + OR + // stack: (is_1_at_1 or is_0_at_2_or_3|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // stack: (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + + // Compute in_range = + // - (0xFF|X⁷)³² for the first 15 bytes + // - (has_prefix => is_0_at_4 |X⁷)³² for the next 15 bytes + // - (~has_prefix|X⁷)³² for the last byte + // Compute also that ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~hash_prefix + // for the second half but it takes less cycles if we do it. + DUP2 %shl_const(3) + NOT + // stack: (is_0_at_4|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 + AND + // stack: (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP2 + DUP2 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000 + OR + // stack: (in_range|X⁷)³², (is_0_at_4|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + OR + // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + + // Compute in_range' = in_range AND + // - (0xFF|X⁷)³² for bytes in positions 1-7 and 16-23 + // - (has_prefix => is_0_at_5 |X⁷)³² on the rest + // Compute also ~has_prefix = ~has_prefix OR is_0_at_5 for all bytes. + + DUP3 %shl_const(4) + NOT + // stack: (is_0_at_5|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP2 + DUP2 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000 + OR + // stack: (in_range'|X⁷)³², (is_0_at_5|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + OR + // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + AND + SWAP1 + + // Compute in_range' = in_range AND + // - (0xFF|X⁷)³² for bytes in positions 1-2, 8-11, 16-19, and 24-27 + // - (has_prefix => is_0_at_6 |X⁷)³² on the rest + // Compute also that ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. + + // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP3 %shl_const(5) + NOT + // stack: (is_0_at_6|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP2 + DUP2 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF0000000000 + OR + // stack: (in_range'|X⁷)³², (is_0_at_6|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + OR + // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + AND + SWAP1 + + // Compute in_range' = in_range AND + // - (0xFF|X⁷)³² for bytes in positions 1-7 and 16-23 + // - (has_prefix => is_0_at_7 |X⁷)³² on the rest + // Compute also that ~has_prefix = ~has_prefix OR is_0_at_7 for all bytes. + + // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP3 %shl_const(6) + NOT + // stack: (is_0_at_7|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP2 + DUP2 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF000000 + OR + // stack: (in_range'|X⁷)³², (is_0_at_7|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + OR + // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP2 + AND + SWAP1 + + // Compute in_range' = in_range AND + // - (0xFF|X⁷)³² for bytes in odd positions + // - (has_prefix => is_0_at_8 |X⁷)³² on the rest + // Compute also that ~has_prefix = ~has_prefix OR is_0_at_7 for all bytes. + + // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP3 %shl_const(7) + NOT + // stack: (is_0_at_8|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF + OR + AND + // stack: (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + + // Get rid of the irrelevant bits + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 + AND + %assert_eq_const(0x8080808080808080808080808080808080808080808080808080808080808080) + POP // check the remaining path %jump(verify_path_and_write_table) @@ -139,7 +265,9 @@ return: JUMP -// Chek if the opcode pointed by proof_prefix address is + + +// Check if the opcode pointed by proof_prefix address is // less than max and increment proof_prefix_addr %macro check_and_step(max) %stack diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index d62dc27ec7..55debe1257 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -8,7 +8,7 @@ jumpi %endmacro -%macro jump_eq_const(c, jumpdest) +%macro jump_neq_const(c, jumpdest) PUSH $c SUB %jumpi($jumpdest) From 8f1efa155436c2cb2a45faf1dc53aa685a11a1dc Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 16:15:16 +0100 Subject: [PATCH 064/175] Fix minor error --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 8625683a4b..0e92fcacf3 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -256,6 +256,7 @@ global write_table_if_jumpdest: AND %assert_eq_const(0x8080808080808080808080808080808080808080808080808080808080808080) POP + %add_const(32) // check the remaining path %jump(verify_path_and_write_table) From 247d655b3903c94167d3b5960180798239c846dd Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 17:00:32 +0100 Subject: [PATCH 065/175] Minor --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 0e92fcacf3..59fbaa033e 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -146,7 +146,7 @@ global write_table_if_jumpdest: // - (0xFF|X⁷)³² for the first 15 bytes // - (has_prefix => is_0_at_4 |X⁷)³² for the next 15 bytes // - (~has_prefix|X⁷)³² for the last byte - // Compute also that ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~hash_prefix + // Compute also ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~hash_prefix // for the second half but it takes less cycles if we do it. DUP2 %shl_const(3) NOT @@ -237,7 +237,6 @@ global write_table_if_jumpdest: // Compute in_range' = in_range AND // - (0xFF|X⁷)³² for bytes in odd positions // - (has_prefix => is_0_at_8 |X⁷)³² on the rest - // Compute also that ~has_prefix = ~has_prefix OR is_0_at_7 for all bytes. // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest DUP3 %shl_const(7) From f4713c44d3d1dec43ae36b745263aa87078c9398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Fri, 5 Jan 2024 17:18:03 +0100 Subject: [PATCH 066/175] Apply suggestions from code review Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 2 +- evm/src/cpu/kernel/constants/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 9feb4e1916..6d0cf10ccc 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -197,7 +197,7 @@ encode_node_branch_prepend_prefix: // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest + // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest PUSH 160 DUP5 // rlp_pos %mstore_rlp diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index fa25fa8704..1cd99b384d 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -94,7 +94,7 @@ const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ hex!("0000000000000000000000000000000100000000000000000000000000000000"), ), // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is - // equal to usize::MAX so that all rlp pointers all much smalled than that + // equal to usize::MAX so that all rlp pointers all much smaller than that. ( "ENCODED_EMPTY_NODE_POS", hex!("00000000000000000000000000000000000000000000000000000000FFFFFFFF"), From 1c994737ef025117333f6f0d5745d44a35a1d824 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 17:30:59 +0100 Subject: [PATCH 067/175] Address comments --- evm/src/cpu/kernel/asm/main.asm | 3 --- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 2 +- evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm | 3 --- evm/src/cpu/kernel/interpreter.rs | 2 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 4 +--- evm/src/generation/mod.rs | 2 +- 6 files changed, 4 insertions(+), 12 deletions(-) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index d1e06b6633..df6b09b3e7 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -15,9 +15,6 @@ global main: %shift_table_init // Encode constant nodes %initialize_rlp_segment - - // Encode constant nodes - %initialize_rlp_segment // Initialize the state, transaction and receipt trie root pointers. PROVER_INPUT(trie_ptr::state) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 6d0cf10ccc..c45785fa28 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -105,7 +105,7 @@ after_packed_small_rlp: // indicating where the data lives within @SEGMENT_RLP_RAW. // // Pre stack: node_type, node_ptr, encode_value, cur_len, retdest -// Post stack: result_ptr, result_len +// Post stack: result_ptr, result_len, cur_len encode_node: // stack: node_type, node_ptr, encode_value, cur_len, retdest // Increment node_ptr, so it points to the node payload instead of its type. diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index f3ee000def..03a8d27589 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -3,9 +3,6 @@ global mpt_hash_state_trie: // stack: cur_len, retdest PUSH encode_account - PUSH debug_before_encoding_child - PUSH mpt_delete - %pop2 %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // stack: node_ptr, encode_account, cur_len, retdest %jump(mpt_hash) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 58de9e3485..4641e8a188 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1174,7 +1174,7 @@ impl<'a> Interpreter<'a> { self.generation_state.registers.context = context; } - /// Writes the encoding of 0 to position @ + /// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS pub(crate) fn initialize_rlp_segment(&mut self) { self.generation_state.memory.set( MemoryAddress { diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index acd42e8f3a..9c2fd50b24 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -12,12 +12,11 @@ use crate::cpu::kernel::tests::mpt::{ }; use crate::generation::mpt::AccountRlp; use crate::generation::TrieInputs; -use crate::util::u256_to_usize; use crate::Node; #[test] fn mpt_insert_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xDEF), test_account_2()) + test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) } #[test] @@ -234,7 +233,6 @@ fn test_state_trie( let hash = H256::from_uint(&interpreter.stack()[1]); state_trie.insert(k, rlp::encode(&account).to_vec()); - let expected_state_trie_hash = state_trie.hash(); assert_eq!(hash, expected_state_trie_hash); diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index e5dbc3e7b4..d691d34e61 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -35,7 +35,7 @@ pub mod mpt; pub(crate) mod prover_input; pub(crate) mod rlp; pub(crate) mod state; -pub(crate) mod trie_extractor; +mod trie_extractor; use self::mpt::{load_all_mpts, TrieRootPtrs}; use crate::witness::util::mem_write_log; From 897ba5856aecf3948a900877b67fb8644c8f3b85 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 17:57:46 +0100 Subject: [PATCH 068/175] Remove assertion in packed verif --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 59fbaa033e..bce399bb4f 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -254,6 +254,7 @@ global write_table_if_jumpdest: PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 AND %assert_eq_const(0x8080808080808080808080808080808080808080808080808080808080808080) + %jumpi(return) POP %add_const(32) From 18a14bf2f293e8a1bd9704a8238aa0a1ee929ce1 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Fri, 5 Jan 2024 18:15:57 +0100 Subject: [PATCH 069/175] Remove assertion --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bce399bb4f..8f66b369a8 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -253,8 +253,7 @@ global write_table_if_jumpdest: // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 AND - %assert_eq_const(0x8080808080808080808080808080808080808080808080808080808080808080) - %jumpi(return) + %jump_neq_const(0x8080808080808080808080808080808080808080808080808080808080808080, return) POP %add_const(32) From 20db596e573e7f9d78af6010b5b2518c9a978b02 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Fri, 5 Jan 2024 09:45:45 +0100 Subject: [PATCH 070/175] Add some more explicit doc on plonky2 crate --- plonky2/src/gadgets/mod.rs | 3 + plonky2/src/gates/gate.rs | 49 +++++++++++- plonky2/src/iop/ext_target.rs | 4 + plonky2/src/iop/target.rs | 16 +++- plonky2/src/plonk/circuit_builder.rs | 114 ++++++++++++++++++++++++--- plonky2/src/plonk/circuit_data.rs | 12 +++ 6 files changed, 185 insertions(+), 13 deletions(-) diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index 9016211f97..ba19e667f8 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -1,3 +1,6 @@ +//! Gadgets provide additional methods to [`CircuitBuilder`] +//! to ease circuit creation. + pub mod arithmetic; pub mod arithmetic_extension; pub mod hash; diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 2f8f7df16a..baf26f8cd4 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -26,15 +26,45 @@ use crate::plonk::vars::{ use crate::util::serialization::{Buffer, IoResult}; /// A custom gate. +/// +/// Vanilla Plonk arithmetization only supports basic fan-in 2 / fan-out 1 arithmetic gates, +/// each of the form +/// +/// $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$ +/// +/// where: +/// - q_M, q_L, q_R and q_O are boolean selectors, +/// - a, b and c are values used as inputs and output respectively, +/// - q_C is a constant (possibly 0). +/// +/// This allows expressing simple operations like multiplication, addition, etc. For +/// instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. +/// Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. +/// +/// However, such gate is fairly limited for more complex computations. Hence, when a computation may +/// require too many of these "vanilla" gates, or when a computation arises often within the same circuit, +/// one may want to construct a tailored custom gate. These custom gates can use more selectors and are +/// not necessarily limited to 2 inputs + 1 output = 3 wires. +/// For instance, plonky2 supports natively a custom Poseidon hash gate that uses 135 wires. +/// +/// Note however that extending the number of wires necessary for a custom gate comes at a price, and may +/// impact the overall performances when generating proofs for a circuit containing them. pub trait Gate, const D: usize>: 'static + Send + Sync { + /// Defines a unique identifier for this custom gate. + /// + /// This is used as differentiating tag in gate serializers. fn id(&self) -> String; + /// Serializes this custom gate to the targeted byte buffer, with the provided [`CommonCircuitData`]. fn serialize(&self, dst: &mut Vec, common_data: &CommonCircuitData) -> IoResult<()>; + /// Deserializes the bytes in the provided buffer into this custom gate, given some [`CommonCircuitData`]. fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult where Self: Sized; + /// Defines the constraints that enforce the statement represented by this gate. + /// Constraints must be defined in the extension of this custom gate base field. fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec; /// Like `eval_unfiltered`, but specialized for points in the base field. @@ -88,6 +118,12 @@ pub trait Gate, const D: usize>: 'static + Send + S res } + /// Defines the recursive constraints that enforce the statement represented by this custom gate. + /// This is necessary to recursively verify proofs generated from a circuit containing such gates. + /// + /// **Note**: The order of the recursive constraints output by this method should match exactly the order + /// of the constraints obtained by the non-recursive [`Gate::eval_unfiltered`] method, otherwise the + /// prover won't be able to generate proofs. fn eval_unfiltered_circuit( &self, builder: &mut CircuitBuilder, @@ -175,10 +211,20 @@ pub trait Gate, const D: usize>: 'static + Send + S } /// The generators used to populate the witness. - /// Note: This should return exactly 1 generator per operation in the gate. + /// + /// **Note**: This should return exactly 1 generator per operation in the gate. fn generators(&self, row: usize, local_constants: &[F]) -> Vec>; /// The number of wires used by this gate. + /// + /// While vanilla Plonk can only evaluate one addition/multiplication at a time, a wider + /// configuration may be able to accomodate several identical gates at once. This is + /// particularly helpful for tiny custom gates that are being used extensively in circuits. + /// + /// For instance, the [crate::gates::multiplication_extension::MulExtensionGate] takes `3*D` + /// wires per multiplication (where `D`` is the degree of the extension), hence for a usual + /// configuration of 80 routed wires with D=2, one can evaluate 13 multiplications within a + /// single gate. fn num_wires(&self) -> usize; /// The number of constants used by this gate. @@ -187,6 +233,7 @@ pub trait Gate, const D: usize>: 'static + Send + S /// The maximum degree among this gate's constraint polynomials. fn degree(&self) -> usize; + /// The number of constraints defined by this sole custom gate. fn num_constraints(&self) -> usize; /// Number of operations performed by the gate. diff --git a/plonky2/src/iop/ext_target.rs b/plonky2/src/iop/ext_target.rs index 68d81fef0c..c64d96e872 100644 --- a/plonky2/src/iop/ext_target.rs +++ b/plonky2/src/iop/ext_target.rs @@ -9,6 +9,10 @@ use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; /// `Target`s representing an element of an extension field. +/// +/// This is typically used in recursion settings, where the outer circuit must verify +/// a proof satisfying an inner circuit's statement, which is verified using arithmetic +/// in an extension of the base field. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ExtensionTarget(pub [Target; D]); diff --git a/plonky2/src/iop/target.rs b/plonky2/src/iop/target.rs index c0a1e286b3..f12257145e 100644 --- a/plonky2/src/iop/target.rs +++ b/plonky2/src/iop/target.rs @@ -8,15 +8,25 @@ use crate::iop::wire::Wire; use crate::plonk::circuit_data::CircuitConfig; /// A location in the witness. +/// +/// Targets can either be placed at a specific location, or be "floating" around, +/// serving as intermediary value holders, and copied to other locations whenever needed. +/// +/// When generating a proof for a given circuit, the prover will "set" the values of some +/// (or all) targets, so that they satisfy the circuit constraints. This is done through +/// the [`PartialWitness`] interface. +/// +/// There are different "variants" of the `Target` type, namely [`ExtensionTarget`], +/// [`ExtensionAlgebraTarget`], but the `Target` type is the default one for most circuits +/// verifying some simple statement. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] pub enum Target { + /// A target that has a fixed location in the witness (seen as a `degree x num_wires` grid). Wire(Wire), /// A target that doesn't have any inherent location in the witness (but it can be copied to /// another target that does). This is useful for representing intermediate values in witness /// generation. - VirtualTarget { - index: usize, - }, + VirtualTarget { index: usize }, } impl Default for Target { diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 3bc3409903..38281d51e6 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -83,7 +83,56 @@ pub struct LookupWire { /// Index of the first lookup table row (i.e. the last `LookupTableGate`). pub first_lut_gate: usize, } + +/// Structure used to construct a plonky2 circuit. It provides all the necessary toolkit that, +/// from an initial circuit configuration, will enable one to design a circuit and its associated +/// prover/verifier data. +/// +/// # Usage +/// +/// ```rust +/// use plonky2::plonk::circuit_data::CircuitConfig; +/// use plonky2::plonk::circuit_builder::CircuitBuilder; +/// use plonky2::plonk::config::PoseidonGoldilocksConfig; +/// +/// // Define parameters for this circuit +/// const D: usize = 2; +/// type C = PoseidonGoldilocksConfig; +/// type F = >::F; +/// +/// let config = CircuitConfig::standard_recursion_config(); +/// let mut builder = CircuitBuilder::::new(config); +/// +/// // Build a circuit for the statement: "I know the 100th term +/// // of the Fibonacci sequence, starting from 0 and 1". +/// let initial_a = builder.constant(F::ZERO); +/// let initial_b = builder.constant(F::ONE); +/// let mut prev_target = initial_a; +/// let mut cur_target = initial_b; +/// for _ in 0..99 { +/// let temp = builder.add(prev_target, cur_target); +/// prev_target = cur_target; +/// cur_target = temp; +/// } +/// +/// // The only public input is the result (which is generated). +/// builder.register_public_input(cur_target); +/// +/// // Build the circuit +/// let circuit_data = builder.build::(); +/// +/// // Now compute the witness and generate a proof +/// let mut pw = PartialWitness::new(); +/// +/// // There are no public inputs to register, as the only one +/// // will be generated while proving the statement. +/// let proof = data.prove(pw).unwrap(); +/// +/// // Verify the proof +/// assert!(data.verify(proof).is_ok()); +/// ``` pub struct CircuitBuilder, const D: usize> { + /// Circuit configuration to be used by this `CircuitBuilder`. pub config: CircuitConfig, /// A domain separator, which is included in the initial Fiat-Shamir seed. This is generally not @@ -146,6 +195,10 @@ pub struct CircuitBuilder, const D: usize> { } impl, const D: usize> CircuitBuilder { + /// Given a [`CircuitConfig`], generate a new [`CircuitBuilder`] instance. + /// It will also check that the configuration provided is consistent, i.e. + /// that the different parameters provided can achieve the targeted security + /// level. pub fn new(config: CircuitConfig) -> Self { let builder = CircuitBuilder { config, @@ -173,6 +226,8 @@ impl, const D: usize> CircuitBuilder { builder } + /// Assert that the configuration used to create this `CircuitBuilder` is consistent, + /// i.e. that the different parameters meet the targeted security level. fn check_config(&self) { let &CircuitConfig { security_bits, @@ -201,6 +256,7 @@ impl, const D: usize> CircuitBuilder { self.domain_separator = Some(separator); } + /// Outputs the number of gates in this circuit. pub fn num_gates(&self) -> usize { self.gate_instances.len() } @@ -215,6 +271,7 @@ impl, const D: usize> CircuitBuilder { targets.iter().for_each(|&t| self.register_public_input(t)); } + /// Outputs the number of public inputs in this circuit. pub fn num_public_inputs(&self) -> usize { self.public_inputs.len() } @@ -244,10 +301,13 @@ impl, const D: usize> CircuitBuilder { self.lut_to_lookups[lut_index].push((looking_in, looking_out)); } + /// Outputs the number of lookup tables in this circuit. pub fn num_luts(&self) -> usize { self.lut_to_lookups.len() } + /// Given an index, outputs the corresponding looking table in the set of tables + /// used in this circuit, as a sequence of target tuples `(input, output)`. pub fn get_lut_lookups(&self, lut_index: usize) -> &[(Target, Target)] { &self.lut_to_lookups[lut_index] } @@ -262,22 +322,28 @@ impl, const D: usize> CircuitBuilder { Target::VirtualTarget { index } } + /// Adds `n` new "virtual" targets. pub fn add_virtual_targets(&mut self, n: usize) -> Vec { (0..n).map(|_i| self.add_virtual_target()).collect() } + /// Adds `N` new "virtual" targets, arranged as an array. pub fn add_virtual_target_arr(&mut self) -> [Target; N] { [0; N].map(|_| self.add_virtual_target()) } + /// Adds a new `HashOutTarget`. `NUM_HASH_OUT_ELTS` being hardcoded to 4, it internally + /// adds 4 virtual targets in a vector fashion. pub fn add_virtual_hash(&mut self) -> HashOutTarget { HashOutTarget::from_vec(self.add_virtual_targets(4)) } + /// Adds a new `MerkleCapTarget`, consisting in `1 << cap_height` `HashOutTarget`. pub fn add_virtual_cap(&mut self, cap_height: usize) -> MerkleCapTarget { MerkleCapTarget(self.add_virtual_hashes(1 << cap_height)) } + /// Adds `n` new `HashOutTarget` in a vector fashion. pub fn add_virtual_hashes(&mut self, n: usize) -> Vec { (0..n).map(|_i| self.add_virtual_hash()).collect() } @@ -337,7 +403,9 @@ impl, const D: usize> CircuitBuilder { } /// Add a virtual verifier data, register it as a public input and set it to `self.verifier_data_public_input`. - /// WARNING: Do not register any public input after calling this! TODO: relax this + /// + /// **WARNING**: Do not register any public input after calling this! + // TODO: relax this pub fn add_verifier_data_public_inputs(&mut self) -> VerifierCircuitTarget { assert!( self.verifier_data_public_input.is_none(), @@ -410,16 +478,12 @@ impl, const D: usize> CircuitBuilder { ); } + /// Adds a gate type to the set of gates to be used in this circuit. This can be useful + /// in conditional recursion to uniformize the gates set of the different circuits. pub fn add_gate_to_gate_set(&mut self, gate: GateRef) { self.gates.insert(gate); } - pub fn connect_extension(&mut self, src: ExtensionTarget, dst: ExtensionTarget) { - for i in 0..D { - self.connect(src.0[i], dst.0[i]); - } - } - /// Adds a generator which will copy `src` to `dst`. pub fn generate_copy(&mut self, src: Target, dst: Target) { self.add_simple_generator(CopyGenerator { src, dst }); @@ -427,6 +491,8 @@ impl, const D: usize> CircuitBuilder { /// Uses Plonk's permutation argument to require that two elements be equal. /// Both elements must be routable, otherwise this method will panic. + /// + /// For an example of usage, see [`CircuitBuilder::assert_one()`]. pub fn connect(&mut self, x: Target, y: Target) { assert!( x.is_routable(&self.config), @@ -440,17 +506,40 @@ impl, const D: usize> CircuitBuilder { .push(CopyConstraint::new((x, y), self.context_log.open_stack())); } + /// Enforces that two [`ExtensionTarget`] underlying values are equal. + pub fn connect_extension(&mut self, src: ExtensionTarget, dst: ExtensionTarget) { + for i in 0..D { + self.connect(src.0[i], dst.0[i]); + } + } + + /// Enforces that a routable `Target` value is 0, using Plonk's permutation argument. pub fn assert_zero(&mut self, x: Target) { let zero = self.zero(); self.connect(x, zero); } + /// Enforces that a routable `Target` value is 1, using Plonk's permutation argument. + /// + /// # Example + /// + /// Let say the circuit contains a target `a`, and a target `b` as public input so that the + /// prover can non-deterministically compute the multiplicative inverse of `a` when generating + /// a proof. + /// + /// One can then add the following constraint in the circuit to enforce that the value provided + /// by the prover is correct: + /// + /// ```ignore + /// let c = builder.mul(a, b); + /// builder.assert_one(c); + /// ``` pub fn assert_one(&mut self, x: Target) { let one = self.one(); self.connect(x, one); } - pub fn add_generators(&mut self, generators: Vec>) { + fn add_generators(&mut self, generators: Vec>) { self.generators.extend(generators); } @@ -479,10 +568,12 @@ impl, const D: usize> CircuitBuilder { self.constant(F::NEG_ONE) } + /// Returns a rootable boolean target set to false. pub fn _false(&mut self) -> BoolTarget { BoolTarget::new_unsafe(self.zero()) } + /// Returns a rootable boolean target set to true. pub fn _true(&mut self) -> BoolTarget { BoolTarget::new_unsafe(self.one()) } @@ -501,10 +592,12 @@ impl, const D: usize> CircuitBuilder { target } + /// Returns a vector of routable targets with the given constant values. pub fn constants(&mut self, constants: &[F]) -> Vec { constants.iter().map(|&c| self.constant(c)).collect() } + /// Returns a routable target with the given constant boolean value. pub fn constant_bool(&mut self, b: bool) -> BoolTarget { if b { self._true() @@ -513,12 +606,14 @@ impl, const D: usize> CircuitBuilder { } } + /// Returns a routable [`HashOutTarget`]. pub fn constant_hash(&mut self, h: HashOut) -> HashOutTarget { HashOutTarget { elements: h.elements.map(|x| self.constant(x)), } } + /// Returns a routable [`MerkleCapTarget`]. pub fn constant_merkle_cap>>( &mut self, cap: &MerkleCap, @@ -545,7 +640,7 @@ impl, const D: usize> CircuitBuilder { self.targets_to_constants.get(&target).cloned() } - /// If the given `ExtensionTarget` is a constant (i.e. it was created by the + /// If the given [`ExtensionTarget`] is a constant (i.e. it was created by the /// `constant_extension(F)` method), returns its constant value. Otherwise, returns `None`. pub fn target_as_constant_ext(&self, target: ExtensionTarget) -> Option { // Get a Vec of any coefficients that are constant. If we end up with exactly D of them, @@ -1178,6 +1273,7 @@ impl, const D: usize> CircuitBuilder { ) } + /// Builds a "full circuit", with both prover and verifier data. pub fn build>(self) -> CircuitData { self.build_with_options(true) } diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 1b07a8b2ab..c0c6022093 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -38,9 +38,19 @@ use crate::util::serialization::{ }; use crate::util::timing::TimingTree; +/// Configuration to be used when building a circuit. This defines the shape of the circuit +/// as well as its targeted security level and sub-protocol (e.g. FRI) parameters. +/// +/// It supports a [`Default`] implementation tailored for recursion with Poseidon hash (of width 12) +/// as internal hash function and FRI rate of 1/8. #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct CircuitConfig { + /// The number of wires available at each row. This corresponds to the "width" of the circuit, + /// and consists in the sum of routed wires and advice wires. pub num_wires: usize, + /// The number of routed wires, i.e. wires that will be involved in Plonk's permutation argument. + /// This allows copy constraints, i.e. enforcing that two distant values in a circuit are equal. + /// Non-routed wires are called advice wires. pub num_routed_wires: usize, pub num_constants: usize, /// Whether to use a dedicated gate for base field arithmetic, rather than using a single gate @@ -50,6 +60,8 @@ pub struct CircuitConfig { /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly) /// `degree / |F|`. pub num_challenges: usize, + /// A boolean to activate the zero-knowledge property. When this is set to `false`, proofs *may* + /// leak additional information. pub zero_knowledge: bool, /// A cap on the quotient polynomial's degree factor. The actual degree factor is derived /// systematically, but will never exceed this value. From 3c8b150f0f6528c933b4e9fa4ae186bb5b28e8de Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 8 Jan 2024 09:37:42 +0100 Subject: [PATCH 071/175] Rustdoc --- plonky2/src/plonk/circuit_builder.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 38281d51e6..fba3343401 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -92,8 +92,10 @@ pub struct LookupWire { /// /// ```rust /// use plonky2::plonk::circuit_data::CircuitConfig; +/// use plonky2::iop::witness::PartialWitness; /// use plonky2::plonk::circuit_builder::CircuitBuilder; -/// use plonky2::plonk::config::PoseidonGoldilocksConfig; +/// use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; +/// use plonky2::field::types::Field; /// /// // Define parameters for this circuit /// const D: usize = 2; @@ -126,10 +128,10 @@ pub struct LookupWire { /// /// // There are no public inputs to register, as the only one /// // will be generated while proving the statement. -/// let proof = data.prove(pw).unwrap(); +/// let proof = circuit_data.prove(pw).unwrap(); /// /// // Verify the proof -/// assert!(data.verify(proof).is_ok()); +/// assert!(circuit_data.verify(proof).is_ok()); /// ``` pub struct CircuitBuilder, const D: usize> { /// Circuit configuration to be used by this `CircuitBuilder`. From ed2e1bc780f749748b20f83edd2a899dd4c5bb53 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 8 Jan 2024 09:38:55 +0100 Subject: [PATCH 072/175] Add comment --- plonky2/src/plonk/circuit_builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index fba3343401..c0e0d870d1 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -112,7 +112,9 @@ pub struct LookupWire { /// let mut prev_target = initial_a; /// let mut cur_target = initial_b; /// for _ in 0..99 { +/// // Encode an addition of the two previous terms /// let temp = builder.add(prev_target, cur_target); +/// // Shift the two previous terms with the new value /// prev_target = cur_target; /// cur_target = temp; /// } From 3e61f06a1d2012785c65f1708620cf399857ac8d Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:49:28 +0100 Subject: [PATCH 073/175] Remove gas check in sys_stop (#1448) --- evm/src/cpu/kernel/asm/core/terminate.asm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 0c5d5a8f3e..d2c16b51c0 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -6,10 +6,6 @@ global sys_stop: // Set the parent context's return data size to 0. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - // This makes sure the gas used hasn't overflowed the gaslimit. - // This could happen when executing a native instruction (i.e. not a syscall). - %charge_gas_const(0) - %leftover_gas // stack: leftover_gas PUSH 1 // success From 2dacbfe2ffd9bc5242caaafe17dd43095d664f28 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:46:26 +0100 Subject: [PATCH 074/175] Address bundling (#1426) * Start * Scale TxnFields * Speed-up * Misc fixes * Other fixes * Fix * Fix offset * One more fix * And one more fix * Fix * Fix * Fix init * More interpreter fixes * Final fixes * Add helper methods * Clippy * Apply suggestions * Comments * Update documentation * Regenerate pdf * minor * Rename some macros for consistency * Add utility method for unscaling segments and scaled metadata * Address comments --- evm/spec/cpulogic.tex | 45 ++- evm/spec/zkevm.pdf | Bin 295982 -> 315675 bytes evm/src/cpu/byte_unpacking.rs | 49 ++- evm/src/cpu/contextops.rs | 42 +-- evm/src/cpu/cpu_stark.rs | 62 ++-- evm/src/cpu/dup_swap.rs | 4 +- evm/src/cpu/jumps.rs | 9 +- evm/src/cpu/kernel/asm/account_code.asm | 21 +- evm/src/cpu/kernel/asm/bignum/util.asm | 14 +- evm/src/cpu/kernel/asm/core/access_lists.asm | 22 +- evm/src/cpu/kernel/asm/core/call.asm | 90 +++--- evm/src/cpu/kernel/asm/core/call_gas.asm | 2 +- evm/src/cpu/kernel/asm/core/create.asm | 19 +- .../cpu/kernel/asm/core/create_addresses.asm | 17 +- .../cpu/kernel/asm/core/create_receipt.asm | 20 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 8 +- .../kernel/asm/core/precompiles/blake2_f.asm | 30 +- .../kernel/asm/core/precompiles/bn_add.asm | 12 +- .../kernel/asm/core/precompiles/bn_mul.asm | 11 +- .../cpu/kernel/asm/core/precompiles/ecrec.asm | 11 +- .../kernel/asm/core/precompiles/expmod.asm | 84 +++--- .../cpu/kernel/asm/core/precompiles/id.asm | 17 +- .../cpu/kernel/asm/core/precompiles/main.asm | 4 +- .../kernel/asm/core/precompiles/rip160.asm | 35 +-- .../kernel/asm/core/precompiles/sha256.asm | 37 +-- .../kernel/asm/core/precompiles/snarkv.asm | 9 +- evm/src/cpu/kernel/asm/core/process_txn.asm | 30 +- evm/src/cpu/kernel/asm/core/terminate.asm | 32 +- evm/src/cpu/kernel/asm/core/util.asm | 5 +- evm/src/cpu/kernel/asm/curve/wnaf.asm | 6 +- evm/src/cpu/kernel/asm/main.asm | 8 +- evm/src/cpu/kernel/asm/memory/core.asm | 122 ++++---- evm/src/cpu/kernel/asm/memory/memcpy.asm | 76 ++--- evm/src/cpu/kernel/asm/memory/memset.asm | 36 +-- evm/src/cpu/kernel/asm/memory/metadata.asm | 72 ++++- evm/src/cpu/kernel/asm/memory/packing.asm | 283 +++++++++--------- evm/src/cpu/kernel/asm/memory/syscalls.asm | 20 +- evm/src/cpu/kernel/asm/memory/txn_fields.asm | 17 +- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 34 +-- .../asm/mpt/hash/hash_trie_specific.asm | 193 ++++++------ evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 91 +++--- .../asm/mpt/insert/insert_trie_specific.asm | 10 +- evm/src/cpu/kernel/asm/rlp/decode.asm | 98 +++--- evm/src/cpu/kernel/asm/rlp/encode.asm | 209 ++++++------- .../cpu/kernel/asm/rlp/encode_rlp_scalar.asm | 63 ++-- .../cpu/kernel/asm/rlp/encode_rlp_string.asm | 97 +++--- evm/src/cpu/kernel/asm/rlp/read_to_memory.asm | 35 ++- evm/src/cpu/kernel/asm/shift.asm | 16 +- .../asm/transactions/common_decoding.asm | 149 +++++---- .../cpu/kernel/asm/transactions/router.asm | 14 +- .../cpu/kernel/asm/transactions/type_0.asm | 87 +++--- .../cpu/kernel/asm/transactions/type_1.asm | 67 ++--- .../cpu/kernel/asm/transactions/type_2.asm | 67 ++--- evm/src/cpu/kernel/asm/util/basic_macros.asm | 33 ++ evm/src/cpu/kernel/asm/util/keccak.asm | 17 +- .../cpu/kernel/constants/context_metadata.rs | 40 ++- .../cpu/kernel/constants/global_metadata.rs | 112 +++---- evm/src/cpu/kernel/constants/mod.rs | 11 +- evm/src/cpu/kernel/constants/txn_fields.rs | 45 ++- evm/src/cpu/kernel/interpreter.rs | 179 ++++++----- evm/src/cpu/kernel/tests/account_code.rs | 79 ++--- evm/src/cpu/kernel/tests/add11.rs | 8 +- evm/src/cpu/kernel/tests/core/access_lists.rs | 46 ++- .../kernel/tests/core/jumpdest_analysis.rs | 8 +- evm/src/cpu/kernel/tests/mpt/hex_prefix.rs | 17 +- evm/src/cpu/kernel/tests/packing.rs | 26 +- evm/src/cpu/kernel/tests/rlp/decode.rs | 35 ++- evm/src/cpu/kernel/tests/rlp/encode.rs | 30 +- evm/src/cpu/memio.rs | 53 ++-- evm/src/cpu/shift.rs | 4 +- evm/src/cpu/stack.rs | 25 +- evm/src/cpu/syscalls_exceptions.rs | 4 +- evm/src/generation/mod.rs | 3 +- evm/src/generation/prover_input.rs | 9 +- evm/src/generation/state.rs | 12 +- evm/src/generation/trie_extractor.rs | 2 +- evm/src/keccak_sponge/keccak_sponge_stark.rs | 6 +- evm/src/memory/memory_stark.rs | 4 +- evm/src/memory/segments.rs | 85 +++--- evm/src/recursive_verifier.rs | 82 +++-- evm/src/verifier.rs | 19 +- evm/src/witness/memory.rs | 26 +- evm/src/witness/operation.rs | 87 +++--- evm/src/witness/transition.rs | 24 +- evm/tests/log_opcode.rs | 2 +- 85 files changed, 2026 insertions(+), 1718 deletions(-) diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex index df866daf1a..318e2db487 100644 --- a/evm/spec/cpulogic.tex +++ b/evm/spec/cpulogic.tex @@ -75,15 +75,14 @@ \subsection{Privileged instructions} \item[0x0F.] \texttt{SUBMOD}. Pops 3 elements from the stack, and pushes the modular difference of the first two elements of the stack by the third one. It is similar to the SUB instruction, with an extra pop for the custom modulus. - \item[0x21.] \texttt{KECCAK\_GENERAL}. Pops 4 elements (successively the context, segment, and offset portions of a Memory address, followed by a length $\ell$) - and pushes the hash of the memory portion starting at the constructed address and of length $\ell$. It is similar to KECCAK256 (0x20) instruction, but can be applied to - any memory section (i.e. even privileged ones). + \item[0x21.] \texttt{KECCAK\_GENERAL}. Pops 2 elements (a Memory address, followed by a length $\ell$) and pushes the hash of the memory portion starting at the + constructed address and of length $\ell$. It is similar to KECCAK256 (0x20) instruction, but can be applied to any memory section (i.e. even privileged ones). \item[0x49.] \texttt{PROVER\_INPUT}. Pushes a single prover input onto the stack. - \item[0xC0-0xDF.] \texttt{MSTORE\_32BYTES}. Pops 4 elements from the stack (successively the context, segment, and offset portions of a Memory address, and then a value), and pushes - a new offset' onto the stack. The value is being decomposed into bytes and written to memory, starting from the reconstructed address. The new offset being pushed is computed as the - initial address offset + the length of the byte sequence being written to memory. Note that similarly to PUSH (0x60-0x7F) instructions there are 31 MSTORE\_32BYTES instructions, each + \item[0xC0-0xDF.] \texttt{MSTORE\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a value), and pushes + a new address' onto the stack. The value is being decomposed into bytes and written to memory, starting from the fetched address. The new address being pushed is computed as the + initial address + the length of the byte sequence being written to memory. Note that similarly to PUSH (0x60-0x7F) instructions, there are 32 MSTORE\_32BYTES instructions, each corresponding to a target byte length (length 0 is ignored, for the same reasons as MLOAD\_32BYTES, see below). Writing to memory an integer fitting in $n$ bytes with a length $\ell < n$ will result in the integer being truncated. On the other hand, specifying a length $\ell$ greater than the byte size of the value being written will result in padding with zeroes. This process is heavily used when resetting memory sections (by calling MSTORE\_32BYTES\_32 with the value 0). @@ -93,29 +92,49 @@ \subsection{Privileged instructions} \item[0xF7.] \texttt{SET\_CONTEXT}. Pops the top element of the stack and updates the current context to this value. It is usually used when calling another contract or precompile, to distinguish the caller from the callee. - \item[0xF8.] \texttt{MLOAD\_32BYTES}. Pops 4 elements from the stack (successively the context, segment, and offset portions of a Memory address, and then a length $\ell$), and pushes - a value onto the stack. The pushed value corresponds to the U256 integer read from the big-endian sequence of length $\ell$ from the memory address being reconstructed. Note that an + \item[0xF8.] \texttt{MLOAD\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a length $\ell$), and pushes + a value onto the stack. The pushed value corresponds to the U256 integer read from the big-endian sequence of length $\ell$ from the memory address being fetched. Note that an empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. The popped element, \textit{kexit\_info}, contains several pieces of information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. - \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 3 elements (successively the context, segment, and offset portions of a Memory address), and pushes the value stored at this memory + \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 1 elements (a Memory address), and pushes the value stored at this memory address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. - \item[0xFC.] \texttt{MSTORE\_GENERAL}. Pops 4 elements (successively a value, then the context, segment, and offset portions of a Memory address), and writes the popped value from - the stack at the reconstructed address. It can write to any memory location, general (similarly to MSTORE (0x52) / MSTORE8 (0x53) instructions) or privileged. - + \item[0xFC.] \texttt{MSTORE\_GENERAL}. Pops 2 elements (a value and a Memory address), and writes the popped value from + the stack at the fetched address. It can write to any memory location, general (similarly to MSTORE (0x52) / MSTORE8 (0x53) instructions) or privileged. \end{enumerate} +\subsection{Memory addresses} +\label{memoryaddresses} + +Kernel operations deal with memory addresses as single U256 elements. +However, when processing the operations to generate the proof witness, the CPU will decompose these into three components: + +\begin{itemize} + \item[context.] The context of the memory address. The Kernel context is special, and has value 0. + + \item[segment.] The segment of the memory address, corresponding to a specific section given a context (eg. MPT data, global metadata, etc.). + + \item[virtual.] The offset of the memory address, within a segment given a context. +\end{itemize} + +To easily retrieve these components, we scale them so that they can represent a memory address as: + +$$ \mathrm{addr} = 2^{64} \cdot \mathrm{context} + 2^{32} \cdot \mathrm{segment} + \mathrm{offset}$$ + +This allows to easily retrieve each component individually once a Memory address has been decomposed into 32-bit limbs. + + \subsection{Stack handling} \label{stackhandling} \subsubsection{Top of the stack} The majority of memory operations involve the stack. The stack is a segment in memory, and stack operations (popping or pushing) use the memory channels. -Every CPU instruction performs between 0 and 4 pops, and may push at most once. However, for efficiency purposes, we hold the top of the stack in +Every CPU instruction performs between 0 and 3 pops, and may push at most once. However, for efficiency purposes, we hold the top of the stack in the first memory channel \texttt{current\_row.mem\_channels[0]}, only writing it in memory if necessary. \paragraph*{Motivation:} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 4f4f0a00b0effa6b21385b1cf3e66f00d8f93e83..97217d5d8069f79d32afa655ebcfafe1cea12357 100644 GIT binary patch delta 129878 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⁣y6?kta2ql;)g{j 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%0AE*D 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

JR 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 // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES // by the 5th bit set to 0. let filter = lv.op.m_op_32bytes * (lv.opcode_bits[5] - P::ONES); - let new_offset = nv.mem_channels[0].value; - let virt = lv.mem_channels[2].value[0]; + + // The address to write to is stored in the first memory channel. + // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. + // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. + let new_addr = nv.mem_channels[0].value; + let written_addr = lv.mem_channels[0].value; + // Read len from opcode bits and constrain the pushed new offset. let len_bits: P = lv.opcode_bits[..5] .iter() @@ -25,8 +30,16 @@ pub(crate) fn eval_packed( .map(|(i, &bit)| bit * P::Scalar::from_canonical_u64(1 << i)) .sum(); let len = len_bits + P::ONES; - yield_constr.constraint(filter * (new_offset[0] - virt - len)); - for &limb in &new_offset[1..] { + + // Check that `virt` is increased properly. + yield_constr.constraint(filter * (new_addr[0] - written_addr[0] - len)); + + // Check that `segment` and `ctx` do not change. + yield_constr.constraint(filter * (new_addr[1] - written_addr[1])); + yield_constr.constraint(filter * (new_addr[2] - written_addr[2])); + + // Check that the rest of the returned address is null. + for &limb in &new_addr[3..] { yield_constr.constraint(filter * limb); } } @@ -41,8 +54,13 @@ pub(crate) fn eval_ext_circuit, const D: usize>( // by the 5th bit set to 0. let filter = builder.mul_sub_extension(lv.op.m_op_32bytes, lv.opcode_bits[5], lv.op.m_op_32bytes); - let new_offset = nv.mem_channels[0].value; - let virt = lv.mem_channels[2].value[0]; + + // The address to write to is stored in the first memory channel. + // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. + // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. + let new_addr = nv.mem_channels[0].value; + let written_addr = lv.mem_channels[0].value; + // Read len from opcode bits and constrain the pushed new offset. let len_bits = lv.opcode_bits[..5].iter().enumerate().fold( builder.zero_extension(), @@ -50,11 +68,26 @@ pub(crate) fn eval_ext_circuit, const D: usize>( builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) }, ); - let diff = builder.sub_extension(new_offset[0], virt); + + // Check that `virt` is increased properly. + let diff = builder.sub_extension(new_addr[0], written_addr[0]); let diff = builder.sub_extension(diff, len_bits); let constr = builder.mul_sub_extension(filter, diff, filter); yield_constr.constraint(builder, constr); - for &limb in &new_offset[1..] { + + // Check that `segment` and `ctx` do not change. + { + let diff = builder.sub_extension(new_addr[1], written_addr[1]); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + + let diff = builder.sub_extension(new_addr[2], written_addr[2]); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + } + + // Check that the rest of the returned address is null. + for &limb in &new_addr[3..] { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 2f0c72d433..0afc070f09 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -11,7 +11,8 @@ use super::membus::NUM_GP_CHANNELS; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::VALUE_LIMBS; // If true, the instruction will keep the current context for the next row. // If false, next row's context is handled manually. @@ -83,8 +84,10 @@ fn eval_packed_get( // If the opcode is GET_CONTEXT, then lv.opcode_bits[0] = 0. let filter = lv.op.context_op * (P::ONES - lv.opcode_bits[0]); let new_stack_top = nv.mem_channels[0].value; - yield_constr.constraint(filter * (new_stack_top[0] - lv.context)); - for &limb in &new_stack_top[1..] { + // Context is scaled by 2^64, hence stored in the 3rd limb. + yield_constr.constraint(filter * (new_stack_top[2] - lv.context)); + + for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -113,12 +116,14 @@ fn eval_ext_circuit_get, const D: usize>( let prod = builder.mul_extension(lv.op.context_op, lv.opcode_bits[0]); let filter = builder.sub_extension(lv.op.context_op, prod); let new_stack_top = nv.mem_channels[0].value; + // Context is scaled by 2^64, hence stored in the 3rd limb. { - let diff = builder.sub_extension(new_stack_top[0], lv.context); + let diff = builder.sub_extension(new_stack_top[2], lv.context); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); } - for &limb in &new_stack_top[1..] { + + for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } @@ -155,13 +160,14 @@ fn eval_packed_set( let stack_top = lv.mem_channels[0].value; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; - let ctx_metadata_segment = P::Scalar::from_canonical_u64(Segment::ContextMetadata as u64); - let stack_size_field = P::Scalar::from_canonical_u64(ContextMetadata::StackSize as u64); + // We need to unscale the context metadata segment and related field. + let ctx_metadata_segment = P::Scalar::from_canonical_usize(Segment::ContextMetadata.unscale()); + let stack_size_field = P::Scalar::from_canonical_usize(ContextMetadata::StackSize.unscale()); let local_sp_dec = lv.stack_len - P::ONES; // The next row's context is read from stack_top. - yield_constr.constraint(filter * (stack_top[0] - nv.context)); - for &limb in &stack_top[1..] { + yield_constr.constraint(filter * (stack_top[2] - nv.context)); + for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -220,22 +226,23 @@ fn eval_ext_circuit_set, const D: usize>( let stack_top = lv.mem_channels[0].value; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; - let ctx_metadata_segment = builder.constant_extension(F::Extension::from_canonical_u32( - Segment::ContextMetadata as u32, + // We need to unscale the context metadata segment and related field. + let ctx_metadata_segment = builder.constant_extension(F::Extension::from_canonical_usize( + Segment::ContextMetadata.unscale(), )); - let stack_size_field = builder.constant_extension(F::Extension::from_canonical_u32( - ContextMetadata::StackSize as u32, + let stack_size_field = builder.constant_extension(F::Extension::from_canonical_usize( + ContextMetadata::StackSize.unscale(), )); let one = builder.one_extension(); let local_sp_dec = builder.sub_extension(lv.stack_len, one); // The next row's context is read from stack_top. { - let diff = builder.sub_extension(stack_top[0], nv.context); + let diff = builder.sub_extension(stack_top[2], nv.context); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); } - for &limb in &stack_top[1..] { + for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } @@ -368,7 +375,8 @@ pub(crate) fn eval_packed( yield_constr.constraint(new_filter * (channel.addr_context - nv.context)); // Same segment for both. yield_constr.constraint( - new_filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + new_filter + * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // The address is one less than stack_len. let addr_virtual = stack_len - P::ONES; @@ -429,7 +437,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( { let diff = builder.add_const_extension( channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint(builder, constr); diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index c93f1b8770..478dc9c2a6 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -21,7 +21,7 @@ use crate::cpu::{ }; use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; @@ -29,15 +29,14 @@ use crate::stark::Stark; /// the CPU reads the output of the sponge directly from the `KeccakSpongeStark` table. pub(crate) fn ctl_data_keccak_sponge() -> Vec> { // When executing KECCAK_GENERAL, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = context - // GP channel 1: stack[-2] = segment - // GP channel 2: stack[-3] = virt - // GP channel 3: stack[-4] = len + // GP channel 0: stack[-1] = addr (context, segment, virt) + // GP channel 1: stack[-2] = len // Next GP channel 0: pushed = outputs - let context = Column::single(COL_MAP.mem_channels[0].value[0]); - let segment = Column::single(COL_MAP.mem_channels[1].value[0]); - let virt = Column::single(COL_MAP.mem_channels[2].value[0]); - let len = Column::single(COL_MAP.mem_channels[3].value[0]); + let (context, segment, virt) = get_addr(&COL_MAP, 0); + let context = Column::single(context); + let segment = Column::single(segment); + let virt = Column::single(virt); + let len = Column::single(COL_MAP.mem_channels[1].value[0]); let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); @@ -149,27 +148,30 @@ pub(crate) fn ctl_data_byte_unpacking() -> Vec> { let is_read = Column::constant(F::ZERO); // When executing MSTORE_32BYTES, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = context - // GP channel 1: stack[-2] = segment - // GP channel 2: stack[-3] = virt - // GP channel 3: stack[-4] = val + // GP channel 0: stack[-1] = addr (context, segment, virt) + // GP channel 1: stack[-2] = val // Next GP channel 0: pushed = new_offset (virt + len) - let context = Column::single(COL_MAP.mem_channels[0].value[0]); - let segment = Column::single(COL_MAP.mem_channels[1].value[0]); - let virt = Column::single(COL_MAP.mem_channels[2].value[0]); - let val = Column::singles(COL_MAP.mem_channels[3].value); + let (context, segment, virt) = get_addr(&COL_MAP, 0); + let mut res = vec![ + is_read, + Column::single(context), + Column::single(segment), + Column::single(virt), + ]; // len can be reconstructed as new_offset - virt. let len = Column::linear_combination_and_next_row_with_constant( - [(COL_MAP.mem_channels[2].value[0], -F::ONE)], + [(COL_MAP.mem_channels[0].value[0], -F::ONE)], [(COL_MAP.mem_channels[0].value[0], F::ONE)], F::ZERO, ); + res.push(len); let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); + res.push(timestamp); - let mut res = vec![is_read, context, segment, virt, len, timestamp]; + let val = Column::singles(COL_MAP.mem_channels[1].value); res.extend(val); res @@ -224,6 +226,20 @@ pub(crate) const MEM_CODE_CHANNEL_IDX: usize = 0; /// Index of the first general purpose memory channel. pub(crate) const MEM_GP_CHANNELS_IDX_START: usize = MEM_CODE_CHANNEL_IDX + 1; +/// Recover the three components of an address, given a CPU row and +/// a provided memory channel index. +/// The components are recovered as follows: +/// +/// - `context`, shifted by 2^64 (i.e. at index 2) +/// - `segment`, shifted by 2^32 (i.e. at index 1) +/// - `virtual`, not shifted (i.e. at index 0) +pub(crate) const fn get_addr(lv: &CpuColumnsView, mem_channel: usize) -> (T, T, T) { + let addr_context = lv.mem_channels[mem_channel].value[2]; + let addr_segment = lv.mem_channels[mem_channel].value[1]; + let addr_virtual = lv.mem_channels[mem_channel].value[0]; + (addr_context, addr_segment, addr_virtual) +} + /// Make the time/channel column for memory lookups. fn mem_time_and_channel(channel: usize) -> Column { let scalar = F::from_canonical_usize(NUM_CHANNELS); @@ -234,10 +250,10 @@ fn mem_time_and_channel(channel: usize) -> Column { /// Creates the vector of `Columns` corresponding to the contents of the code channel when reading code values. pub(crate) fn ctl_data_code_memory() -> Vec> { let mut cols = vec![ - Column::constant(F::ONE), // is_read - Column::single(COL_MAP.code_context), // addr_context - Column::constant(F::from_canonical_u64(Segment::Code as u64)), // addr_segment - Column::single(COL_MAP.program_counter), // addr_virtual + Column::constant(F::ONE), // is_read + Column::single(COL_MAP.code_context), // addr_context + Column::constant(F::from_canonical_usize(Segment::Code.unscale())), // addr_segment + Column::single(COL_MAP.program_counter), // addr_virtual ]; // Low limb of the value matches the opcode bits diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 78e5891a90..44763fdc91 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -54,7 +54,7 @@ fn constrain_channel_packed( yield_constr.constraint(filter * (channel.is_read - P::Scalar::from_bool(is_read))); yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( - filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + filter * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Top of the stack is at `addr = lv.stack_len - 1`. let addr_virtual = lv.stack_len - P::ONES - offset; @@ -94,7 +94,7 @@ fn constrain_channel_ext_circuit, const D: usize>( { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), filter, channel.addr_segment, filter, diff --git a/evm/src/cpu/jumps.rs b/evm/src/cpu/jumps.rs index f2fd544c67..fd7fcfd962 100644 --- a/evm/src/cpu/jumps.rs +++ b/evm/src/cpu/jumps.rs @@ -87,7 +87,8 @@ pub(crate) fn eval_packed_jump_jumpi( yield_constr.constraint_transition(new_filter * (channel.is_read - P::ONES)); yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); yield_constr.constraint_transition( - new_filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + new_filter + * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); @@ -134,7 +135,7 @@ pub(crate) fn eval_packed_jump_jumpi( yield_constr.constraint( filter * (jumpdest_flag_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::JumpdestBits as u64)), + - P::Scalar::from_canonical_usize(Segment::JumpdestBits.unscale())), ); yield_constr.constraint(filter * (jumpdest_flag_channel.addr_virtual - dst[0])); @@ -205,7 +206,7 @@ pub(crate) fn eval_ext_circuit_jump_jumpi, const D: { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -308,7 +309,7 @@ pub(crate) fn eval_ext_circuit_jump_jumpi, const D: { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::JumpdestBits as u64), + -F::from_canonical_usize(Segment::JumpdestBits.unscale()), filter, jumpdest_flag_channel.addr_segment, filter, diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 1a9262eea9..6aedbd2e39 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -86,6 +86,8 @@ global extcodesize: // Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. // Pre stack: address, ctx, retdest // Post stack: code_size +// +// NOTE: The provided `dest` **MUST** have a virtual address of 0. global load_code: %stack (address, ctx, retdest) -> (extcodehash, address, load_code_ctd, ctx, retdest) JUMP @@ -94,8 +96,9 @@ load_code_ctd: DUP1 ISZERO %jumpi(load_code_non_existent_account) // Load the code non-deterministically in memory and return the length. PROVER_INPUT(account_code) - %stack (code_size, codehash, ctx, retdest) -> (ctx, @SEGMENT_CODE, 0, code_size, codehash, retdest, code_size) + %stack (code_size, codehash, ctx, retdest) -> (ctx, code_size, codehash, retdest, code_size) // Check that the hash of the loaded code equals `codehash`. + // ctx == DST, as SEGMENT_CODE == offset == 0. KECCAK_GENERAL // stack: shouldbecodehash, codehash, retdest, code_size %assert_eq @@ -103,9 +106,9 @@ load_code_ctd: JUMP load_code_non_existent_account: - // Write 0 at address 0 for soundness. - // stack: codehash, ctx, retdest - %stack (codehash, ctx, retdest) -> (0, ctx, @SEGMENT_CODE, 0, retdest, 0) + // Write 0 at address 0 for soundness: SEGMENT_CODE == 0, hence ctx == addr. + // stack: codehash, addr, retdest + %stack (codehash, addr, retdest) -> (0, addr, retdest, 0) MSTORE_GENERAL // stack: retdest, 0 JUMP @@ -120,10 +123,14 @@ global load_code_padded: %jump(load_code) load_code_padded_ctd: - %stack (code_size, ctx, retdest) -> (ctx, @SEGMENT_CODE, code_size, 0, ctx, retdest, code_size) + // SEGMENT_CODE == 0. + // stack: code_size, ctx, retdest + %stack (code_size, ctx, retdest) -> (ctx, code_size, 0, retdest, code_size) + ADD + // stack: addr, 0, retdest, code_size MSTORE_32BYTES_32 - // stack: last_offset, ctx, retdest, code_size - %stack (last_offset, ctx) -> (0, ctx, @SEGMENT_CODE, last_offset) + // stack: addr', retdest, code_size + PUSH 0 MSTORE_GENERAL // stack: retdest, code_size JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/util.asm b/evm/src/cpu/kernel/asm/bignum/util.asm index 7bd6e0dc33..0385deec2d 100644 --- a/evm/src/cpu/kernel/asm/bignum/util.asm +++ b/evm/src/cpu/kernel/asm/bignum/util.asm @@ -1,15 +1,21 @@ %macro memcpy_current_general // stack: dst, src, len - GET_CONTEXT - %stack (context, dst, src, len) -> (context, @SEGMENT_KERNEL_GENERAL, dst, context, @SEGMENT_KERNEL_GENERAL, src, len, %%after) + // DST and SRC are offsets, for the same memory segment + GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address_no_offset + %stack (addr_no_offset, dst, src, len) -> (addr_no_offset, src, addr_no_offset, dst, len, %%after) + ADD + // stack: SRC, addr_no_offset, dst, len, %%after + SWAP2 + ADD + // stack: DST, SRC, len, %%after %jump(memcpy) %%after: %endmacro %macro clear_current_general // stack: dst, len - GET_CONTEXT - %stack (context, dst, len) -> (context, @SEGMENT_KERNEL_GENERAL, dst, len, %%after) + GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address + %stack (DST, len) -> (DST, len, %%after) %jump(memset) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm index b1b9fd5d5e..5019b6840e 100644 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -120,14 +120,20 @@ insert_storage_key: // stack: i, len, addr, key, value, retdest DUP4 DUP4 %journal_add_storage_loaded // Add a journal entry for the loaded storage key. // stack: i, len, addr, key, value, retdest - DUP1 %increment - DUP1 %increment - %stack (i_plus_2, i_plus_1, i, len, addr, key, value) -> (i, addr, i_plus_1, key, i_plus_2, value, i_plus_2, value) - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new address at the end of the array. - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new key after that - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new value after that - // stack: i_plus_2, value, retdest - %increment + DUP1 + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS + %build_kernel_address + + %stack(dst, i, len, addr, key, value) -> (addr, dst, dst, key, dst, value, i, value) + MSTORE_GENERAL // Store new address at the end of the array. + // stack: dst, key, dst, value, i, value, retdest + %increment SWAP1 + MSTORE_GENERAL // Store new key after that + // stack: dst, value, i, value, retdest + %add_const(2) SWAP1 + MSTORE_GENERAL // Store new value after that + // stack: i, value, retdest + %add_const(3) %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length. %stack (value, retdest) -> (retdest, 1, value) // Return 1 to indicate that the storage key was inserted. JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 2e7d1d7345..aa8fbf0c05 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -1,4 +1,5 @@ // Handlers for call-like operations, namely CALL, CALLCODE, STATICCALL and DELEGATECALL. +// Reminder: All context metadata hardcoded offsets are already scaled by `Segment::ContextMetadata`. // Creates a new sub context and executes the code of the given account. global sys_call: @@ -271,7 +272,10 @@ call_too_deep: // because it will already be 0 by default. %macro set_static_true // stack: new_ctx - %stack (new_ctx) -> (1, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, new_ctx) + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) + PUSH 1 + // stack: 1, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro @@ -279,74 +283,90 @@ call_too_deep: // Set @CTX_METADATA_STATIC of the next context to the current value. %macro set_static // stack: new_ctx + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) %mload_context_metadata(@CTX_METADATA_STATIC) - %stack (is_static, new_ctx) -> (is_static, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, new_ctx) + // stack: is_static, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_addr // stack: called_addr, new_ctx - %stack (called_addr, new_ctx) - -> (called_addr, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_ADDRESS, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_ADDRESS) + SWAP1 + // stack: called_addr, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_caller // stack: sender, new_ctx - %stack (sender, new_ctx) - -> (sender, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLER, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLER) + SWAP1 + // stack: sender, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_value // stack: value, new_ctx - %stack (value, new_ctx) - -> (value, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALL_VALUE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALL_VALUE) + SWAP1 + // stack: value, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_code_size // stack: code_size, new_ctx - %stack (code_size, new_ctx) - -> (code_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CODE_SIZE) + SWAP1 + // stack: code_size, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_calldata_size // stack: calldata_size, new_ctx - %stack (calldata_size, new_ctx) - -> (calldata_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLDATA_SIZE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) + SWAP1 + // stack: calldata_size, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_gas_limit // stack: gas_limit, new_ctx - %stack (gas_limit, new_ctx) - -> (gas_limit, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_GAS_LIMIT, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_GAS_LIMIT) + SWAP1 + // stack: gas_limit, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_parent_ctx // stack: new_ctx - PUSH @CTX_METADATA_PARENT_CONTEXT - PUSH @SEGMENT_CONTEXT_METADATA - DUP3 // new_ctx + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_CONTEXT) GET_CONTEXT + // stack: ctx, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_parent_pc(label) // stack: new_ctx - %stack (new_ctx) - -> ($label, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_PARENT_PC, new_ctx) + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_PC) + PUSH $label + // stack: label, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro @@ -381,17 +401,18 @@ call_too_deep: %macro copy_mem_to_calldata // stack: new_ctx, args_offset, args_size GET_CONTEXT - %stack (ctx, new_ctx, args_offset, args_size) -> - ( - new_ctx, @SEGMENT_CALLDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, args_offset, // SRC - args_size, %%after, // count, retdest - new_ctx, args_size - ) + %stack(ctx, new_ctx, args_offset, args_size) -> (ctx, @SEGMENT_MAIN_MEMORY, args_offset, args_size, %%after, new_ctx, args_size) + %build_address + // stack: SRC, args_size, %%after, new_ctx, args_size + DUP4 + %build_address_with_ctx_no_offset(@SEGMENT_CALLDATA) + // stack: DST, SRC, args_size, %%after, new_ctx, args_size %jump(memcpy_bytes) %%after: - %stack (new_ctx, args_size) -> - (args_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLDATA_SIZE) + // stack: new_ctx, args_size + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) + // stack: addr, args_size + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -403,13 +424,12 @@ call_too_deep: // stack: returndata_size, ret_size, new_ctx, success, ret_offset, kexit_info %min GET_CONTEXT - %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> - ( - ctx, @SEGMENT_MAIN_MEMORY, ret_offset, // DST - ctx, @SEGMENT_RETURNDATA, 0, // SRC - n, %%after, // count, retdest - kexit_info, success - ) + %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> (ctx, @SEGMENT_RETURNDATA, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success) + %build_address_no_offset + // stack: SRC, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success + SWAP3 + %build_address + // stack: DST, SRC, n, %%after, kexit_info, success %jump(memcpy_bytes) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/core/call_gas.asm b/evm/src/cpu/kernel/asm/core/call_gas.asm index 69e2796661..3961352139 100644 --- a/evm/src/cpu/kernel/asm/core/call_gas.asm +++ b/evm/src/cpu/kernel/asm/core/call_gas.asm @@ -9,7 +9,7 @@ // Charge gas for *call opcodes and return the sub-context gas limit. // Doesn't include memory expansion costs. global call_charge_gas: - // Compute C_aaccess + // Compute C_access // stack: is_call_or_callcode, is_call_or_staticcall, cold_access, address, gas, kexit_info, value, retdest SWAP2 // stack: cold_access, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index 4756b40702..80f8f46188 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -57,6 +57,7 @@ global sys_create2: DUP5 // code_offset PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT + %build_address KECCAK_GENERAL // stack: hash, salt, create_common, value, code_offset, code_len, kexit_info @@ -99,11 +100,15 @@ global create_common: %set_new_ctx_code_size POP // Copy the code from memory to the new context's code segment. %stack (src_ctx, new_ctx, address, value, code_offset, code_len) - -> (new_ctx, @SEGMENT_CODE, 0, // DST - src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC + -> (src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC + new_ctx, // DST (SEGMENT_CODE == virt == 0) code_len, run_constructor, new_ctx, value, address) + %build_address + // stack: SRC, DST, code_len, run_constructor, new_ctx, value, address + SWAP1 + // stack: DST, SRC, code_len, run_constructor, new_ctx, value, address %jump(memcpy_bytes) run_constructor: @@ -144,7 +149,11 @@ after_constructor: POP // EIP-3541: Reject new contract code starting with the 0xEF byte - PUSH 0 %mload_current(@SEGMENT_RETURNDATA) %eq_const(0xEF) %jumpi(create_first_byte_ef) + PUSH @SEGMENT_RETURNDATA + GET_CONTEXT + %build_address_no_offset + MLOAD_GENERAL + %eq_const(0xEF) %jumpi(create_first_byte_ef) // Charge gas for the code size. // stack: leftover_gas, success, address, kexit_info @@ -160,9 +169,9 @@ after_constructor: %pop_checkpoint // Store the code hash of the new contract. - GET_CONTEXT %returndatasize - %stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len + PUSH @SEGMENT_RETURNDATA GET_CONTEXT %build_address_no_offset + // stack: addr, len KECCAK_GENERAL // stack: codehash, leftover_gas, success, address, kexit_info %observe_new_contract diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 70f57b6f0b..1cd50f661a 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -14,10 +14,7 @@ global get_create_address: %encode_rlp_scalar // stack: rlp_pos, rlp_start, retdest %prepend_rlp_list_prefix - // stack: rlp_prefix_start, rlp_len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: RLP_ADDR: 3, rlp_len, retdest + // stack: RLP_ADDR, rlp_len, retdest KECCAK_GENERAL // stack: hash, retdest %u256_to_addr @@ -41,19 +38,23 @@ global get_create_address: global get_create2_address: // stack: sender, code_hash, salt, retdest PUSH 0xff PUSH 0 %mstore_kernel_general - %stack (sender, code_hash, salt, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + %stack (sender, code_hash, salt, retdest) -> (@SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + ADD %jump(mstore_unpacking) get_create2_address_contd: POP - %stack (salt, code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + %stack (salt, code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + ADD %jump(mstore_unpacking) get_create2_address_contd2: POP - %stack (code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + %stack (code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + ADD %jump(mstore_unpacking) get_create2_address_finish: POP - %stack (retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 0, 85, retdest) // context, segment, offset, len + %stack (retdest) -> (@SEGMENT_KERNEL_GENERAL, 85, retdest) // offset == context == 0 + // addr, len, retdest KECCAK_GENERAL // stack: hash, retdest %u256_to_addr diff --git a/evm/src/cpu/kernel/asm/core/create_receipt.asm b/evm/src/cpu/kernel/asm/core/create_receipt.asm index 16a4fcaaeb..9f7cb9f89d 100644 --- a/evm/src/cpu/kernel/asm/core/create_receipt.asm +++ b/evm/src/cpu/kernel/asm/core/create_receipt.asm @@ -55,8 +55,8 @@ process_receipt_after_bloom: %get_trie_data_size // stack: receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write transaction type if necessary. RLP_RAW contains, at index 0, the current transaction type. - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL // stack: first_txn_byte, receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 %eq_const(1) %jumpi(receipt_nonzero_type) DUP1 %eq_const(2) %jumpi(receipt_nonzero_type) @@ -79,8 +79,10 @@ process_receipt_after_type: // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write Bloom filter. PUSH 256 // Bloom length. - PUSH 0 PUSH @SEGMENT_TXN_BLOOM PUSH 0 // Bloom memory address. - %get_trie_data_size PUSH @SEGMENT_TRIE_DATA PUSH 0 // MPT dest address. + PUSH @SEGMENT_TXN_BLOOM // ctx == virt == 0 + // stack: bloom_addr, 256, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest + %get_trie_data_size + PUSH @SEGMENT_TRIE_DATA ADD // MPT dest address. // stack: DST, SRC, 256, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest %memcpy_bytes // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest @@ -204,16 +206,14 @@ process_receipt_after_write: %mpt_insert_receipt_trie // stack: new_cum_gas, txn_nb, num_nibbles, retdest // Now, we set the Bloom filter back to 0. We proceed by chunks of 32 bytes. - PUSH 0 + PUSH @SEGMENT_TXN_BLOOM // ctx == offset == 0 %rep 8 - // stack: counter, new_cum_gas, txn_nb, num_nibbles, retdest + // stack: addr, new_cum_gas, txn_nb, num_nibbles, retdest PUSH 0 // we will fill the memory segment with zeroes DUP2 - PUSH @SEGMENT_TXN_BLOOM - DUP3 // kernel context is 0 - // stack: ctx, segment, counter, 0, counter, new_cum_gas, txn_nb, num_nibbles, retdes + // stack: addr, 0, addr, new_cum_gas, txn_nb, num_nibbles, retdest MSTORE_32BYTES_32 - // stack: new_counter, counter, new_cum_gas, txn_nb, num_nibbles, retdest + // stack: new_addr, addr, new_cum_gas, txn_nb, num_nibbles, retdest SWAP1 POP %endrep POP diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e63..a43f301af4 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -14,7 +14,8 @@ loop: %jumpi(return) // stack: i, ctx, code_len, retdest - %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) + %stack (i, ctx) -> (ctx, i, i, ctx) + ADD // combine context and offset to make an address (SEGMENT_CODE == 0) MLOAD_GENERAL // stack: opcode, i, ctx, code_len, retdest @@ -26,7 +27,10 @@ loop: %jumpi(continue) // stack: JUMPDEST, i, ctx, code_len, retdest - %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + %stack (JUMPDEST, i, ctx) -> (ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + %build_address + PUSH 1 + // stack: 1, addr, JUMPDEST, i, ctx MSTORE_GENERAL continue: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm index 01c027156f..500548eff5 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm @@ -29,7 +29,8 @@ global precompile_blake2_f: // stack: flag_addr, flag_addr, blake2_f_contd, kexit_info PUSH @SEGMENT_CALLDATA GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, flag_addr, flag_addr, blake2_f_contd, kexit_info + %build_address + // stack: addr, flag_addr, blake2_f_contd, kexit_info MLOAD_GENERAL // stack: flag, flag_addr, blake2_f_contd, kexit_info DUP1 @@ -45,6 +46,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: t_1, t1_addr, flag, blake2_f_contd, kexit_info SWAP1 @@ -56,6 +58,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: t_0, t0_addr, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -71,6 +74,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: m_i, m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -88,6 +92,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: h_i, h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -96,9 +101,10 @@ global precompile_blake2_f: // stack: h0_addr + 8 * 8 = 68, h_0, ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info POP - %stack () -> (@SEGMENT_CALLDATA, 0, 4) + %stack () -> (@SEGMENT_CALLDATA, 4) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address_no_offset %mload_packing // stack: rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info @@ -113,20 +119,20 @@ blake2_f_contd: // Store the result hash to the parent's return data using `mstore_unpacking_u64_LE`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) - PUSH 0 - // stack: addr_0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + PUSH @SEGMENT_RETURNDATA %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - // stack: parent_ctx, addr_0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + // stack: parent_ctx, segment, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + %build_address_no_offset + // stack: addr0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info %rep 8 - // stack: parent_ctx, addr_i, h_i', ..., h_7', kexit_info - %stack (ctx, addr, h_i) -> (ctx, @SEGMENT_RETURNDATA, addr, h_i, addr, ctx) + // stack: addri, h_i', ..., h_7', kexit_info + %stack (addr, h_i) -> (addr, h_i, addr) %mstore_unpacking_u64_LE - // stack: addr_i, parent_ctx, h_(i+1)', ..., h_7', kexit_info + // stack: addr_i, h_(i+1)', ..., h_7', kexit_info %add_const(8) - // stack: addr_(i+1), parent_ctx, h_(i+1)', ..., h_7', kexit_info - SWAP1 - // stack: parent_ctx, addr_(i+1), h_(i+1)', ..., h_7', kexit_info + // stack: addr_(i+1), h_(i+1)', ..., h_7', kexit_info %endrep // stack: kexit_info diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm index 1dafbe8a43..dcd641a327 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm @@ -20,21 +20,25 @@ global precompile_bn_add: %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, bn_add_return, kexit_info + %build_address %mload_packing // stack: y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, y1, bn_add_return, kexit_info + %build_address %mload_packing // stack: x1, y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, x1, y1, bn_add_return, kexit_info + %build_address %mload_packing // stack: y0, x1, y1, bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, y0, x1, y1, bn_add_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, y0, x1, y1, bn_add_return, kexit_info + %build_address_no_offset %mload_packing // stack: x0, y0, x1, y1, bn_add_return, kexit_info %jump(bn_add) @@ -49,9 +53,11 @@ bn_add_return: // Store the result (x, y) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, x, 32, bn_add_contd6, parent_ctx, y) + %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, x, 32, bn_add_contd6, parent_ctx, y) + %build_address_no_offset %jump(mstore_unpacking) bn_add_contd6: POP %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y, 32, pop_and_return_success) + %build_address %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm index b3865506d8..df2e27e99f 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm @@ -20,16 +20,19 @@ global precompile_bn_mul: %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, bn_mul_return, kexit_info + %build_address %mload_packing // stack: n, bn_mul_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, n, bn_mul_return, kexit_info + %build_address %mload_packing // stack: y, n, bn_mul_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, y, n, bn_mul_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, y, n, bn_mul_return, kexit_info + %build_address_no_offset %mload_packing // stack: x, y, n, bn_mul_return, kexit_info %jump(bn_mul) @@ -44,9 +47,11 @@ bn_mul_return: // Store the result (Px, Py) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, Px, 32, bn_mul_contd6, parent_ctx, Py) + %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, Px, 32, bn_mul_contd6, parent_ctx, Py) + %build_address_no_offset %jump(mstore_unpacking) bn_mul_contd6: POP %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py, 32, pop_and_return_success) + %build_address %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm index b38307c4db..baa661962d 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm @@ -20,21 +20,25 @@ global precompile_ecrec: %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, ecrec_return, kexit_info + %build_address %mload_packing // stack: s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, s, ecrec_return, kexit_info + %build_address %mload_packing // stack: r, s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, r, s, ecrec_return, kexit_info + %build_address %mload_packing // stack: v, r, s, ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, v, r, s, ecrec_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, v, r, s, ecrec_return, kexit_info + %build_address_no_offset %mload_packing // stack: hash, v, r, s, ecrec_return, kexit_info %jump(ecrecover) @@ -45,7 +49,8 @@ ecrec_return: // Store the result address to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) // On bad input, return empty return data but still return success. diff --git a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm index 2185ee2c9d..52bb220dab 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm @@ -11,43 +11,43 @@ // We pass around total_num_limbs and len for conveience, because we can't access them from the stack // if they're hidden behind the variable number of limbs. mload_bytes_as_limbs: - // stack: ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - DUP4 - // stack: num_bytes, ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs + // stack: addr, num_bytes, retdest, total_num_limbs, len, ..limbs + DUP2 + // stack: num_bytes, addr, num_bytes, retdest, total_num_limbs, len, ..limbs %mod_16 - // stack: min(16, num_bytes), ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - %stack (len, addr: 3) -> (addr, len, addr) - // stack: ctx, segment, offset, min(16, num_bytes), ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs + // stack: min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs + DUP2 + // stack: addr, min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs %mload_packing - // stack: new_limb, ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - %stack (new, addr: 3, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) - // stack: num_bytes, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: new_limb, addr, num_bytes, retdest, total_num_limbs, len, ..limbs + %stack (new, addr, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) + // stack: num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 %mod_16 - // stack: num_bytes%16, num_bytes, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: num_bytes%16, num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 SWAP2 SUB - // stack:num_bytes_new, num_bytes%16, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 ISZERO %jumpi(mload_bytes_return) SWAP1 - // stack: num_bytes%16, num_bytes_new, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - DUP5 // offset - ADD - // stack: offset_new, num_bytes_new, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - SWAP4 POP - // stack: num_bytes_new, ctx, segment, offset_new, retdest, total_num_limbs, len, new_limb, ..limbs - %stack (num, addr: 3) -> (addr, num) + // stack: num_bytes%16, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs + DUP3 // addr + ADD // increment offset + // stack: addr_new, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs + SWAP2 POP + // stack: num_bytes_new, addr_new, retdest, total_num_limbs, len, new_limb, ..limbs + SWAP1 %jump(mload_bytes_as_limbs) mload_bytes_return: - // stack: num_bytes_new, num_bytes%16, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - %pop5 + // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs + %pop3 // stack: retdest, total_num_limbs, len, ..limbs JUMP %macro mload_bytes_as_limbs - %stack (ctx, segment, offset, num_bytes, total_num_limbs) -> (ctx, segment, offset, num_bytes, %%after, total_num_limbs) + %stack (addr, num_bytes, total_num_limbs) -> (addr, num_bytes, %%after, total_num_limbs) %jump(mload_bytes_as_limbs) %%after: %endmacro @@ -112,6 +112,7 @@ calculate_l_E_prime: // stack: 96 + l_B, 32, l_E, l_B, retdest PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address %mload_packing // stack: i[96 + l_B..128 + l_B], l_E, l_B, retdest %log2_floor @@ -142,6 +143,7 @@ case_le_32: // stack: 96 + l_B, l_E, retdest PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address %mload_packing // stack: E, retdest %log2_floor @@ -165,22 +167,25 @@ global precompile_expmod: // stack: kexit_info // Load l_B from i[0..32]. - %stack () -> (@SEGMENT_CALLDATA, 0, 32) - // stack: @SEGMENT_CALLDATA, 0, 32, kexit_info + %stack () -> (@SEGMENT_CALLDATA, 32) + // stack: @SEGMENT_CALLDATA, 32, kexit_info GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, kexit_info + %build_address_no_offset %mload_packing // stack: l_B, kexit_info // Load l_E from i[32..64]. %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT + %build_address %mload_packing // stack: l_E, l_B, kexit_info // Load l_M from i[64..96]. %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT + %build_address %mload_packing // stack: l_M, l_E, l_B, kexit_info DUP3 ISZERO DUP2 ISZERO @@ -247,6 +252,7 @@ l_E_prime_return: %stack () -> (@SEGMENT_CALLDATA, 96) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -282,6 +288,7 @@ copy_b_end: PUSH @SEGMENT_CALLDATA GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -316,6 +323,7 @@ copy_e_end: PUSH @SEGMENT_CALLDATA GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B + l_E, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -410,33 +418,33 @@ expmod_contd: DUP2 DUP2 ADD - // stack: cur_address=out+l_M_128-1, end_address=out-1, l_M_128, l_M%16, kexit_info + // stack: cur_offset=out+l_M_128-1, end_offset=out-1, l_M_128, l_M%16, kexit_info DUP1 %mload_current_general - %stack (cur_limb, cur_address, end_address, l_M_128, l_M_mod16, kexit_info) -> - (@SEGMENT_RETURNDATA, 0, cur_limb, l_M_mod16, cur_address, end_address, l_M_128, kexit_info) + %stack (cur_limb, cur_offset, end_offset, l_M_128, l_M_mod16, kexit_info) -> + (@SEGMENT_RETURNDATA, cur_limb, l_M_mod16, cur_offset, end_offset, l_M_128, kexit_info) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %build_address_no_offset %mstore_unpacking - // stack: offset, cur_address, end_address, l_M_128, kexit_info + // stack: address, cur_offset, end_offset, l_M_128, kexit_info SWAP1 %decrement - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info // Store in big-endian format. expmod_store_loop: - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info DUP3 DUP2 EQ %jumpi(expmod_store_end) - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info DUP1 %mload_current_general - %stack (cur_limb, cur_address, offset, end_address, l_M_128, kexit_info) -> - (offset, cur_limb, cur_address, end_address, l_M_128, kexit_info) - %stack (offset, cur_limb) -> (@SEGMENT_RETURNDATA, offset, cur_limb, 16) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (cur_limb, cur_offset, address, end_offset, l_M_128, kexit_info) -> + (address, cur_limb, cur_offset, end_offset, l_M_128, kexit_info) + %stack (address, cur_limb) -> (address, cur_limb, 16) %mstore_unpacking - // stack: offset', cur_address, end_address, l_M_128, kexit_info) + // stack: address', cur_offset, end_offset, l_M_128, kexit_info) SWAP1 %decrement - // stack: cur_address-1, offset', end_address, l_M_128, kexit_info) + // stack: cur_offset-1, address', end_offset, l_M_128, kexit_info) %jump(expmod_store_loop) expmod_store_end: - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info %pop4 the_end: // stack: kexit_info diff --git a/evm/src/cpu/kernel/asm/core/precompiles/id.asm b/evm/src/cpu/kernel/asm/core/precompiles/id.asm index 83cee0d042..a606ef4a85 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/id.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/id.asm @@ -24,14 +24,19 @@ global precompile_id: // Simply copy the call data to the parent's return data. %calldatasize DUP1 %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) + + PUSH id_contd SWAP1 + + PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address_no_offset + // stack: SRC, size, id_contd + + PUSH @SEGMENT_RETURNDATA %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, ctx, size) -> - ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_CALLDATA, 0, // SRC - size, id_contd // count, retdest - ) + %build_address_no_offset + + // stack: DST, SRC, size, id_contd %jump(memcpy_bytes) id_contd: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/main.asm b/evm/src/cpu/kernel/asm/core/precompiles/main.asm index d6cb100bdc..b7c916e9c4 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/main.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/main.asm @@ -58,7 +58,9 @@ global handle_precompiles_from_eoa: %mload_txn_field(@TXN_FIELD_DATA_LEN) %stack (calldata_size, new_ctx) -> (calldata_size, new_ctx, calldata_size) %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) + %stack (new_ctx, calldata_size) -> (@SEGMENT_TXN_DATA, @SEGMENT_CALLDATA, new_ctx, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) + SWAP2 %build_address_no_offset // DST + // stack: DST, SRC, calldata_size, handle_precompiles_from_eoa_finish, new_ctx %jump(memcpy_bytes) handle_precompiles_from_eoa_finish: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm index 0e5aee8cb1..0231baf0eb 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm @@ -25,27 +25,17 @@ global precompile_rip160: %calldatasize GET_CONTEXT - // The next block of code is equivalent to the following %stack macro call - // (unfortunately the macro call takes too long to expand dynamically). - // - // %stack (ctx, size) -> - // ( - // ctx, @SEGMENT_KERNEL_GENERAL, 200, // DST - // ctx, @SEGMENT_CALLDATA, 0, // SRC - // size, ripemd, // count, retdest - // 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest - // ) - PUSH 200 - PUSH ripemd - DUP4 - PUSH 0 - PUSH @SEGMENT_CALLDATA - PUSH rip160_contd - SWAP7 - SWAP6 - PUSH 200 - PUSH @SEGMENT_KERNEL_GENERAL - DUP3 + %stack (ctx, size) -> + ( + ctx, @SEGMENT_CALLDATA, // SRC + ctx, + size, ripemd, // count, retdest + 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest + ) + %build_address_no_offset + %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 200, addr) + %build_address + // stack: DST, SRC, count, retdest, virt, num_bytes, retdest %jump(memcpy_bytes) @@ -54,5 +44,6 @@ rip160_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm index 6dad0745ba..b537cd4c7b 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm @@ -24,30 +24,18 @@ global precompile_sha256: // Copy the call data to the kernel general segment (sha2 expects it there) and call sha2. %calldatasize GET_CONTEXT - // stack: ctx, size - // The next block of code is equivalent to the following %stack macro call - // (unfortunately the macro call takes too long to expand dynamically). - // - // %stack (ctx, size) -> - // ( - // ctx, @SEGMENT_KERNEL_GENERAL, 1, // DST - // ctx, @SEGMENT_CALLDATA, 0, // SRC - // size, sha2, // count, retdest - // 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest - // ) - // - PUSH 0 - PUSH sha2 - DUP4 - PUSH 0 - PUSH @SEGMENT_CALLDATA - PUSH sha256_contd - SWAP7 - SWAP6 - PUSH 1 - PUSH @SEGMENT_KERNEL_GENERAL - DUP3 + %stack (ctx, size) -> + ( + ctx, @SEGMENT_CALLDATA, // SRC + ctx, + size, sha2, // count, retdest + 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest + ) + %build_address_no_offset + %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 1, addr) + %build_address + // stack: DST, SRC, count, retdest, virt, num_bytes, retdest %jump(memcpy_bytes) @@ -56,5 +44,6 @@ sha256_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm index f128cd51ad..2d990d09d9 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm @@ -31,18 +31,21 @@ loading_loop: // stack: px, i, k, kexit_info GET_CONTEXT %stack (ctx, px) -> (ctx, @SEGMENT_CALLDATA, px, 32, loading_loop_contd, px) + %build_address %jump(mload_packing) loading_loop_contd: // stack: x, px, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT %stack (ctx, py) -> (ctx, @SEGMENT_CALLDATA, py, 32, loading_loop_contd2, py) + %build_address %jump(mload_packing) loading_loop_contd2: // stack: y, py, x, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT %stack (ctx, px_im) -> (ctx, @SEGMENT_CALLDATA, px_im, 32, loading_loop_contd3, px_im) + %build_address %jump(mload_packing) loading_loop_contd3: // stack: x_im, px_im, y, x, i, k, kexit_info @@ -50,6 +53,7 @@ loading_loop_contd3: // stack: px_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, px_re) -> (ctx, @SEGMENT_CALLDATA, px_re, 32, loading_loop_contd4, px_re) + %build_address %jump(mload_packing) loading_loop_contd4: // stack: x_re, px_re, x_im, y, x, i, k, kexit_info @@ -57,6 +61,7 @@ loading_loop_contd4: // stack: py_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, py_im) -> (ctx, @SEGMENT_CALLDATA, py_im, 32, loading_loop_contd5, py_im) + %build_address %jump(mload_packing) loading_loop_contd5: // stack: y_im, py_im, x_re, x_im, y, x, i, k, kexit_info @@ -64,6 +69,7 @@ loading_loop_contd5: // stack: py_re, y_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, py_re) -> (ctx, @SEGMENT_CALLDATA, py_re, 32, loading_loop_contd6) + %build_address %jump(mload_packing) loading_loop_contd6: // stack: y_re, y_im, x_re, x_im, y, x, i, k, kexit_info @@ -118,5 +124,6 @@ got_result: // Store the result bool (repr. by a U256) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index df39b9d814..c70287a6f9 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -12,11 +12,11 @@ global process_normalized_txn: // Compute this transaction's intrinsic gas and store it. %intrinsic_gas + DUP1 %mstore_txn_field(@TXN_FIELD_INTRINSIC_GAS) - // stack: retdest + // stack: intrinsic_gas, retdest // Assert gas_limit >= intrinsic_gas. - %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) %mload_txn_field(@TXN_FIELD_GAS_LIMIT) %assert_ge(invalid_txn) @@ -146,23 +146,20 @@ global process_contract_creation_txn: // Store constructor code length PUSH @CTX_METADATA_CODE_SIZE - PUSH @SEGMENT_CONTEXT_METADATA - // stack: segment, offset, new_ctx, address, retdest - DUP3 // new_ctx + // stack: offset, new_ctx, address, retdest + DUP2 // new_ctx + ADD // CTX_METADATA_CODE_SIZE is already scaled by its segment + // stack: addr, new_ctx, address, retdest %mload_txn_field(@TXN_FIELD_DATA_LEN) - // stack: data_len, new_ctx, segment, offset, new_ctx, address, retdest + // stack: data_len, addr, new_ctx, address, retdest MSTORE_GENERAL // stack: new_ctx, address, retdest // Copy the code from txdata to the new context's code segment. PUSH process_contract_creation_txn_after_code_loaded %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // SRC.offset - PUSH @SEGMENT_TXN_DATA // SRC.segment - PUSH 0 // SRC.context - PUSH 0 // DST.offset - PUSH @SEGMENT_CODE // DST.segment - DUP8 // DST.context = new_ctx + PUSH @SEGMENT_TXN_DATA // SRC (context == offset == 0) + DUP4 // DST (segment == 0 (i.e. CODE), and offset == 0) %jump(memcpy_bytes) global process_contract_creation_txn_after_code_loaded: @@ -203,9 +200,11 @@ global process_contract_creation_txn_after_constructor: // Store the code hash of the new contract. // stack: leftover_gas, new_ctx, address, retdest, success - GET_CONTEXT %returndatasize - %stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len + PUSH @SEGMENT_RETURNDATA + GET_CONTEXT + %build_address_no_offset + // stack: addr, len KECCAK_GENERAL // stack: codehash, leftover_gas, new_ctx, address, retdest, success %observe_new_contract @@ -292,7 +291,8 @@ global process_message_txn_code_loaded: %mload_txn_field(@TXN_FIELD_DATA_LEN) %stack (calldata_size, new_ctx, retdest) -> (calldata_size, new_ctx, calldata_size, retdest) %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size, retdest) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, process_message_txn_code_loaded_finish, new_ctx, retdest) + %stack (new_ctx, calldata_size, retdest) -> (new_ctx, @SEGMENT_CALLDATA, @SEGMENT_TXN_DATA, calldata_size, process_message_txn_code_loaded_finish, new_ctx, retdest) + %build_address_no_offset // DST %jump(memcpy_bytes) process_message_txn_code_loaded_finish: diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index d2c16b51c0..9b52591933 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -29,18 +29,26 @@ return_after_gas: // Store the return data size in the parent context's metadata. %stack (parent_ctx, kexit_info, offset, size) -> - (size, parent_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_RETURNDATA_SIZE, offset, size, parent_ctx, kexit_info) + (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) + ADD // addr (CTX offsets are already scaled by their segment) + SWAP1 + // stack: size, addr, offset, size, parent_ctx, kexit_info MSTORE_GENERAL // stack: offset, size, parent_ctx, kexit_info // Store the return data in the parent context's returndata segment. + PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - %stack (ctx, offset, size, parent_ctx, kexit_info) -> + %build_address + + %stack (addr, size, parent_ctx, kexit_info) -> ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, offset, // SRC + parent_ctx, @SEGMENT_RETURNDATA, // DST + addr, // SRC size, sys_return_finish, kexit_info // count, retdest, ... ) + %build_address_no_offset + // stack: DST, SRC, size, sys_return_finish, kexit_info %jump(memcpy_bytes) sys_return_finish: @@ -129,18 +137,26 @@ revert_after_gas: // Store the return data size in the parent context's metadata. %stack (parent_ctx, kexit_info, offset, size) -> - (size, parent_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_RETURNDATA_SIZE, offset, size, parent_ctx, kexit_info) + (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) + ADD // addr (CTX offsets are already scaled by their segment) + SWAP1 + // stack: size, addr, offset, size, parent_ctx, kexit_info MSTORE_GENERAL // stack: offset, size, parent_ctx, kexit_info // Store the return data in the parent context's returndata segment. + PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - %stack (ctx, offset, size, parent_ctx, kexit_info) -> + %build_address + + %stack (addr, size, parent_ctx, kexit_info) -> ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, offset, // SRC + parent_ctx, @SEGMENT_RETURNDATA, // DST + addr, // SRC size, sys_revert_finish, kexit_info // count, retdest, ... ) + %build_address_no_offset + // stack: DST, SRC, size, sys_revert_finish, kexit_info %jump(memcpy_bytes) sys_revert_finish: diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm index ee33ff26ca..a77329bd8c 100644 --- a/evm/src/cpu/kernel/asm/core/util.asm +++ b/evm/src/cpu/kernel/asm/core/util.asm @@ -11,7 +11,7 @@ %macro next_context_id // stack: (empty) %mload_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) - %increment + %add_const(0x10000000000000000) // scale each context by 2^64 // stack: new_ctx DUP1 %mstore_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) @@ -83,7 +83,6 @@ SET_CONTEXT // stack: (empty) // We can now read this stack length from memory. - push @CTX_METADATA_STACK_SIZE - %mload_current(@SEGMENT_CONTEXT_METADATA) + %mload_context_metadata(@CTX_METADATA_STACK_SIZE) // stack: stack_length %endmacro diff --git a/evm/src/cpu/kernel/asm/curve/wnaf.asm b/evm/src/cpu/kernel/asm/curve/wnaf.asm index a416d1ba79..f554bc649d 100644 --- a/evm/src/cpu/kernel/asm/curve/wnaf.asm +++ b/evm/src/cpu/kernel/asm/curve/wnaf.asm @@ -34,8 +34,12 @@ wnaf_loop_contd: DUP2 SWAP1 SUB %stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest) SUB + // stack: i, m, o, segment, n, retdest + DUP4 GET_CONTEXT - %stack (ctx, i, m, o, segment, n, retdest) -> (m, ctx, segment, i, o, segment, n, retdest) + %build_address + // stack: addr, m, o, segment, n, retdest + SWAP1 MSTORE_GENERAL // stack: o, segment, n, retdest DUP3 ISZERO %jumpi(wnaf_end) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index b495d49947..42e326a0ab 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -2,9 +2,7 @@ global main: // First, hash the kernel code %mload_global_metadata(@GLOBAL_METADATA_KERNEL_LEN) PUSH 0 - PUSH 0 - PUSH 0 - // stack: context, segment, virt, len + // stack: addr, len KECCAK_GENERAL // stack: hash %mload_global_metadata(@GLOBAL_METADATA_KERNEL_HASH) @@ -13,6 +11,10 @@ global main: // Initialise the shift table %shift_table_init + + // Initialize the RLP DATA pointer to its initial position (ctx == virt == 0, segment = RLP) + PUSH @SEGMENT_RLP_RAW + %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) // Initialize the state, transaction and receipt trie root pointers. PROVER_INPUT(trie_ptr::state) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index a4c99cec15..3a3a17a500 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -1,39 +1,31 @@ // Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mload_u32 - // stack: context, segment, offset - %stack (addr: 3) -> (addr, 4, %%after) + // stack: addr + %stack (addr) -> (addr, 4, %%after) %jump(mload_packing) %%after: %endmacro // Load a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3). %macro mload_u32_LE - // stack: context, segment, offset - DUP3 - DUP3 - DUP3 + // stack: addr + DUP1 MLOAD_GENERAL - // stack: c0, context, segment, offset - DUP4 + // stack: c0, addr + DUP2 %increment - DUP4 - DUP4 MLOAD_GENERAL %shl_const(8) ADD - // stack: c0 | (c1 << 8), context, segment, offset - DUP4 + // stack: c0 | (c1 << 8), addr + DUP2 %add_const(2) - DUP4 - DUP4 MLOAD_GENERAL %shl_const(16) ADD - // stack: c0 | (c1 << 8) | (c2 << 16), context, segment, offset - SWAP3 - %add_const(3) - SWAP2 + // stack: c0 | (c1 << 8) | (c2 << 16), addr SWAP1 + %add_const(3) MLOAD_GENERAL %shl_const(24) ADD // OR @@ -42,16 +34,12 @@ // Load a little-endian u64, consisting of 8 bytes (c_0, ..., c_7). %macro mload_u64_LE - // stack: context, segment, offset - DUP3 - DUP3 - DUP3 + // stack: addr + DUP1 %mload_u32_LE - // stack: lo, context, segment, offset - SWAP3 - %add_const(4) - SWAP2 + // stack: lo, addr SWAP1 + %add_const(4) %mload_u32_LE // stack: hi, lo %shl_const(32) @@ -62,16 +50,16 @@ // Load a big-endian u256. %macro mload_u256 - // stack: context, segment, offset - %stack (addr: 3) -> (addr, 32, %%after) + // stack: addr + %stack (addr) -> (addr, 32, %%after) %jump(mload_packing) %%after: %endmacro // Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mstore_u32 - // stack: context, segment, offset, value - %stack (addr: 3, value) -> (addr, value, 4, %%after) + // stack: addr, value + %stack (addr, value) -> (addr, value, 4, %%after) %jump(mstore_unpacking) %%after: // stack: offset @@ -88,6 +76,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address MLOAD_GENERAL // stack: value %endmacro @@ -102,7 +91,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -115,7 +105,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -123,7 +114,10 @@ // Load a single byte from user code. %macro mload_current_code // stack: offset - %mload_current(@SEGMENT_CODE) + // SEGMENT_CODE == 0 + GET_CONTEXT ADD + // stack: addr + MLOAD_GENERAL // stack: value %endmacro @@ -141,6 +135,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u32 // stack: value %endmacro @@ -152,6 +147,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u32_LE // stack: value %endmacro @@ -163,6 +159,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u64_LE // stack: value %endmacro @@ -174,6 +171,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u256 // stack: value %endmacro @@ -185,7 +183,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -205,6 +204,7 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value + %build_address %mstore_u32 // stack: (empty) %endmacro @@ -224,8 +224,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address MLOAD_GENERAL // stack: value %endmacro @@ -235,9 +234,9 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_kernel_address + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -249,9 +248,9 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_kernel_address + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -261,8 +260,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u32 %endmacro @@ -271,8 +269,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u32_LE %endmacro @@ -281,8 +278,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u64_LE %endmacro @@ -291,8 +287,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u256 %endmacro @@ -302,15 +297,16 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value + %build_kernel_address + // stack: addr, value %mstore_u32 %endmacro // Load a single byte from kernel code. %macro mload_kernel_code // stack: offset - %mload_kernel(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + MLOAD_GENERAL // stack: value %endmacro @@ -327,7 +323,8 @@ // from kernel code. %macro mload_kernel_code_u32 // stack: offset - %mload_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u32 // stack: value %endmacro @@ -338,7 +335,8 @@ PUSH $label ADD // stack: offset - %mload_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u32 // stack: value %endmacro @@ -383,7 +381,8 @@ // Load a u256 (big-endian) from kernel code. %macro mload_kernel_code_u256 // stack: offset - %mload_kernel_u256(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u256 // stack: value %endmacro @@ -397,7 +396,8 @@ // Store a single byte to kernel code. %macro mstore_kernel_code // stack: offset, value - %mstore_kernel(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + MLOAD_GENERAL // stack: (empty) %endmacro @@ -405,13 +405,15 @@ // to kernel code. %macro mstore_kernel_code_u32 // stack: offset, value - %mstore_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mstore_u32 %endmacro // Store a single byte to @SEGMENT_RLP_RAW. %macro mstore_rlp - // stack: offset, value - %mstore_kernel(@SEGMENT_RLP_RAW) + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memcpy.asm b/evm/src/cpu/kernel/asm/memory/memcpy.asm index 9dd2305d8d..a7819bf6e8 100644 --- a/evm/src/cpu/kernel/asm/memory/memcpy.asm +++ b/evm/src/cpu/kernel/asm/memory/memcpy.asm @@ -1,25 +1,16 @@ -// Copies `count` values from -// SRC = (src_ctx, src_segment, src_addr) -// to -// DST = (dst_ctx, dst_segment, dst_addr). -// These tuple definitions are used for brevity in the stack comments below. +// Copies `count` values from SRC to DST. global memcpy: // stack: DST, SRC, count, retdest - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest ISZERO // stack: count == 0, DST, SRC, count, retdest %jumpi(memcpy_finish) // stack: DST, SRC, count, retdest - DUP3 - DUP3 - DUP3 + DUP1 - // Copy the next value - // stack: DST, DST, SRC, count, retdest - DUP9 - DUP9 - DUP9 + // Copy the next value. + DUP3 // stack: SRC, DST, DST, SRC, count, retdest MLOAD_GENERAL // stack: value, DST, DST, SRC, count, retdest @@ -27,23 +18,19 @@ global memcpy: // stack: DST, SRC, count, retdest // Increment dst_addr. - SWAP2 %increment - SWAP2 // Increment src_addr. - SWAP5 + SWAP1 %increment - SWAP5 + SWAP1 // Decrement count. - SWAP6 - %decrement - SWAP6 + PUSH 1 DUP4 SUB SWAP3 POP // Continue the loop. %jump(memcpy) %macro memcpy - %stack (dst: 3, src: 3, count) -> (dst, src, count, %%after) + %stack (dst, src, count) -> (dst, src, count, %%after) %jump(memcpy) %%after: %endmacro @@ -53,7 +40,7 @@ global memcpy_bytes: // stack: DST, SRC, count, retdest // Handle small case - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest %lt_const(0x21) // stack: count <= 32, DST, SRC, count, retdest @@ -61,31 +48,22 @@ global memcpy_bytes: // We will pack 32 bytes into a U256 from the source, and then unpack it at the destination. // Copy the next chunk of bytes. + // stack: DST, SRC, count, retdest PUSH 32 - DUP7 - DUP7 - DUP7 + DUP3 // stack: SRC, 32, DST, SRC, count, retdest MLOAD_32BYTES // stack: value, DST, SRC, count, retdest - DUP4 - DUP4 - DUP4 - // stack: DST, value, DST, SRC, count, retdest + SWAP1 + // stack: DST, value, SRC, count, retdest MSTORE_32BYTES_32 - // stack: new_offset, DST, SRC, count, retdest - // Increment dst_addr by 32. - SWAP3 - POP - // stack: DST, SRC, count, retdest - // Increment src_addr by 32. - SWAP5 + // stack: DST', SRC, count, retdest + // Increment SRC by 32. + SWAP1 %add_const(0x20) - SWAP5 + SWAP1 // Decrement count by 32. - SWAP6 - %sub_const(0x20) - SWAP6 + PUSH 32 DUP4 SUB SWAP3 POP // Continue the loop. %jump(memcpy_bytes) @@ -94,7 +72,7 @@ memcpy_bytes_finish: // stack: DST, SRC, count, retdest // Handle empty case - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest ISZERO // stack: count == 0, DST, SRC, count, retdest @@ -103,17 +81,13 @@ memcpy_bytes_finish: // stack: DST, SRC, count, retdest // Copy the last chunk of `count` bytes. - DUP7 + DUP3 DUP1 - DUP8 - DUP8 - DUP8 + DUP4 // stack: SRC, count, count, DST, SRC, count, retdest MLOAD_32BYTES // stack: value, count, DST, SRC, count, retdest - DUP5 - DUP5 - DUP5 + DUP3 // stack: DST, value, count, DST, SRC, count, retdest %mstore_unpacking // stack: new_offset, DST, SRC, count, retdest @@ -121,12 +95,12 @@ memcpy_bytes_finish: memcpy_finish: // stack: DST, SRC, count, retdest - %pop7 + %pop3 // stack: retdest JUMP %macro memcpy_bytes - %stack (dst: 3, src: 3, count) -> (dst, src, count, %%after) + %stack (dst, src, count) -> (dst, src, count, %%after) %jump(memcpy_bytes) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memset.asm b/evm/src/cpu/kernel/asm/memory/memset.asm index 97e5dae876..792aeabc68 100644 --- a/evm/src/cpu/kernel/asm/memory/memset.asm +++ b/evm/src/cpu/kernel/asm/memory/memset.asm @@ -1,11 +1,9 @@ -// Sets `count` values to 0 at -// DST = (dst_ctx, dst_segment, dst_addr). -// This tuple definition is used for brevity in the stack comments below. +// Sets `count` values to 0 at DST. global memset: // stack: DST, count, retdest // Handle small case - DUP4 + DUP2 // stack: count, DST, count, retdest %lt_const(0x21) // stack: count <= 32, DST, count, retdest @@ -13,20 +11,12 @@ global memset: // stack: DST, count, retdest PUSH 0 - DUP4 - DUP4 - DUP4 - // stack: DST, 0, DST, count, retdest + SWAP1 + // stack: DST, 0, count, retdest MSTORE_32BYTES_32 - // stack: new_offset, DST, count, retdest - - // Update dst_addr. - SWAP3 - POP + // stack: DST', count, retdest // Decrement count. - SWAP3 - %sub_const(0x20) - SWAP3 + PUSH 32 DUP3 SUB SWAP2 POP // Continue the loop. %jump(memset) @@ -35,27 +25,25 @@ memset_finish: // stack: DST, final_count, retdest // Handle empty case - DUP4 + DUP2 // stack: final_count, DST, final_count, retdest ISZERO // stack: final_count == 0, DST, final_count, retdest %jumpi(memset_bytes_empty) // stack: DST, final_count, retdest - DUP4 + DUP2 PUSH 0 - DUP5 - DUP5 - DUP5 + DUP3 // stack: DST, 0, final_count, DST, final_count, retdest %mstore_unpacking - // stack: new_offset, DST, final_count, retdest - %pop5 + // stack: DST, final_count, retdest + %pop3 // stack: retdest JUMP memset_bytes_empty: // stack: DST, 0, retdest - %pop4 + %pop2 // stack: retdest JUMP diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 625b57f179..dfbfb6460b 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -1,62 +1,104 @@ // Load the given global metadata field from memory. %macro mload_global_metadata(field) + // Global metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_kernel(@SEGMENT_GLOBAL_METADATA) + MLOAD_GENERAL // stack: value %endmacro // Store the given global metadata field to memory. %macro mstore_global_metadata(field) + // Global metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_kernel(@SEGMENT_GLOBAL_METADATA) + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro // Load the given context metadata field from memory. %macro mload_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr + MLOAD_GENERAL // stack: value %endmacro // Store the given context metadata field to memory. %macro mstore_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro // Store the given context metadata field to memory. %macro mstore_context_metadata(field, value) - PUSH $value + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + PUSH $field - // stack: offset, value - %mstore_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr + PUSH $value + // stack: value, addr + MSTORE_GENERAL // stack: (empty) %endmacro %macro mstore_parent_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, value) -> - (value, parent_ctx, @SEGMENT_CONTEXT_METADATA, $field) + + // stack: parent_ctx, value + PUSH $field ADD + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro %macro mstore_parent_context_metadata(field, value) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx) -> - ($value, parent_ctx, @SEGMENT_CONTEXT_METADATA, $field) + + // stack: parent_ctx + PUSH $field ADD + // stack: addr + PUSH $value + // stack: value, addr MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/packing.asm b/evm/src/cpu/kernel/asm/memory/packing.asm index 1feeeaf8fa..9b2d6cdd3b 100644 --- a/evm/src/cpu/kernel/asm/memory/packing.asm +++ b/evm/src/cpu/kernel/asm/memory/packing.asm @@ -2,11 +2,10 @@ // decoding bytes as integers. All big-endian. // Given a pointer to some bytes in memory, pack them into a word. Assumes 0 < len <= 32. -// Pre stack: addr: 3, len, retdest +// Pre stack: addr, len, retdest // Post stack: packed_value -// NOTE: addr: 3 denotes a (context, segment, virtual) tuple global mload_packing: - // stack: addr: 3, len, retdest + // stack: addr, len, retdest MLOAD_32BYTES // stack: packed_value, retdest SWAP1 @@ -14,50 +13,50 @@ global mload_packing: JUMP %macro mload_packing - %stack (addr: 3, len) -> (addr, len, %%after) + %stack (addr, len) -> (addr, len, %%after) %jump(mload_packing) %%after: %endmacro global mload_packing_u64_LE: - // stack: context, segment, offset, retdest - DUP3 DUP3 DUP3 MLOAD_GENERAL - DUP4 %add_const(1) DUP4 DUP4 MLOAD_GENERAL %shl_const( 8) ADD - DUP4 %add_const(2) DUP4 DUP4 MLOAD_GENERAL %shl_const(16) ADD - DUP4 %add_const(3) DUP4 DUP4 MLOAD_GENERAL %shl_const(24) ADD - DUP4 %add_const(4) DUP4 DUP4 MLOAD_GENERAL %shl_const(32) ADD - DUP4 %add_const(5) DUP4 DUP4 MLOAD_GENERAL %shl_const(40) ADD - DUP4 %add_const(6) DUP4 DUP4 MLOAD_GENERAL %shl_const(48) ADD - DUP4 %add_const(7) DUP4 DUP4 MLOAD_GENERAL %shl_const(56) ADD - %stack (value, context, segment, offset, retdest) -> (retdest, value) + // stack: addr, retdest + DUP1 MLOAD_GENERAL + DUP2 %add_const(1) MLOAD_GENERAL %shl_const( 8) ADD + DUP2 %add_const(2) MLOAD_GENERAL %shl_const(16) ADD + DUP2 %add_const(3) MLOAD_GENERAL %shl_const(24) ADD + DUP2 %add_const(4) MLOAD_GENERAL %shl_const(32) ADD + DUP2 %add_const(5) MLOAD_GENERAL %shl_const(40) ADD + DUP2 %add_const(6) MLOAD_GENERAL %shl_const(48) ADD + DUP2 %add_const(7) MLOAD_GENERAL %shl_const(56) ADD + %stack (value, addr, retdest) -> (retdest, value) JUMP %macro mload_packing_u64_LE - %stack (addr: 3) -> (addr, %%after) + %stack (addr) -> (addr, %%after) %jump(mload_packing_u64_LE) %%after: %endmacro -// Pre stack: context, segment, offset, value, len, retdest -// Post stack: offset' +// Pre stack: addr, value, len, retdest +// Post stack: addr' global mstore_unpacking: - // stack: context, segment, offset, value, len, retdest - DUP5 ISZERO - // stack: len == 0, context, segment, offset, value, len, retdest + // stack: addr, value, len, retdest + DUP3 ISZERO + // stack: len == 0, addr, value, len, retdest %jumpi(mstore_unpacking_empty) - %stack(context, segment, offset, value, len, retdest) -> (len, context, segment, offset, value, retdest) + %stack(addr, value, len, retdest) -> (len, addr, value, retdest) PUSH 3 - // stack: BYTES_PER_JUMP, len, context, segment, offset, value, retdest + // stack: BYTES_PER_JUMP, len, addr, value, retdest MUL - // stack: jump_offset, context, segment, offset, value, retdest + // stack: jump_offset, addr, value, retdest PUSH mstore_unpacking_0 - // stack: mstore_unpacking_0, jump_offset, context, segment, offset, value, retdest + // stack: mstore_unpacking_0, jump_offset, addr, value, retdest ADD - // stack: address_unpacking, context, segment, offset, value, retdest + // stack: address_unpacking, addr, value, retdest JUMP mstore_unpacking_empty: - %stack(context, segment, offset, value, len, retdest) -> (retdest, offset) + %stack(addr, value, len, retdest) -> (retdest, addr) JUMP // This case can never be reached. It's only here to offset the table correctly. @@ -66,274 +65,274 @@ mstore_unpacking_0: PANIC %endrep mstore_unpacking_1: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_1 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_2: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_2 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_3: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_3 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_4: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_4 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_5: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_5 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_6: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_6 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_7: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_7 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_8: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_8 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_9: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_9 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_10: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_10 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_11: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_11 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_12: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_12 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_13: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_13 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_14: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_14 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_15: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_15 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_16: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_16 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_17: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_17 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_18: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_18 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_19: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_19 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_20: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_20 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_21: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_21 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_22: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_22 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_23: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_23 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_24: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_24 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_25: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_25 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_26: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_26 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_27: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_27 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_28: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_28 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_29: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_29 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_30: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_30 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_31: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_31 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_32: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_32 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP %macro mstore_unpacking - %stack (addr: 3, value, len) -> (addr, value, len, %%after) + %stack (addr, value, len) -> (addr, value, len, %%after) %jump(mstore_unpacking) %%after: %endmacro -// Pre stack: context, segment, offset, value, retdest -// Post stack: offset' +// Pre stack: addr, value, retdest +// Post stack: addr' global mstore_unpacking_u64_LE: - %stack (context, segment, offset, value) -> (0xff, value, context, segment, offset, context, segment, offset, value) + %stack (addr, value) -> (0xff, value, addr, addr, value) AND MSTORE_GENERAL // First byte - DUP3 %add_const(1) - %stack (new_offset, context, segment, offset, value) -> (0xff00, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(1) + %stack (new_addr, addr, value) -> (0xff00, value, new_addr, addr, value) AND %shr_const(8) MSTORE_GENERAL // Second byte - DUP3 %add_const(2) - %stack (new_offset, context, segment, offset, value) -> (0xff0000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(2) + %stack (new_addr, addr, value) -> (0xff0000, value, new_addr, addr, value) AND %shr_const(16) MSTORE_GENERAL // Third byte - DUP3 %add_const(3) - %stack (new_offset, context, segment, offset, value) -> (0xff000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(3) + %stack (new_addr, addr, value) -> (0xff000000, value, new_addr, addr, value) AND %shr_const(24) MSTORE_GENERAL // Fourth byte - DUP3 %add_const(4) - %stack (new_offset, context, segment, offset, value) -> (0xff00000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(4) + %stack (new_addr, addr, value) -> (0xff00000000, value, new_addr, addr, value) AND %shr_const(32) MSTORE_GENERAL // Fifth byte - DUP3 %add_const(5) - %stack (new_offset, context, segment, offset, value) -> (0xff0000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(5) + %stack (new_addr, addr, value) -> (0xff0000000000, value, new_addr, addr, value) AND %shr_const(40) MSTORE_GENERAL // Sixth byte - DUP3 %add_const(6) - %stack (new_offset, context, segment, offset, value) -> (0xff000000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(6) + %stack (new_addr, addr, value) -> (0xff000000000000, value, new_addr, addr, value) AND %shr_const(48) MSTORE_GENERAL // Seventh byte - DUP3 %add_const(7) - %stack (new_offset, context, segment, offset, value) -> (0xff00000000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(7) + %stack (new_addr, addr, value) -> (0xff00000000000000, value, new_addr, addr, value) AND %shr_const(56) MSTORE_GENERAL // Eighth byte - %pop4 JUMP + %pop2 JUMP %macro mstore_unpacking_u64_LE - %stack (addr: 3, value) -> (addr, value, %%after) + %stack (addr, value) -> (addr, value, %%after) %jump(mstore_unpacking_u64_LE) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index 9798f42474..a0af8b07db 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -11,7 +11,8 @@ global sys_mload: %stack(kexit_info, offset) -> (offset, 32, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: addr: 3, len, kexit_info + %build_address + // stack: addr, len, kexit_info MLOAD_32BYTES %stack (value, kexit_info) -> (kexit_info, value) EXIT_KERNEL @@ -29,7 +30,8 @@ global sys_mstore: %stack(kexit_info, offset, value) -> (offset, value, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: addr: 3, value, kexit_info + %build_address + // stack: addr, value, kexit_info MSTORE_32BYTES_32 POP // stack: kexit_info @@ -60,7 +62,8 @@ global sys_calldataload: LT %jumpi(calldataload_large_offset) %stack (kexit_info, i) -> (@SEGMENT_CALLDATA, i, 32, sys_calldataload_after_mload_packing, kexit_info) GET_CONTEXT - // stack: ADDR: 3, 32, sys_calldataload_after_mload_packing, kexit_info + %build_address + // stack: addr, 32, sys_calldataload_after_mload_packing, kexit_info %jump(mload_packing) sys_calldataload_after_mload_packing: // stack: value, kexit_info @@ -113,7 +116,10 @@ wcopy_within_bounds: // stack: segment, src_ctx, kexit_info, dest_offset, offset, size GET_CONTEXT %stack (context, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, size, wcopy_after, kexit_info) + (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, size, wcopy_after, kexit_info) + %build_address + SWAP3 %build_address + // stack: DST, SRC, size, wcopy_after, kexit_info %jump(memcpy_bytes) wcopy_empty: @@ -132,6 +138,7 @@ wcopy_large_offset: GET_CONTEXT %stack (context, kexit_info, dest_offset, offset, size) -> (context, @SEGMENT_MAIN_MEMORY, dest_offset, size, wcopy_after, kexit_info) + %build_address %jump(memset) wcopy_after: @@ -241,6 +248,9 @@ extcodecopy_contd: GET_CONTEXT %stack (context, new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size) + (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size) + %build_address + SWAP3 %build_address + // stack: DST, SRC, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size %jump(memcpy_bytes) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/txn_fields.asm b/evm/src/cpu/kernel/asm/memory/txn_fields.asm index e4e6b87544..a8c1c0788f 100644 --- a/evm/src/cpu/kernel/asm/memory/txn_fields.asm +++ b/evm/src/cpu/kernel/asm/memory/txn_fields.asm @@ -1,18 +1,27 @@ // Load the given normalized transaction field from memory. %macro mload_txn_field(field) + // Transaction fields are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_kernel(@SEGMENT_NORMALIZED_TXN) + // stack: addr + MLOAD_GENERAL // stack: value %endmacro // Store the given normalized transaction field to memory. %macro mstore_txn_field(field) + // Transaction fields are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_kernel(@SEGMENT_NORMALIZED_TXN) + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index df7e1d741d..dc1a9392eb 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -29,15 +29,13 @@ mpt_hash_hash_if_rlp: mpt_hash_hash_rlp: // stack: result, result_len, new_len, retdest %stack (result, result_len, new_len) - // context, segment, offset, value, len, trie_len, retdest - -> (0, @SEGMENT_RLP_RAW, 0, result, result_len, mpt_hash_hash_rlp_after_unpacking, new_len) + -> (@SEGMENT_RLP_RAW, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len) + // stack: addr, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len %jump(mstore_unpacking) mpt_hash_hash_rlp_after_unpacking: - // stack: result_len, new_len, retdest - PUSH 0 // offset - PUSH @SEGMENT_RLP_RAW // segment - PUSH 0 // context - // stack: result_addr: 3, result_len, new_len, retdest + // stack: result_addr, result_len, new_len, retdest + POP PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: result_addr, result_len, new_len, retdest KECCAK_GENERAL // stack: hash, new_len, retdest %stack(hash, new_len, retdest) -> (retdest, hash, new_len) @@ -80,23 +78,19 @@ encode_or_hash_concrete_node: %stack (node_type, node_ptr, encode_value, cur_len) -> (node_type, node_ptr, encode_value, cur_len, maybe_hash_node) %jump(encode_node) maybe_hash_node: - // stack: result_ptr, result_len, cur_len, retdest + // stack: result_addr, result_len, cur_len, retdest DUP2 %lt_const(32) %jumpi(pack_small_rlp) // result_len >= 32, so we hash the result. - // stack: result_ptr, result_len, cur_len, retdest - PUSH @SEGMENT_RLP_RAW // segment - PUSH 0 // context - // stack: result_addr: 3, result_len, cur_len, retdest + // stack: result_addr, result_len, cur_len, retdest KECCAK_GENERAL %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP pack_small_rlp: // stack: result_ptr, result_len, cur_len, retdest %stack (result_ptr, result_len, cur_len) - -> (0, @SEGMENT_RLP_RAW, result_ptr, result_len, - after_packed_small_rlp, result_len, cur_len) + -> (result_ptr, result_len, after_packed_small_rlp, result_len, cur_len) %jump(mload_packing) after_packed_small_rlp: %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) @@ -130,13 +124,13 @@ global encode_node_empty: // An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string. // TODO: Write this byte just once to RLP memory, then we can always return (0, 1). %alloc_rlp_block - // stack: rlp_pos, cur_len, retdest + // stack: rlp_start, cur_len, retdest PUSH 0x80 - // stack: 0x80, rlp_pos, cur_len, retdest + // stack: 0x80, rlp_start, cur_len, retdest DUP2 - // stack: rlp_pos, 0x80, rlp_pos, cur_len, retdest + // stack: rlp_start, 0x80, rlp_start, cur_len, retdest %mstore_rlp - %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, 1, cur_len) + %stack (rlp_start, cur_len, retdest) -> (retdest, rlp_start, 1, cur_len) JUMP global encode_node_branch: @@ -244,7 +238,7 @@ encode_node_branch_prepend_prefix: %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) %%after_unpacking: // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest %endmacro @@ -284,7 +278,7 @@ encode_node_extension_after_hex_prefix: encode_node_extension_unpack: %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len) -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start, cur_len) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) encode_node_extension_after_unpacking: // stack: rlp_pos, rlp_start, cur_len, retdest %prepend_rlp_list_prefix diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 2ffefb7d5a..84fbb853e9 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -57,7 +57,7 @@ global mpt_hash_receipt_trie: %endmacro global encode_account: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // First, we compute the length of the RLP data we're about to write. // We also update the length of the trie data segment. // The nonce and balance fields are variable-length, so we need to load them @@ -69,22 +69,22 @@ global encode_account: SWAP2 %add_const(4) SWAP2 // Now, we start the encoding. - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest DUP2 %mload_trie_data // nonce = value[0] %rlp_scalar_len - // stack: nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data // balance = value[1] %rlp_scalar_len - // stack: balance_rlp_len, nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: balance_rlp_len, nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest PUSH 66 // storage_root and code_hash fields each take 1 + 32 bytes ADD ADD - // stack: payload_len, rlp_pos, value_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, payload_len, value_ptr, cur_len, retdest + // stack: rlp_addr, payload_len, value_ptr, cur_len, retdest DUP2 %rlp_list_len - // stack: list_len, rlp_pos, payload_len, value_ptr, cur_len, retdest + // stack: list_len, rlp_addr, payload_len, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, list_len, payload_len, value_ptr, cur_len, retdest + // stack: rlp_addr, list_len, payload_len, value_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix // stack: rlp_pos_2, payload_len, value_ptr, cur_len, retdest %encode_rlp_list_prefix @@ -115,232 +115,237 @@ global encode_account: JUMP global encode_txn: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // Load the txn_rlp_len which is at the beginning of value_ptr DUP2 %mload_trie_data - // stack: txn_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_rlp_len, rlp_addr, value_ptr, cur_len, retdest // We need to add 1+txn_rlp_len to the length of the trie data. SWAP3 DUP4 %increment ADD - // stack: new_len, rlp_pos, value_ptr, txn_rlp_len, retdest + // stack: new_len, rlp_addr, value_ptr, txn_rlp_len, retdest SWAP3 SWAP2 %increment - // stack: txn_rlp_ptr=value_ptr+1, rlp_pos, txn_rlp_len, new_len, retdest + // stack: txn_rlp_ptr=value_ptr+1, rlp_addr, txn_rlp_len, new_len, retdest - %stack (txn_rlp_ptr, rlp_pos, txn_rlp_len) -> (rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) + %stack (txn_rlp_ptr, rlp_addr, txn_rlp_len) -> (rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) // Encode the txn rlp prefix - // stack: rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest + // stack: rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix // copy txn_rlp to the new block - // stack: rlp_pos, txn_rlp_len, txn_rlp_ptr, new_len, retdest - %stack (rlp_pos, txn_rlp_len, txn_rlp_ptr) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, // dest addr - 0, @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 + // stack: rlp_addr, txn_rlp_len, txn_rlp_ptr, new_len, retdest + %stack (rlp_addr, txn_rlp_len, txn_rlp_ptr) -> ( + @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 + rlp_addr, // dest addr txn_rlp_len, // mcpy len - txn_rlp_len, rlp_pos) + txn_rlp_len, rlp_addr) + %build_kernel_address + SWAP1 + // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_addr, new_len, retdest %memcpy_bytes ADD - // stack new_rlp_pos, new_len, retdest - %stack(new_rlp_pos, new_len, retdest) -> (retdest, new_rlp_pos, new_len) + // stack new_rlp_addr, new_len, retdest + %stack(new_rlp_addr, new_len, retdest) -> (retdest, new_rlp_addr, new_len) JUMP // We assume a receipt in memory is stored as: // [payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]]. // A log is [payload_len, address, num_topics, [topics], data_len, [data]]. global encode_receipt: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // First, we add 261 to the trie data length for all values before the logs besides the type. // These are: the payload length, the status, cum_gas_used, the bloom filter (256 elements), // the length of the logs payload and the length of the logs. SWAP2 %add_const(261) SWAP2 - // There is a double encoding! What we compute is: - // either RLP(RLP(receipt)) for Legacy transactions or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. + // There is a double encoding! + // What we compute is: + // - either RLP(RLP(receipt)) for Legacy transactions + // - or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. // First encode the wrapper prefix. DUP2 %mload_trie_data - // stack: first_value, rlp_pos, value_ptr, cur_len, retdest + // stack: first_value, rlp_addr, value_ptr, cur_len, retdest // The first value is either the transaction type or the payload length. // Since the receipt contains at least the 256-bytes long bloom filter, payload_len > 3. DUP1 %lt_const(3) %jumpi(encode_nonzero_receipt_type) // If we are here, then the first byte is the payload length. %rlp_list_len - // stack: rlp_receipt_len, rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_receipt_len, rlp_addr, value_ptr, cur_len, retdest SWAP1 %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest encode_receipt_after_type: - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Then encode the receipt prefix. // `payload_ptr` is either `value_ptr` or `value_ptr+1`, depending on the transaction type. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode status. DUP2 %increment %mload_trie_data - // stack: status, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: status, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode cum_gas_used. DUP2 %add_const(2) %mload_trie_data - // stack: cum_gas_used, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: cum_gas_used, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode bloom. PUSH 256 // Bloom length. - DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA PUSH 0 // MPT src address. - DUP5 - // stack: rlp_pos, SRC, 256, rlp_pos, payload_len_ptr, cur_len, retdest + DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA %build_kernel_address // MPT src address. + DUP3 + // stack: rlp_addr, SRC, 256, rlp_addr, payload_len_ptr, cur_len, retdest %encode_rlp_string - // stack: rlp_pos, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 POP - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode logs prefix. DUP2 %add_const(259) %mload_trie_data - // stack: logs_payload_len, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: logs_payload_len, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest DUP2 %add_const(261) - // stack: logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP3 %add_const(260) %mload_trie_data - // stack: num_logs, logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_logs, logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest PUSH 0 encode_receipt_logs_loop: - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP2 DUP2 EQ - // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest %jumpi(encode_receipt_end) // We add 4 to the trie data length for the fixed size elements in the current log. SWAP5 %add_const(4) SWAP5 - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP3 DUP5 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode log prefix. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode address. DUP2 %increment %mload_trie_data - // stack: address, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: address, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_160 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP2 %add_const(2) %mload_trie_data - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode topics prefix. DUP1 %mul_const(33) - // stack: topics_payload_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: topics_payload_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP3 %encode_rlp_list_prefix - // stack: new_rlp_pos, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: new_rlp_pos, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP2 POP - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Add `num_topics` to the length of the trie data segment. DUP1 SWAP9 - // stack: cur_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest + // stack: cur_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest ADD SWAP8 - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP2 %add_const(3) - // stack: topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest PUSH 0 encode_receipt_topics_loop: - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 DUP2 EQ - // stack: j == num_topics, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j == num_topics, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %jumpi(encode_receipt_topics_end) - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP2 DUP2 ADD %mload_trie_data - // stack: current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 - // stack: rlp_pos, current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: rlp_addr, current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %encode_rlp_256 - // stack: new_rlp_pos, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: new_rlp_pos, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP3 POP // stack: j, topics_ptr, new_rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %increment %jump(encode_receipt_topics_loop) encode_receipt_topics_end: - // stack: num_topics, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest ADD - // stack: data_len_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: data_len_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: rlp_pos, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: rlp_addr, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: num_topics, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest POP - // stack: i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest // Encode data prefix. DUP3 %mload_trie_data - // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest // Add `data_len` to the length of the trie data. DUP1 SWAP7 ADD SWAP6 - // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest DUP4 %increment DUP2 ADD - // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest SWAP4 %increment - // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest - PUSH @SEGMENT_TRIE_DATA PUSH 0 - // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest - DUP8 - // stack: rlp_pos, SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest + PUSH @SEGMENT_TRIE_DATA %build_kernel_address + // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest + DUP6 + // stack: rlp_addr, SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest %encode_rlp_string - // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest SWAP4 POP // stack: i, num_logs, next_log_ptr, new_rlp_pos, payload_len_ptr, cur_len'', retdest %increment %jump(encode_receipt_logs_loop) encode_receipt_end: - // stack: num_logs, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: num_logs, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest %pop3 - // stack: rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: rlp_addr, payload_len_ptr, cur_len'', retdest SWAP1 POP - // stack: rlp_pos, cur_len'', retdest - %stack(rlp_pos, new_len, retdest) -> (retdest, rlp_pos, new_len) + // stack: rlp_addr, cur_len'', retdest + %stack(rlp_addr, new_len, retdest) -> (retdest, rlp_addr, new_len) JUMP encode_nonzero_receipt_type: - // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest // We have a nonlegacy receipt, so the type is also stored in the trie data segment. SWAP3 %increment SWAP3 - // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data - // stack: payload_len, txn_type, rlp_pos, value_ptr, retdest + // stack: payload_len, txn_type, rlp_addr, value_ptr, retdest // The transaction type is encoded in 1 byte %increment %rlp_list_len - // stack: rlp_receipt_len, txn_type, rlp_pos, value_ptr, retdest + // stack: rlp_receipt_len, txn_type, rlp_addr, value_ptr, retdest DUP3 %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest + // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest DUP2 DUP2 %mstore_rlp %increment - // stack: rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest - %stack (rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest) -> (rlp_pos, value_ptr, retdest) + // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest + %stack (rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest) -> (rlp_addr, value_ptr, retdest) // We replace `value_ptr` with `paylaod_len_ptr` so we can encode the rest of the data more easily SWAP1 %increment SWAP1 - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_addr, payload_len_ptr, retdest %jump(encode_receipt_after_type) global encode_storage_value: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest SWAP1 %mload_trie_data SWAP1 // A storage value is a scalar, so we only need to add 1 to the trie data length. SWAP2 %increment SWAP2 - // stack: rlp_pos, value, cur_len, retdest + // stack: rlp_addr, value, cur_len, retdest // The YP says storage trie is a map "... to the RLP-encoded 256-bit integer values" // which seems to imply that this should be %encode_rlp_256. But %encode_rlp_scalar // causes the tests to pass, so it seems storage values should be treated as variable- // length after all. %doubly_encode_rlp_scalar - // stack: rlp_pos', cur_len, retdest - %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, cur_len) + // stack: rlp_addr', cur_len, retdest + %stack (rlp_addr, cur_len, retdest) -> (retdest, rlp_addr, cur_len) JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index 5bb9fa9d88..7dd02c34f9 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -3,8 +3,8 @@ // given position, and returns the updated position, i.e. a pointer to the next // unused offset. // -// Pre stack: rlp_start_pos, num_nibbles, packed_nibbles, terminated, retdest -// Post stack: rlp_end_pos +// Pre stack: rlp_start_addr, num_nibbles, packed_nibbles, terminated, retdest +// Post stack: rlp_end_addr global hex_prefix_rlp: DUP2 %assert_lt_const(65) @@ -12,7 +12,7 @@ global hex_prefix_rlp: // Compute the length of the hex-prefix string, in bytes: // hp_len = num_nibbles / 2 + 1 = i + 1 %increment - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // Write the RLP header. DUP1 %gt_const(55) %jumpi(rlp_header_large) @@ -21,113 +21,112 @@ global hex_prefix_rlp: // The hex-prefix is a single byte. It must be <= 127, since its first // nibble only has two bits. So this is the "small" RLP string case, where // the byte is its own RLP encoding. - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest POP first_byte: - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // get the first nibble, if num_nibbles is odd, or zero otherwise SWAP2 - // stack: packed_nibbles, num_nibbbles, rlp_pos, terminated, retdest + // stack: packed_nibbles, num_nibbbles, rlp_addr, terminated, retdest DUP2 DUP1 %mod_const(2) - // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_pos, terminated, retdest + // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_addr, terminated, retdest SWAP1 SUB %mul_const(4) SHR - // stack: first_nibble_or_zero, num_nibbles, rlp_pos, terminated, retdest + // stack: first_nibble_or_zero, num_nibbles, rlp_addr, terminated, retdest SWAP2 - // stack: rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest + // stack: rlp_addr, num_nibbles, first_nibble_or_zero, terminated, retdest SWAP3 - // stack: terminated, num_nibbles, first_nibble_or_zero, rlp_pos, retdest + // stack: terminated, num_nibbles, first_nibble_or_zero, rlp_addr, retdest %mul_const(2) - // stack: terminated * 2, num_nibbles, first_nibble_or_zero, rlp_pos, retdest + // stack: terminated * 2, num_nibbles, first_nibble_or_zero, rlp_addr, retdest SWAP1 - // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_pos, retdest + // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_addr, retdest %mod_const(2) // parity ADD - // stack: parity + terminated * 2, first_nibble_or_zero, rlp_pos, retdest + // stack: parity + terminated * 2, first_nibble_or_zero, rlp_addr, retdest %mul_const(16) ADD - // stack: first_byte, rlp_pos, retdest + // stack: first_byte, rlp_addr, retdest DUP2 %mstore_rlp %increment - // stack: rlp_pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP remaining_bytes: - // stack: rlp_pos, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, retdest SWAP2 PUSH @U256_MAX - // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_pos, ret_dest + // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_addr, ret_dest SWAP1 SWAP2 DUP1 %mod_const(2) - // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP1 SUB DUP1 - // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest %div_const(2) - // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP2 SWAP1 - // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest %mul_const(4) - // stack: 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest PUSH 256 SUB - // stack: 256 - 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: 256 - 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest SHR - // stack: mask, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: mask, rem_bytes, packed_nibbles, rlp_addr, ret_dest SWAP1 SWAP2 AND - %stack - (remaining_nibbles, rem_bytes, rlp_pos) -> - (rlp_pos, remaining_nibbles, rem_bytes) - %mstore_unpacking_rlp + %stack(remaining_nibbles, rem_bytes, rlp_addr) -> (rlp_addr, remaining_nibbles, rem_bytes) + %mstore_unpacking SWAP1 JUMP rlp_header_medium: - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest %add_const(0x80) // value = 0x80 + hp_len - DUP2 // offset = rlp_pos + DUP2 %mstore_rlp - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - // rlp_pos += 1 + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + // rlp_addr += 1 %increment - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest SWAP4 SWAP5 SWAP6 - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest %jump(first_byte) rlp_header_large: - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // In practice hex-prefix length will never exceed 256, so the length of the // length will always be 1 byte in this case. PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8 - DUP3 // offset = rlp_pos + DUP3 %mstore_rlp + // stack: rlp_addr, value, hp_len, i, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - DUP2 %increment // offset = rlp_pos + 1 + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + DUP2 %increment %mstore_rlp - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - // rlp_pos += 2 + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + // rlp_addr += 2 %add_const(2) - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest SWAP4 SWAP5 SWAP6 - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest %jump(first_byte) diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm index 1bf9f6f8fb..d21e917b28 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm @@ -71,18 +71,18 @@ mpt_insert_receipt_trie_save: global scalar_to_rlp: // stack: scalar, retdest %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - // stack: pos, scalar, retdest + // stack: init_addr, scalar, retdest SWAP1 DUP2 %encode_rlp_scalar - // stack: pos', init_pos, retdest + // stack: addr', init_addr, retdest // Now our rlp_encoding is in RlpRaw. // Set new RlpRaw data size DUP1 %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) DUP2 DUP2 SUB // len of the key - // stack: len, pos', init_pos, retdest - DUP3 PUSH @SEGMENT_RLP_RAW PUSH 0 // address where we get the key from + // stack: len, addr', init_addr, retdest + DUP3 %mload_packing - // stack: packed_key, pos', init_pos, retdest + // stack: packed_key, addr', init_addr, retdest SWAP2 %pop2 // stack: key, retdest SWAP1 diff --git a/evm/src/cpu/kernel/asm/rlp/decode.asm b/evm/src/cpu/kernel/asm/rlp/decode.asm index dd990bbd37..43c6627d6c 100644 --- a/evm/src/cpu/kernel/asm/rlp/decode.asm +++ b/evm/src/cpu/kernel/asm/rlp/decode.asm @@ -7,143 +7,141 @@ // assets. // Parse the length of a bytestring from RLP memory. The next len bytes after -// pos' will contain the string. +// rlp_addr' will contain the string. // -// Pre stack: pos, retdest -// Post stack: pos', len +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', len global decode_rlp_string_len: - // stack: pos, retdest + // stack: rlp_addr, retdest DUP1 - %mload_kernel(@SEGMENT_RLP_RAW) - // stack: first_byte, pos, retdest + MLOAD_GENERAL + // stack: first_byte, rlp_addr, retdest DUP1 %gt_const(0xb7) - // stack: first_byte >= 0xb8, first_byte, pos, retdest + // stack: first_byte >= 0xb8, first_byte, rlp_addr, retdest %jumpi(decode_rlp_string_len_large) - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest DUP1 %gt_const(0x7f) - // stack: first_byte >= 0x80, first_byte, pos, retdest + // stack: first_byte >= 0x80, first_byte, rlp_addr, retdest %jumpi(decode_rlp_string_len_medium) // String is a single byte in the range [0x00, 0x7f]. - %stack (first_byte, pos, retdest) -> (retdest, pos, 1) + %stack (first_byte, rlp_addr, retdest) -> (retdest, rlp_addr, 1) JUMP decode_rlp_string_len_medium: // String is 0-55 bytes long. First byte contains the len. - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest %sub_const(0x80) - // stack: len, pos, retdest + // stack: len, rlp_addr, retdest SWAP1 %increment - // stack: pos', len, retdest - %stack (pos, len, retdest) -> (retdest, pos, len) + // stack: rlp_addr', len, retdest + %stack (rlp_addr, len, retdest) -> (retdest, rlp_addr, len) JUMP decode_rlp_string_len_large: // String is >55 bytes long. First byte contains the len of the len. - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest %sub_const(0xb7) - // stack: len_of_len, pos, retdest + // stack: len_of_len, rlp_addr, retdest SWAP1 %increment - // stack: pos', len_of_len, retdest + // stack: rlp_addr', len_of_len, retdest %jump(decode_int_given_len) // Convenience macro to call decode_rlp_string_len and return where we left off. %macro decode_rlp_string_len - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_string_len) %%after: %endmacro // Parse a scalar from RLP memory. -// Pre stack: pos, retdest -// Post stack: pos', scalar +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', scalar // // Scalars are variable-length, but this method assumes a max length of 32 // bytes, so that the result can be returned as a single word on the stack. // As per the spec, scalars must not have leading zeros. global decode_rlp_scalar: - // stack: pos, retdest + // stack: rlp_addr, retdest PUSH decode_int_given_len - // stack: decode_int_given_len, pos, retdest + // stack: decode_int_given_len, rlp_addr, retdest SWAP1 - // stack: pos, decode_int_given_len, retdest + // stack: rlp_addr, decode_int_given_len, retdest // decode_rlp_string_len will return to decode_int_given_len, at which point - // the stack will contain (pos', len, retdest), which are the proper args + // the stack will contain (rlp_addr', len, retdest), which are the proper args // to decode_int_given_len. %jump(decode_rlp_string_len) // Convenience macro to call decode_rlp_scalar and return where we left off. %macro decode_rlp_scalar - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_scalar) %%after: %endmacro // Parse the length of an RLP list from memory. -// Pre stack: pos, retdest -// Post stack: pos', len +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', len global decode_rlp_list_len: - // stack: pos, retdest + // stack: rlp_addr, retdest DUP1 - %mload_kernel(@SEGMENT_RLP_RAW) - // stack: first_byte, pos, retdest + MLOAD_GENERAL + // stack: first_byte, rlp_addr, retdest SWAP1 - %increment // increment pos + %increment // increment rlp_addr SWAP1 - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest // If first_byte is >= 0xf8, it's a > 55 byte list, and // first_byte - 0xf7 is the length of the length. DUP1 %gt_const(0xf7) // GT is native while GE is not, so compare to 0xf6 instead - // stack: first_byte >= 0xf7, first_byte, pos', retdest + // stack: first_byte >= 0xf7, first_byte, rlp_addr', retdest %jumpi(decode_rlp_list_len_big) // This is the "small list" case. // The list length is first_byte - 0xc0. - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest %sub_const(0xc0) - // stack: len, pos', retdest - %stack (len, pos, retdest) -> (retdest, pos, len) + // stack: len, rlp_addr', retdest + %stack (len, rlp_addr, retdest) -> (retdest, rlp_addr, len) JUMP decode_rlp_list_len_big: // The length of the length is first_byte - 0xf7. - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest %sub_const(0xf7) - // stack: len_of_len, pos', retdest + // stack: len_of_len, rlp_addr', retdest SWAP1 - // stack: pos', len_of_len, retdest + // stack: rlp_addr', len_of_len, retdest %jump(decode_int_given_len) // Convenience macro to call decode_rlp_list_len and return where we left off. %macro decode_rlp_list_len - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_list_len) %%after: %endmacro // Parse an integer of the given length. It is assumed that the integer will // fit in a single (256-bit) word on the stack. -// Pre stack: pos, len, retdest -// Post stack: pos', int +// Pre stack: rlp_addr, len, retdest +// Post stack: rlp_addr', int global decode_int_given_len: DUP2 ISZERO %jumpi(empty_int) - %stack (pos, len, retdest) -> (pos, len, pos, len, retdest) + %stack (rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr, len, retdest) ADD - %stack(pos_two, pos, len, retdest) -> (pos, len, pos_two, retdest) - PUSH @SEGMENT_RLP_RAW - PUSH 0 //context + %stack(rlp_addr_two, rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr_two, retdest) MLOAD_32BYTES - // stack: int, pos', retdest - %stack(int, pos, retdest) -> (retdest, pos, int) + // stack: int, rlp_addr', retdest + %stack(int, rlp_addr, retdest) -> (retdest, rlp_addr, int) JUMP empty_int: - // stack: pos, len, retdest - %stack(pos, len, retdest) -> (retdest, pos, 0) + // stack: rlp_addr, len, retdest + %stack(rlp_addr, len, retdest) -> (retdest, rlp_addr, 0) JUMP diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 23b0db4635..2319e780b9 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -1,76 +1,76 @@ // RLP-encode a fixed-length 160 bit (20 byte) string. Assumes string < 2^160. -// Pre stack: pos, string, retdest -// Post stack: pos +// Pre stack: rlp_addr, string, retdest +// Post stack: rlp_addr global encode_rlp_160: PUSH 20 %jump(encode_rlp_fixed) // Convenience macro to call encode_rlp_160 and return where we left off. %macro encode_rlp_160 - %stack (pos, string) -> (pos, string, %%after) + %stack (rlp_addr, string) -> (rlp_addr, string, %%after) %jump(encode_rlp_160) %%after: %endmacro // RLP-encode a fixed-length 256 bit (32 byte) string. -// Pre stack: pos, string, retdest -// Post stack: pos +// Pre stack: rlp_addr, string, retdest +// Post stack: rlp_addr global encode_rlp_256: PUSH 32 %jump(encode_rlp_fixed) // Convenience macro to call encode_rlp_256 and return where we left off. %macro encode_rlp_256 - %stack (pos, string) -> (pos, string, %%after) + %stack (rlp_addr, string) -> (rlp_addr, string, %%after) %jump(encode_rlp_256) %%after: %endmacro // RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). global encode_rlp_fixed: - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x80) - // stack: first_byte, len, pos, string, retdest + // stack: first_byte, len, rlp_addr, string, retdest DUP3 - // stack: pos, first_byte, len, pos, string, retdest + // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest SWAP1 - %increment // increment pos - // stack: pos, len, string, retdest - %stack (pos, len, string) -> (pos, string, len, encode_rlp_fixed_finish) - // stack: pos, string, len, encode_rlp_fixed_finish, retdest - %jump(mstore_unpacking_rlp) + %increment // increment rlp_addr + // stack: rlp_addr, len, string, retdest + %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) + // stack: rlp_addr, string, len, encode_rlp_fixed_finish, retdest + %jump(mstore_unpacking) encode_rlp_fixed_finish: - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP // Doubly-RLP-encode a fixed-length string with the given byte length. // I.e. writes encode(encode(string). Assumes string < 2^(8 * len). global doubly_encode_rlp_fixed: - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x81) - // stack: first_byte, len, pos, string, retdest + // stack: first_byte, len, rlp_addr, string, retdest DUP3 - // stack: pos, first_byte, len, pos, string, retdest + // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x80) - // stack: second_byte, len, original_pos, string, retdest + // stack: second_byte, len, original_rlp_addr, string, retdest DUP3 %increment - // stack: pos', second_byte, len, pos, string, retdest + // stack: rlp_addr', second_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest SWAP1 %add_const(2) // advance past the two prefix bytes - // stack: pos'', len, string, retdest - %stack (pos, len, string) -> (pos, string, len, encode_rlp_fixed_finish) - // stack: context, segment, pos'', string, len, encode_rlp_fixed_finish, retdest - %jump(mstore_unpacking_rlp) + // stack: rlp_addr'', len, string, retdest + %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) + // stack: context, segment, rlp_addr'', string, len, encode_rlp_fixed_finish, retdest + %jump(mstore_unpacking) // Writes the RLP prefix for a string of the given length. This does not handle // the trivial encoding of certain single-byte strings, as handling that would @@ -78,156 +78,156 @@ global doubly_encode_rlp_fixed: // length. This method should generally be used only when we know a string // contains at least two bytes. // -// Pre stack: pos, str_len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, str_len, retdest +// Post stack: rlp_addr' global encode_rlp_multi_byte_string_prefix: - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest DUP2 %gt_const(55) - // stack: str_len > 55, pos, str_len, retdest + // stack: str_len > 55, rlp_addr, str_len, retdest %jumpi(encode_rlp_multi_byte_string_prefix_large) // Medium case; prefix is 0x80 + str_len. - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest SWAP1 %add_const(0x80) - // stack: prefix, pos, retdest + // stack: prefix, rlp_addr, retdest DUP2 - // stack: pos, prefix, pos, retdest + // stack: rlp_addr, prefix, rlp_addr, retdest %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP encode_rlp_multi_byte_string_prefix_large: // Large case; prefix is 0xb7 + len_of_len, followed by str_len. - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest DUP2 %num_bytes - // stack: len_of_len, pos, str_len, retdest + // stack: len_of_len, rlp_addr, str_len, retdest SWAP1 DUP2 // len_of_len %add_const(0xb7) - // stack: first_byte, pos, len_of_len, str_len, retdest + // stack: first_byte, rlp_addr, len_of_len, str_len, retdest DUP2 - // stack: pos, first_byte, pos, len_of_len, str_len, retdest + // stack: rlp_addr, first_byte, rlp_addr, len_of_len, str_len, retdest %mstore_rlp - // stack: pos, len_of_len, str_len, retdest + // stack: rlp_addr, len_of_len, str_len, retdest %increment - // stack: pos', len_of_len, str_len, retdest - %stack (pos, len_of_len, str_len) -> (pos, str_len, len_of_len) - %jump(mstore_unpacking_rlp) + // stack: rlp_addr', len_of_len, str_len, retdest + %stack (rlp_addr, len_of_len, str_len) -> (rlp_addr, str_len, len_of_len) + %jump(mstore_unpacking) %macro encode_rlp_multi_byte_string_prefix - %stack (pos, str_len) -> (pos, str_len, %%after) + %stack (rlp_addr, str_len) -> (rlp_addr, str_len, %%after) %jump(encode_rlp_multi_byte_string_prefix) %%after: %endmacro // Writes the RLP prefix for a list with the given payload length. // -// Pre stack: pos, payload_len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, payload_len, retdest +// Post stack: rlp_addr' global encode_rlp_list_prefix: - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest DUP2 %gt_const(55) %jumpi(encode_rlp_list_prefix_large) // Small case: prefix is just 0xc0 + length. - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest SWAP1 %add_const(0xc0) - // stack: prefix, pos, retdest + // stack: prefix, rlp_addr, retdest DUP2 - // stack: pos, prefix, pos, retdest + // stack: rlp_addr, prefix, rlp_addr, retdest %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, retdest %increment SWAP1 JUMP encode_rlp_list_prefix_large: // Write 0xf7 + len_of_len. - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest DUP2 %num_bytes - // stack: len_of_len, pos, payload_len, retdest + // stack: len_of_len, rlp_addr, payload_len, retdest DUP1 %add_const(0xf7) - // stack: first_byte, len_of_len, pos, payload_len, retdest - DUP3 // pos + // stack: first_byte, len_of_len, rlp_addr, payload_len, retdest + DUP3 // rlp_addr %mstore_rlp - // stack: len_of_len, pos, payload_len, retdest + // stack: len_of_len, rlp_addr, payload_len, retdest SWAP1 %increment - // stack: pos', len_of_len, payload_len, retdest - %stack (pos, len_of_len, payload_len) - -> (pos, payload_len, len_of_len, + // stack: rlp_addr', len_of_len, payload_len, retdest + %stack (rlp_addr, len_of_len, payload_len) + -> (rlp_addr, payload_len, len_of_len, encode_rlp_list_prefix_large_done_writing_len) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) encode_rlp_list_prefix_large_done_writing_len: - // stack: pos'', retdest + // stack: rlp_addr'', retdest SWAP1 JUMP %macro encode_rlp_list_prefix - %stack (pos, payload_len) -> (pos, payload_len, %%after) + %stack (rlp_addr, payload_len) -> (rlp_addr, payload_len, %%after) %jump(encode_rlp_list_prefix) %%after: %endmacro -// Given an RLP list payload which starts and ends at the given positions, -// prepend the appropriate RLP list prefix. Returns the updated start position, +// Given an RLP list payload which starts and ends at the given rlp_address, +// prepend the appropriate RLP list prefix. Returns the updated start rlp_address, // as well as the length of the RLP data (including the newly-added prefix). // -// Pre stack: end_pos, start_pos, retdest -// Post stack: prefix_start_pos, rlp_len +// Pre stack: end_rlp_addr, start_rlp_addr, retdest +// Post stack: prefix_start_rlp_addr, rlp_len global prepend_rlp_list_prefix: - // stack: end_pos, start_pos, retdest - DUP2 DUP2 SUB // end_pos - start_pos - // stack: payload_len, end_pos, start_pos, retdest + // stack: end_rlp_addr, start_rlp_addr, retdest + DUP2 DUP2 SUB // end_rlp_addr - start_rlp_addr + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %gt_const(55) %jumpi(prepend_rlp_list_prefix_big) - // If we got here, we have a small list, so we prepend 0xc0 + len at position 8. - // stack: payload_len, end_pos, start_pos, retdest + // If we got here, we have a small list, so we prepend 0xc0 + len at rlp_address 8. + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %add_const(0xc0) - // stack: prefix_byte, payload_len, end_pos, start_pos, retdest + // stack: prefix_byte, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP4 %decrement // offset of prefix %mstore_rlp - // stack: payload_len, end_pos, start_pos, retdest + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest %increment - // stack: rlp_len, end_pos, start_pos, retdest + // stack: rlp_len, end_rlp_addr, start_rlp_addr, retdest SWAP2 %decrement - // stack: prefix_start_pos, end_pos, rlp_len, retdest - %stack (prefix_start_pos, end_pos, rlp_len, retdest) -> (retdest, prefix_start_pos, rlp_len) + // stack: prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest + %stack (prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) JUMP prepend_rlp_list_prefix_big: - // We have a large list, so we prepend 0xf7 + len_of_len at position - // prefix_start_pos = start_pos - 1 - len_of_len + // We have a large list, so we prepend 0xf7 + len_of_len at rlp_address + // prefix_start_rlp_addr = start_rlp_addr - 1 - len_of_len // followed by the length itself. - // stack: payload_len, end_pos, start_pos, retdest + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %num_bytes - // stack: len_of_len, payload_len, end_pos, start_pos, retdest + // stack: len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 - DUP5 %decrement // start_pos - 1 + DUP5 %decrement // start_rlp_addr - 1 SUB - // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest - DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_pos] = 0xf7 + len_of_len - // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest - DUP1 %increment // start_len_pos = prefix_start_pos + 1 - %stack (start_len_pos, prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest) - -> (start_len_pos, payload_len, len_of_len, + // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest + DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len + // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest + DUP1 %increment // start_len_rlp_addr = prefix_start_rlp_addr + 1 + %stack (start_len_rlp_addr, prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest) + -> (start_len_rlp_addr, payload_len, len_of_len, prepend_rlp_list_prefix_big_done_writing_len, - prefix_start_pos, end_pos, retdest) - %jump(mstore_unpacking_rlp) + prefix_start_rlp_addr, end_rlp_addr, retdest) + %jump(mstore_unpacking) prepend_rlp_list_prefix_big_done_writing_len: - // stack: start_pos, prefix_start_pos, end_pos, retdest - %stack (start_pos, prefix_start_pos, end_pos) - -> (end_pos, prefix_start_pos, prefix_start_pos) - // stack: end_pos, prefix_start_pos, prefix_start_pos, retdest + // stack: start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr, retdest + %stack (start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr) + -> (end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr) + // stack: end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr, retdest SUB - // stack: rlp_len, prefix_start_pos, retdest - %stack (rlp_len, prefix_start_pos, retdest) -> (retdest, prefix_start_pos, rlp_len) + // stack: rlp_len, prefix_start_rlp_addr, retdest + %stack (rlp_len, prefix_start_rlp_addr, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) JUMP // Convenience macro to call prepend_rlp_list_prefix and return where we left off. %macro prepend_rlp_list_prefix - %stack (end_pos, start_pos) -> (end_pos, start_pos, %%after) + %stack (end_rlp_addr, start_rlp_addr) -> (end_rlp_addr, start_rlp_addr, %%after) %jump(prepend_rlp_list_prefix) %%after: %endmacro @@ -274,18 +274,3 @@ prepend_rlp_list_prefix_big_done_writing_len: ADD %%finish: %endmacro - -// Like mstore_unpacking, but specifically for the RLP segment. -// Pre stack: offset, value, len, retdest -// Post stack: offset' -global mstore_unpacking_rlp: - // stack: offset, value, len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - %jump(mstore_unpacking) - -%macro mstore_unpacking_rlp - %stack (offset, value, len) -> (offset, value, len, %%after) - %jump(mstore_unpacking_rlp) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm index cd4a837e31..8196a452ab 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm @@ -1,8 +1,8 @@ // RLP-encode a scalar, i.e. a variable-length integer. -// Pre stack: pos, scalar, retdest -// Post stack: pos +// Pre stack: rlp_addr, scalar, retdest +// Post stack: rlp_addr global encode_rlp_scalar: - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest // If scalar > 0x7f, this is the "medium" case. DUP2 %gt_const(0x7f) @@ -12,12 +12,12 @@ global encode_rlp_scalar: DUP2 %jumpi(encode_rlp_scalar_small) // scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80. - // stack: pos, scalar, retdest - %stack (pos, scalar) -> (pos, 0x80, pos) - %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (0x80, rlp_addr, rlp_addr) + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP @@ -26,17 +26,17 @@ encode_rlp_scalar_medium: // (big-endian) scalar bytes. We first compute the minimal number of bytes // needed to represent this scalar, then treat it as if it was a fixed- // length string with that length. - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest DUP2 %num_bytes - // stack: scalar_bytes, pos, scalar, retdest + // stack: scalar_bytes, rlp_addr, scalar, retdest %jump(encode_rlp_fixed) // Doubly-RLP-encode a scalar, i.e. return encode(encode(scalar)). -// Pre stack: pos, scalar, retdest -// Post stack: pos +// Pre stack: rlp_addr, scalar, retdest +// Post stack: rlp_addr global doubly_encode_rlp_scalar: - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest // If scalar > 0x7f, this is the "medium" case. DUP2 %gt_const(0x7f) @@ -46,15 +46,16 @@ global doubly_encode_rlp_scalar: DUP2 %jumpi(encode_rlp_scalar_small) // scalar = 0, so BE(scalar) is the empty string, encode(scalar) = 0x80, and encode(encode(scalar)) = 0x8180. - // stack: pos, scalar, retdest - %stack (pos, scalar) -> (pos, 0x81, pos, 0x80, pos) - %mstore_rlp - // stack: pos, 0x80, pos, retdest + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (0x81, rlp_addr, rlp_addr) + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - %mstore_rlp - // stack: pos, retdest - %add_const(2) - // stack: pos, retdest + DUP1 PUSH 0x80 + MSTORE_GENERAL + // stack: rlp_addr, retdest + %increment + // stack: rlp_addr, retdest SWAP1 JUMP @@ -65,35 +66,35 @@ doubly_encode_rlp_scalar_medium: // encode(encode(scalar)) = [0x80 + len + 1] || [0x80 + len] || BE(scalar) // We first compute the length of the scalar with %num_bytes, then treat the scalar as if it was a // fixed-length string with that length. - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest DUP2 %num_bytes - // stack: scalar_bytes, pos, scalar, retdest + // stack: scalar_bytes, rlp_addr, scalar, retdest %jump(doubly_encode_rlp_fixed) // The "small" case of RLP-encoding a scalar, where the value is its own encoding. // This can be used for both for singly encoding or doubly encoding, since encode(encode(x)) = encode(x) = x. encode_rlp_scalar_small: - // stack: pos, scalar, retdest - %stack (pos, scalar) -> (pos, scalar, pos) - // stack: pos, scalar, pos, retdest - %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (scalar, rlp_addr, rlp_addr) + // stack: scalar, rlp_addr, rlp_addr, retdest + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP // Convenience macro to call encode_rlp_scalar and return where we left off. %macro encode_rlp_scalar - %stack (pos, scalar) -> (pos, scalar, %%after) + %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) %jump(encode_rlp_scalar) %%after: %endmacro // Convenience macro to call doubly_encode_rlp_scalar and return where we left off. %macro doubly_encode_rlp_scalar - %stack (pos, scalar) -> (pos, scalar, %%after) + %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) %jump(doubly_encode_rlp_scalar) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm index 1c8bec9673..60174a9436 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm @@ -1,80 +1,79 @@ // Encodes an arbitrary string, given a pointer and length. -// Pre stack: pos, ADDR: 3, len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, ADDR, len, retdest +// Post stack: rlp_addr' global encode_rlp_string: - // stack: pos, ADDR: 3, len, retdest - DUP5 %eq_const(1) - // stack: len == 1, pos, ADDR: 3, len, retdest - DUP5 DUP5 DUP5 // ADDR: 3 + // stack: rlp_addr, ADDR, len, retdest + DUP3 %eq_const(1) + // stack: len == 1, rlp_addr, ADDR, len, retdest + DUP3 MLOAD_GENERAL - // stack: first_byte, len == 1, pos, ADDR: 3, len, retdest + // stack: first_byte, len == 1, rlp_addr, ADDR, len, retdest %lt_const(128) MUL // cheaper than AND - // stack: single_small_byte, pos, ADDR: 3, len, retdest + // stack: single_small_byte, rlp_addr, ADDR, len, retdest %jumpi(encode_rlp_string_small_single_byte) - // stack: pos, ADDR: 3, len, retdest - DUP5 %gt_const(55) - // stack: len > 55, pos, ADDR: 3, len, retdest + // stack: rlp_addr, ADDR, len, retdest + DUP3 %gt_const(55) + // stack: len > 55, rlp_addr, ADDR, len, retdest %jumpi(encode_rlp_string_large) global encode_rlp_string_small: - // stack: pos, ADDR: 3, len, retdest - DUP5 // len + // stack: rlp_addr, ADDR, len, retdest + DUP1 + DUP4 // len %add_const(0x80) - // stack: first_byte, pos, ADDR: 3, len, retdest - DUP2 - // stack: pos, first_byte, pos, ADDR: 3, len, retdest - %mstore_rlp - // stack: pos, ADDR: 3, len, retdest + // stack: first_byte, rlp_addr, rlp_addr, ADDR, len, retdest + MSTORE_GENERAL + // stack: rlp_addr, ADDR, len, retdest %increment - // stack: pos', ADDR: 3, len, retdest - DUP5 DUP2 ADD // pos'' = pos' + len - // stack: pos'', pos', ADDR: 3, len, retdest - %stack (pos2, pos1, ADDR: 3, len, retdest) - -> (0, @SEGMENT_RLP_RAW, pos1, ADDR, len, retdest, pos2) + // stack: rlp_addr', ADDR, len, retdest + DUP3 DUP2 ADD // rlp_addr'' = rlp_addr' + len + // stack: rlp_addr'', rlp_addr', ADDR, len, retdest + %stack (rlp_addr2, rlp_addr1, ADDR, len, retdest) + -> (rlp_addr1, ADDR, len, retdest, rlp_addr2) %jump(memcpy_bytes) global encode_rlp_string_small_single_byte: - // stack: pos, ADDR: 3, len, retdest - %stack (pos, ADDR: 3, len) -> (ADDR, pos) + // stack: rlp_addr, ADDR, len, retdest + %stack (rlp_addr, ADDR, len) -> (ADDR, rlp_addr) MLOAD_GENERAL - // stack: byte, pos, retdest - DUP2 - %mstore_rlp - // stack: pos, retdest + // stack: byte, rlp_addr, retdest + DUP2 SWAP1 + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment SWAP1 - // stack: retdest, pos' + // stack: retdest, rlp_addr' JUMP global encode_rlp_string_large: - // stack: pos, ADDR: 3, len, retdest - DUP5 %num_bytes - // stack: len_of_len, pos, ADDR: 3, len, retdest + // stack: rlp_addr, ADDR, len, retdest + DUP3 %num_bytes + // stack: len_of_len, rlp_addr, ADDR, len, retdest SWAP1 - DUP2 // len_of_len + DUP1 + // stack: rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest + DUP3 // len_of_len %add_const(0xb7) - // stack: first_byte, pos, len_of_len, ADDR: 3, len, retdest - DUP2 - // stack: pos, first_byte, pos, len_of_len, ADDR: 3, len, retdest - %mstore_rlp - // stack: pos, len_of_len, ADDR: 3, len, retdest + // stack: first_byte, rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest + MSTORE_GENERAL + // stack: rlp_addr, len_of_len, ADDR, len, retdest %increment - // stack: pos', len_of_len, ADDR: 3, len, retdest - %stack (pos, len_of_len, ADDR: 3, len) - -> (pos, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len) - %jump(mstore_unpacking_rlp) + // stack: rlp_addr', len_of_len, ADDR, len, retdest + %stack (rlp_addr, len_of_len, ADDR, len) + -> (rlp_addr, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len) + %jump(mstore_unpacking) global encode_rlp_string_large_after_writing_len: - // stack: pos'', ADDR: 3, len, retdest - DUP5 DUP2 ADD // pos''' = pos'' + len - // stack: pos''', pos'', ADDR: 3, len, retdest - %stack (pos3, pos2, ADDR: 3, len, retdest) - -> (0, @SEGMENT_RLP_RAW, pos2, ADDR, len, retdest, pos3) + // stack: rlp_addr'', ADDR, len, retdest + DUP3 DUP2 ADD // rlp_addr''' = rlp_addr'' + len + // stack: rlp_addr''', rlp_addr'', ADDR, len, retdest + %stack (rlp_addr3, rlp_addr2, ADDR, len, retdest) + -> (rlp_addr2, ADDR, len, retdest, rlp_addr3) %jump(memcpy_bytes) %macro encode_rlp_string - %stack (pos, ADDR: 3, len) -> (pos, ADDR, len, %%after) + %stack (rlp_addr, ADDR, len) -> (rlp_addr, ADDR, len, %%after) %jump(encode_rlp_string) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm index 85a7817522..7593537194 100644 --- a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm +++ b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm @@ -8,29 +8,34 @@ global read_rlp_to_memory: // stack: retdest PROVER_INPUT(rlp) // Read the RLP blob length from the prover tape. // stack: len, retdest - PUSH 0 // initial position - // stack: pos, len, retdest + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: addr, final_addr, retdest read_rlp_to_memory_loop: - // stack: pos, len, retdest + // stack: addr, final_addr, retdest DUP2 DUP2 EQ - // stack: pos == len, pos, len, retdest + // stack: addr == final_addr, addr, final_addr, retdest %jumpi(read_rlp_to_memory_finish) - // stack: pos, len, retdest + // stack: addr, len, retdest + DUP1 PROVER_INPUT(rlp) - // stack: byte, pos, len, retdest - DUP2 - // stack: pos, byte, pos, len, retdest - %mstore_kernel(@SEGMENT_RLP_RAW) - // stack: pos, len, retdest + // stack: byte, addr, addr, final_addr, retdest + MSTORE_GENERAL + // stack: addr, final_addr, retdest %increment - // stack: pos', len, retdest + // stack: addr', final_addr, retdest %jump(read_rlp_to_memory_loop) read_rlp_to_memory_finish: - // stack: pos, len, retdest - POP - // stack: len, retdest - SWAP1 JUMP + // stack: addr, final_addr, retdest + // we recover the offset here + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + DUP2 SUB + // stack: pos, addr, final_addr, retdest + %stack(pos, addr, final_addr, retdest) -> (retdest, pos) + JUMP diff --git a/evm/src/cpu/kernel/asm/shift.asm b/evm/src/cpu/kernel/asm/shift.asm index 9040f1955b..ee9ccbfaea 100644 --- a/evm/src/cpu/kernel/asm/shift.asm +++ b/evm/src/cpu/kernel/asm/shift.asm @@ -2,21 +2,17 @@ /// /// Specifically, set SHIFT_TABLE_SEGMENT[i] = 2^i for i = 0..255. %macro shift_table_init - push 0 // initial offset is zero - push @SEGMENT_SHIFT_TABLE // segment - dup2 // kernel context is 0 + push @SEGMENT_SHIFT_TABLE // segment, ctx == virt == 0 push 1 // 2^0 %rep 255 - // stack: 2^i, context, segment, ost_i - dup4 + // stack: 2^i, addr_i + dup2 %increment - dup4 - dup4 - // stack: context, segment, ost_(i+1), 2^i, context, segment, ost_i - dup4 + // stack: addr_(i+1), 2^i, addr_i + dup2 dup1 add - // stack: 2^(i+1), context, segment, ost_(i+1), 2^i, context, segment, ost_i + // stack: 2^(i+1), addr_(i+1), 2^i, addr_i %endrep %rep 256 mstore_general diff --git a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm index d4df7a6e57..4a8feccaa3 100644 --- a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm +++ b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm @@ -6,207 +6,206 @@ // Decode the chain ID and store it. %macro decode_and_store_chain_id - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, chain_id) -> (chain_id, pos) + %stack (rlp_addr, chain_id) -> (chain_id, rlp_addr) %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: pos + // stack: rlp_addr %endmacro // Decode the nonce and store it. %macro decode_and_store_nonce - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, nonce) -> (nonce, pos) + %stack (rlp_addr, nonce) -> (nonce, rlp_addr) %mstore_txn_field(@TXN_FIELD_NONCE) - // stack: pos + // stack: rlp_addr %endmacro // Decode the gas price and, since this is for legacy txns, store it as both // TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS and TXN_FIELD_MAX_FEE_PER_GAS. %macro decode_and_store_gas_price_legacy - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the max priority fee and store it. %macro decode_and_store_max_priority_fee - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the max fee and store it. %macro decode_and_store_max_fee - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the gas limit and store it. %macro decode_and_store_gas_limit - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_limit) -> (gas_limit, pos) + %stack (rlp_addr, gas_limit) -> (gas_limit, rlp_addr) %mstore_txn_field(@TXN_FIELD_GAS_LIMIT) - // stack: pos + // stack: rlp_addr %endmacro // Decode the "to" field and store it. // This field is either 160-bit or empty in the case of a contract creation txn. %macro decode_and_store_to - // stack: pos + // stack: rlp_addr %decode_rlp_string_len - // stack: pos, len + // stack: rlp_addr, len SWAP1 - // stack: len, pos + // stack: len, rlp_addr DUP1 ISZERO %jumpi(%%contract_creation) - // stack: len, pos + // stack: len, rlp_addr DUP1 %eq_const(20) ISZERO %jumpi(invalid_txn) // Address is 160-bit - %stack (len, pos) -> (pos, len, %%with_scalar) + %stack (len, rlp_addr) -> (rlp_addr, len, %%with_scalar) %jump(decode_int_given_len) %%with_scalar: - // stack: pos, int + // stack: rlp_addr, int SWAP1 %mstore_txn_field(@TXN_FIELD_TO) - // stack: pos + // stack: rlp_addr %jump(%%end) %%contract_creation: - // stack: len, pos + // stack: len, rlp_addr POP PUSH 1 %mstore_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) - // stack: pos + // stack: rlp_addr %%end: %endmacro // Decode the "value" field and store it. %macro decode_and_store_value - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, value) -> (value, pos) + %stack (rlp_addr, value) -> (value, rlp_addr) %mstore_txn_field(@TXN_FIELD_VALUE) - // stack: pos + // stack: rlp_addr %endmacro // Decode the calldata field, store its length in @TXN_FIELD_DATA_LEN, and copy it to @SEGMENT_TXN_DATA. %macro decode_and_store_data - // stack: pos - // Decode the data length, store it, and compute new_pos after any data. + // stack: rlp_addr + // Decode the data length, store it, and compute new_rlp_addr after any data. %decode_rlp_string_len - %stack (pos, data_len) -> (data_len, pos, data_len, pos, data_len) + %stack (rlp_addr, data_len) -> (data_len, rlp_addr, data_len, rlp_addr, data_len) %mstore_txn_field(@TXN_FIELD_DATA_LEN) - // stack: pos, data_len, pos, data_len + // stack: rlp_addr, data_len, rlp_addr, data_len ADD - // stack: new_pos, old_pos, data_len + // stack: new_rlp_addr, old_rlp_addr, data_len // Memcpy the txn data from @SEGMENT_RLP_RAW to @SEGMENT_TXN_DATA. - %stack (new_pos, old_pos, data_len) -> (old_pos, data_len, %%after, new_pos) - PUSH @SEGMENT_RLP_RAW - GET_CONTEXT - PUSH 0 + %stack (new_rlp_addr, old_rlp_addr, data_len) -> (old_rlp_addr, data_len, %%after, new_rlp_addr) + // old_rlp_addr has context 0. We will call GET_CONTEXT and update it. + GET_CONTEXT ADD PUSH @SEGMENT_TXN_DATA - GET_CONTEXT - // stack: DST, SRC, data_len, %%after, new_pos + GET_CONTEXT ADD + // stack: DST, SRC, data_len, %%after, new_rlp_addr %jump(memcpy_bytes) %%after: - // stack: new_pos + // stack: new_rlp_addr %endmacro %macro decode_and_store_access_list - // stack: pos + // stack: rlp_addr DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %decode_rlp_list_len - %stack (pos, len) -> (len, len, pos, %%after) + %stack (rlp_addr, len) -> (len, len, rlp_addr, %%after) %jumpi(decode_and_store_access_list) - // stack: len, pos, %%after + // stack: len, rlp_addr, %%after POP SWAP1 POP - // stack: pos + // stack: rlp_addr %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) %%after: %endmacro %macro decode_and_store_y_parity - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, y_parity) -> (y_parity, pos) + %stack (rlp_addr, y_parity) -> (y_parity, rlp_addr) %mstore_txn_field(@TXN_FIELD_Y_PARITY) - // stack: pos + // stack: rlp_addr %endmacro %macro decode_and_store_r - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, r) -> (r, pos) + %stack (rlp_addr, r) -> (r, rlp_addr) %mstore_txn_field(@TXN_FIELD_R) - // stack: pos + // stack: rlp_addr %endmacro %macro decode_and_store_s - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, s) -> (s, pos) + %stack (rlp_addr, s) -> (s, rlp_addr) %mstore_txn_field(@TXN_FIELD_S) - // stack: pos + // stack: rlp_addr %endmacro // The access list is of the form `[[{20 bytes}, [{32 bytes}...]]...]`. global decode_and_store_access_list: - // stack: len, pos + // stack: len, rlp_addr DUP2 ADD - // stack: end_pos, pos + // stack: end_rlp_addr, rlp_addr // Store the RLP length. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) SWAP1 decode_and_store_access_list_loop: - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr DUP2 DUP2 EQ %jumpi(decode_and_store_access_list_finish) - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr %decode_rlp_list_len // Should be a list `[{20 bytes}, [{32 bytes}...]]` - // stack: pos, internal_len, end_pos + // stack: rlp_addr, internal_len, end_rlp_addr SWAP1 POP // We don't need the length of this list. - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr %decode_rlp_scalar // Address // TODO: Should panic when address is not 20 bytes? - // stack: pos, addr, end_pos + // stack: rlp_addr, addr, end_rlp_addr SWAP1 - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr DUP1 %insert_accessed_addresses_no_return - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr %add_address_cost - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr SWAP1 - // stack: pos, addr, end_pos + // stack: rlp_addr, addr, end_rlp_addr %decode_rlp_list_len // Should be a list of storage keys `[{32 bytes}...]` - // stack: pos, sk_len, addr, end_pos + // stack: rlp_addr, sk_len, addr, end_rlp_addr SWAP1 DUP2 ADD - // stack: sk_end_pos, pos, addr, end_pos + // stack: sk_end_rlp_addr, rlp_addr, addr, end_rlp_addr SWAP1 - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr sk_loop: DUP2 DUP2 EQ %jumpi(end_sk) - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr %decode_rlp_scalar // Storage key // TODO: Should panic when key is not 32 bytes? - %stack (pos, key, sk_end_pos, addr, end_pos) -> - (addr, key, sk_loop_contd, pos, sk_end_pos, addr, end_pos) + %stack (rlp_addr, key, sk_end_rlp_addr, addr, end_rlp_addr) -> + (addr, key, sk_loop_contd, rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) %jump(insert_accessed_storage_keys_with_original_value) sk_loop_contd: - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr %add_storage_key_cost %jump(sk_loop) end_sk: - %stack (pos, sk_end_pos, addr, end_pos) -> (pos, end_pos) + %stack (rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) -> (rlp_addr, end_rlp_addr) %jump(decode_and_store_access_list_loop) decode_and_store_access_list_finish: - %stack (pos, end_pos, retdest) -> (retdest, pos) + %stack (rlp_addr, end_rlp_addr, retdest) -> (retdest, rlp_addr) JUMP %macro add_address_cost diff --git a/evm/src/cpu/kernel/asm/transactions/router.asm b/evm/src/cpu/kernel/asm/transactions/router.asm index 109334dd0d..2ecccfe02b 100644 --- a/evm/src/cpu/kernel/asm/transactions/router.asm +++ b/evm/src/cpu/kernel/asm/transactions/router.asm @@ -20,15 +20,15 @@ read_txn_from_memory: // Type 0 (legacy) transactions have no such prefix, but their RLP will have a // first byte >= 0xc0, so there is no overlap. - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL %eq_const(1) // stack: first_byte == 1, retdest %jumpi(process_type_1_txn) // stack: retdest - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL %eq_const(2) // stack: first_byte == 2, retdest %jumpi(process_type_2_txn) @@ -53,10 +53,12 @@ global update_txn_trie: // and now copy txn_rlp to the new block %stack (rlp_start, txn_rlp_len, value_ptr, txn_counter, num_nibbles) -> ( - 0, @SEGMENT_TRIE_DATA, rlp_start, // dest addr - 0, @SEGMENT_RLP_RAW, 0, // src addr. Kernel has context 0 + @SEGMENT_RLP_RAW, // src addr. ctx == virt == 0 + rlp_start, @SEGMENT_TRIE_DATA, // swapped dest addr, ctx == 0 txn_rlp_len, // mcpy len txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr) + SWAP2 %build_kernel_address + // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr %memcpy_bytes ADD %set_trie_data_size diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index edd01e512d..0cd22c10f2 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -13,68 +13,68 @@ global process_type_0_txn: // stack: retdest - PUSH 0 // initial pos - // stack: pos, retdest + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_and_store_nonce %decode_and_store_gas_price_legacy %decode_and_store_gas_limit %decode_and_store_to %decode_and_store_value %decode_and_store_data - // stack: pos, retdest + // stack: rlp_addr, retdest // Parse the "v" field. - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_rlp_scalar - // stack: pos, v, retdest + // stack: rlp_addr, v, retdest SWAP1 - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest DUP1 %gt_const(28) - // stack: v > 28, v, pos, retdest + // stack: v > 28, v, rlp_addr, retdest %jumpi(process_v_new_style) // We have an old style v, so y_parity = v - 27. // No chain ID is present, so we can leave TXN_FIELD_CHAIN_ID_PRESENT and // TXN_FIELD_CHAIN_ID with their default values of zero. - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest %sub_const(27) - %stack (y_parity, pos) -> (y_parity, pos) + %stack (y_parity, rlp_addr) -> (y_parity, rlp_addr) %mstore_txn_field(@TXN_FIELD_Y_PARITY) - // stack: pos, retdest + // stack: rlp_addr, retdest %jump(decode_r_and_s) process_v_new_style: - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest // We have a new style v, so chain_id_present = 1, // chain_id = (v - 35) / 2, and y_parity = (v - 35) % 2. - %stack (v, pos) -> (1, v, pos) + %stack (v, rlp_addr) -> (1, v, rlp_addr) %mstore_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest %sub_const(35) DUP1 - // stack: v - 35, v - 35, pos, retdest + // stack: v - 35, v - 35, rlp_addr, retdest %div_const(2) - // stack: chain_id, v - 35, pos, retdest + // stack: chain_id, v - 35, rlp_addr, retdest %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: v - 35, pos, retdest + // stack: v - 35, rlp_addr, retdest %mod_const(2) - // stack: y_parity, pos, retdest + // stack: y_parity, rlp_addr, retdest %mstore_txn_field(@TXN_FIELD_Y_PARITY) decode_r_and_s: - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -85,73 +85,68 @@ type_0_compute_signed_data: // keccak256(rlp([nonce, gas_price, gas_limit, to, value, data])) %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) - // stack: nonce, rlp_start, retdest + // stack: nonce, rlp_addr_start, retdest DUP2 - // stack: rlp_pos, nonce, rlp_start, retdest + // stack: rlp_addr, nonce, rlp_addr_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) ISZERO %jumpi(finish_rlp_list) - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest finish_rlp_list: %prepend_rlp_list_prefix - // stack: prefix_start_pos, rlp_len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, rlp_len, retdest + // stack: ADDR, rlp_len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/transactions/type_1.asm b/evm/src/cpu/kernel/asm/transactions/type_1.asm index c9298b6617..f9142bd4a4 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_1.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_1.asm @@ -8,11 +8,14 @@ global process_type_1_txn: // stack: retdest - PUSH 1 // initial pos, skipping over the 0x01 byte - // stack: pos, retdest + // Initial rlp address offset of 1 (skipping over the 0x01 byte) + PUSH 1 + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) %store_chain_id_present_true %decode_and_store_chain_id @@ -27,7 +30,7 @@ global process_type_1_txn: %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -36,83 +39,79 @@ global process_type_1_txn: // over keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])). type_1_compute_signed_data: %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: chain_id, rlp_start, retdest + // stack: chain_id, rlp_addr_start, retdest DUP2 - // stack: rlp_pos, chain_id, rlp_start, retdest + // stack: rlp_addr, chain_id, rlp_addr_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt - PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 + // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_pos, rlp_start, retdest) -> + %stack (al_len, al_start, rlp_addr, rlp_addr_start, retdest) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, - 0, @SEGMENT_RLP_RAW, al_start, + rlp_addr, + al_start, al_len, after_serializing_access_list, - rlp_pos, rlp_start, retdest) + rlp_addr, rlp_addr_start, retdest) %jump(memcpy_bytes) after_serializing_access_list: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %prepend_rlp_list_prefix - // stack: prefix_start_pos, rlp_len, retdest + // stack: prefix_start_rlp_addr, rlp_len, retdest // Store a `1` in front of the RLP %decrement - %stack (pos) -> (1, 0, @SEGMENT_RLP_RAW, pos, pos) + %stack (rlp_addr) -> (1, rlp_addr, rlp_addr) MSTORE_GENERAL - // stack: pos, rlp_len, retdest + // stack: rlp_addr, rlp_len, retdest // Hash the RLP + the leading `1` SWAP1 %increment SWAP1 - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, len, retdest + // stack: ADDR, len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/transactions/type_2.asm b/evm/src/cpu/kernel/asm/transactions/type_2.asm index b7f6e6c7d5..cd4f85e677 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_2.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_2.asm @@ -9,13 +9,16 @@ global process_type_2_txn: // stack: retdest - PUSH 1 // initial pos, skipping over the 0x02 byte - // stack: pos, retdest + // Initial rlp address offset of 1 (skipping over the 0x02 byte) + PUSH 1 + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) - // stack: pos, retdest + // stack: rlp_addr, retdest %store_chain_id_present_true %decode_and_store_chain_id %decode_and_store_nonce @@ -30,7 +33,7 @@ global process_type_2_txn: %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -39,87 +42,83 @@ global process_type_2_txn: // keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list])) type_2_compute_signed_data: %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) // stack: chain_id, rlp_start, retdest DUP2 - // stack: rlp_pos, chain_id, rlp_start, retdest + // stack: rlp_addr, chain_id, rlp_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt - PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 + // stack: ADDR, len, rlp_addr, rlp_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_pos, rlp_start, retdest) -> + %stack (al_len, al_start, rlp_addr, rlp_start, retdest) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, - 0, @SEGMENT_RLP_RAW, al_start, + rlp_addr, + al_start, al_len, after_serializing_access_list, - rlp_pos, rlp_start, retdest) + rlp_addr, rlp_start, retdest) %jump(memcpy_bytes) after_serializing_access_list: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %prepend_rlp_list_prefix // stack: prefix_start_pos, rlp_len, retdest // Store a `2` in front of the RLP %decrement - %stack (pos) -> (2, 0, @SEGMENT_RLP_RAW, pos, pos) + %stack (rlp_addr) -> (2, rlp_addr, rlp_addr) MSTORE_GENERAL - // stack: pos, rlp_len, retdest + // stack: rlp_addr, rlp_len, retdest // Hash the RLP + the leading `2` SWAP1 %increment SWAP1 - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, len, retdest + // stack: ADDR, len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index fc2472b3b8..c331acb423 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -410,3 +410,36 @@ ISZERO // stack: not b %endmacro + +%macro build_address + // stack: ctx, seg, off + ADD + ADD + // stack: addr +%endmacro + +%macro build_address_no_offset + // stack: ctx, seg + ADD + // stack: addr +%endmacro + +%macro build_kernel_address + // stack: seg, off + ADD + // stack: addr (ctx == 0) +%endmacro + +%macro build_address_with_ctx_no_offset(seg) + // stack: ctx + PUSH $seg + ADD + // stack: addr +%endmacro + +%macro build_address_with_ctx_no_segment(off) + // stack: ctx + PUSH $off + ADD + // stack: addr +%endmacro diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm index 1a1f437287..8385ee59af 100644 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ b/evm/src/cpu/kernel/asm/util/keccak.asm @@ -18,7 +18,8 @@ global sys_keccak256: %stack (kexit_info, offset, len) -> (offset, len, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: ADDR: 3, len, kexit_info + %build_address + // stack: ADDR, len, kexit_info KECCAK_GENERAL // stack: hash, kexit_info SWAP1 @@ -37,11 +38,12 @@ sys_keccak256_empty: %macro keccak256_word(num_bytes) // Since KECCAK_GENERAL takes its input from memory, we will first write // input_word's bytes to @SEGMENT_KERNEL_GENERAL[0..$num_bytes]. - %stack (word) -> (0, @SEGMENT_KERNEL_GENERAL, 0, word, $num_bytes, %%after_mstore) + %stack (word) -> (@SEGMENT_KERNEL_GENERAL, word, $num_bytes, %%after_mstore) %jump(mstore_unpacking) %%after_mstore: - // stack: offset - %stack (offset) -> (0, @SEGMENT_KERNEL_GENERAL, 0, $num_bytes) // context, segment, offset, len + // stack: addr + %stack(addr) -> (addr, $num_bytes, $num_bytes) + SUB KECCAK_GENERAL %endmacro @@ -53,12 +55,13 @@ sys_keccak256_empty: // Since KECCAK_GENERAL takes its input from memory, we will first write // a's bytes to @SEGMENT_KERNEL_GENERAL[0..32], then b's bytes to // @SEGMENT_KERNEL_GENERAL[32..64]. - %stack (a) -> (0, @SEGMENT_KERNEL_GENERAL, 0, a, 32, %%after_mstore_a) + %stack (a) -> (@SEGMENT_KERNEL_GENERAL, a, 32, %%after_mstore_a) %jump(mstore_unpacking) %%after_mstore_a: - %stack (offset, b) -> (0, @SEGMENT_KERNEL_GENERAL, 32, b, 32, %%after_mstore_b) + %stack (addr, b) -> (addr, b, 32, %%after_mstore_b) %jump(mstore_unpacking) %%after_mstore_b: - %stack (offset) -> (0, @SEGMENT_KERNEL_GENERAL, 0, 64) // context, segment, offset, len + %stack (addr) -> (addr, 64, 64) // reset the address offset + SUB KECCAK_GENERAL %endmacro diff --git a/evm/src/cpu/kernel/constants/context_metadata.rs b/evm/src/cpu/kernel/constants/context_metadata.rs index 664ce6e957..ffcc65387a 100644 --- a/evm/src/cpu/kernel/constants/context_metadata.rs +++ b/evm/src/cpu/kernel/constants/context_metadata.rs @@ -1,39 +1,51 @@ +use crate::memory::segments::Segment; + /// These metadata fields contain VM state specific to a particular context. +/// +/// Each value is directly scaled by the corresponding `Segment::ContextMetadata` value for faster +/// memory access in the kernel. +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum ContextMetadata { /// The ID of the context which created this one. - ParentContext = 0, + ParentContext = Segment::ContextMetadata as usize, /// The program counter to return to when we return to the parent context. - ParentProgramCounter = 1, - CalldataSize = 2, - ReturndataSize = 3, + ParentProgramCounter, + CalldataSize, + ReturndataSize, /// The address of the account associated with this context. - Address = 4, + Address, /// The size of the code under the account associated with this context. /// While this information could be obtained from the state trie, it is best to cache it since /// the `CODESIZE` instruction is very cheap. - CodeSize = 5, + CodeSize, /// The address of the caller who spawned this context. - Caller = 6, + Caller, /// The value (in wei) deposited by the caller. - CallValue = 7, + CallValue, /// Whether this context was created by `STATICCALL`, in which case state changes are /// prohibited. - Static = 8, + Static, /// Pointer to the initial version of the state trie, at the creation of this context. Used when /// we need to revert a context. - StateTrieCheckpointPointer = 9, + StateTrieCheckpointPointer, /// Size of the active main memory, in (32 byte) words. - MemWords = 10, - StackSize = 11, + MemWords, + StackSize, /// The gas limit for this call (not the entire transaction). - GasLimit = 12, - ContextCheckpointsLen = 13, + GasLimit, + ContextCheckpointsLen, } impl ContextMetadata { pub(crate) const COUNT: usize = 14; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::ContextMetadata as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::ParentContext, diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 8b85c0b552..4669674efb 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -1,98 +1,110 @@ +use crate::memory::segments::Segment; + /// These metadata fields contain global VM state, stored in the `Segment::Metadata` segment of the /// kernel's context (which is zero). +/// +/// Each value is directly scaled by the corresponding `Segment::GlobalMetadata` value for faster +/// memory access in the kernel. +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum GlobalMetadata { /// The largest context ID that has been used so far in this execution. Tracking this allows us /// give each new context a unique ID, so that its memory will be zero-initialized. - LargestContext = 0, + LargestContext = Segment::GlobalMetadata as usize, /// The size of active memory, in bytes. - MemorySize = 1, - /// The size of the `TrieData` segment, in bytes. In other words, the next address available for - /// appending additional trie data. - TrieDataSize = 2, + MemorySize, /// The size of the `TrieData` segment, in bytes. In other words, the next address available for /// appending additional trie data. - RlpDataSize = 3, + TrieDataSize, + /// The size of the `TrieData` segment, in bytes, represented as a whole address. + /// In other words, the next address available for appending additional trie data. + RlpDataSize, /// A pointer to the root of the state trie within the `TrieData` buffer. - StateTrieRoot = 4, + StateTrieRoot, /// A pointer to the root of the transaction trie within the `TrieData` buffer. - TransactionTrieRoot = 5, + TransactionTrieRoot, /// A pointer to the root of the receipt trie within the `TrieData` buffer. - ReceiptTrieRoot = 6, + ReceiptTrieRoot, // The root digests of each Merkle trie before these transactions. - StateTrieRootDigestBefore = 7, - TransactionTrieRootDigestBefore = 8, - ReceiptTrieRootDigestBefore = 9, + StateTrieRootDigestBefore, + TransactionTrieRootDigestBefore, + ReceiptTrieRootDigestBefore, // The root digests of each Merkle trie after these transactions. - StateTrieRootDigestAfter = 10, - TransactionTrieRootDigestAfter = 11, - ReceiptTrieRootDigestAfter = 12, + StateTrieRootDigestAfter, + TransactionTrieRootDigestAfter, + ReceiptTrieRootDigestAfter, /// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the /// next available offset in these buffers. - TrieEncodedChildSize = 13, + TrieEncodedChildSize, // Block metadata. - BlockBeneficiary = 14, - BlockTimestamp = 15, - BlockNumber = 16, - BlockDifficulty = 17, - BlockRandom = 18, - BlockGasLimit = 19, - BlockChainId = 20, - BlockBaseFee = 21, - BlockGasUsed = 22, + BlockBeneficiary, + BlockTimestamp, + BlockNumber, + BlockDifficulty, + BlockRandom, + BlockGasLimit, + BlockChainId, + BlockBaseFee, + BlockGasUsed, /// Before current transactions block values. - BlockGasUsedBefore = 23, + BlockGasUsedBefore, /// After current transactions block values. - BlockGasUsedAfter = 24, + BlockGasUsedAfter, /// Current block header hash - BlockCurrentHash = 25, + BlockCurrentHash, /// Gas to refund at the end of the transaction. - RefundCounter = 26, + RefundCounter, /// Length of the addresses access list. - AccessedAddressesLen = 27, + AccessedAddressesLen, /// Length of the storage keys access list. - AccessedStorageKeysLen = 28, + AccessedStorageKeysLen, /// Length of the self-destruct list. - SelfDestructListLen = 29, + SelfDestructListLen, /// Length of the bloom entry buffer. - BloomEntryLen = 30, + BloomEntryLen, /// Length of the journal. - JournalLen = 31, + JournalLen, /// Length of the `JournalData` segment. - JournalDataLen = 32, + JournalDataLen, /// Current checkpoint. - CurrentCheckpoint = 33, - TouchedAddressesLen = 34, + CurrentCheckpoint, + TouchedAddressesLen, // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost = 35, + AccessListDataCost, // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart = 36, + AccessListRlpStart, // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen = 37, + AccessListRlpLen, // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation = 38, - IsPrecompileFromEoa = 39, - CallStackDepth = 40, + ContractCreation, + IsPrecompileFromEoa, + CallStackDepth, /// Transaction logs list length - LogsLen = 41, - LogsDataLen = 42, - LogsPayloadLen = 43, - TxnNumberBefore = 44, - TxnNumberAfter = 45, + LogsLen, + LogsDataLen, + LogsPayloadLen, + TxnNumberBefore, + TxnNumberAfter, - KernelHash = 46, - KernelLen = 47, + KernelHash, + KernelLen, } impl GlobalMetadata { pub(crate) const COUNT: usize = 48; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::GlobalMetadata as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::LargestContext, diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 6e2a0015d3..451d7bebf4 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -58,16 +58,19 @@ pub(crate) fn evm_constants() -> HashMap { c.insert(CALL_STACK_LIMIT.0.into(), U256::from(CALL_STACK_LIMIT.1)); for segment in Segment::all() { - c.insert(segment.var_name().into(), (segment as u32).into()); + c.insert(segment.var_name().into(), (segment as usize).into()); } for txn_field in NormalizedTxnField::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for txn_field in GlobalMetadata::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for txn_field in ContextMetadata::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for trie_type in PartialTrieType::all() { c.insert(trie_type.var_name().into(), (trie_type as u32).into()); diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index d62159a225..0b74409b37 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -1,33 +1,46 @@ +use crate::memory::segments::Segment; + /// These are normalized transaction fields, i.e. not specific to any transaction type. +/// +/// Each value is directly scaled by the corresponding `Segment::TxnFields` value for faster +/// memory access in the kernel. +#[allow(dead_code)] +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum NormalizedTxnField { /// Whether a chain ID was present in the txn data. Type 0 transaction with v=27 or v=28 have /// no chain ID. This affects what fields get signed. - ChainIdPresent = 0, - ChainId = 1, - Nonce = 2, - MaxPriorityFeePerGas = 3, - MaxFeePerGas = 4, - GasLimit = 6, - IntrinsicGas = 7, - To = 8, - Value = 9, + ChainIdPresent = Segment::TxnFields as usize, + ChainId, + Nonce, + MaxPriorityFeePerGas, + MaxFeePerGas, + GasLimit, + IntrinsicGas, + To, + Value, /// The length of the data field. The data itself is stored in another segment. - DataLen = 10, - YParity = 11, - R = 12, - S = 13, - Origin = 14, + DataLen, + YParity, + R, + S, + Origin, /// The actual computed gas price for this transaction in the block. /// This is not technically a transaction field, as it depends on the block's base fee. - ComputedFeePerGas = 15, - ComputedPriorityFeePerGas = 16, + ComputedFeePerGas, + ComputedPriorityFeePerGas, } impl NormalizedTxnField { pub(crate) const COUNT: usize = 16; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::TxnFields as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::ChainIdPresent, diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c437672113..4dc887dec1 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -19,12 +19,12 @@ use crate::extension_tower::BN_BASE; use crate::generation::prover_input::ProverInputFn; use crate::generation::state::GenerationState; use crate::generation::GenerationInputs; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::util::u256_to_usize; use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; -use crate::witness::operation::Operation; +use crate::witness::operation::{Operation, CONTEXT_SCALING_FACTOR}; use crate::witness::state::RegistersState; use crate::witness::transition::decode; use crate::witness::util::stack_peek; @@ -199,19 +199,20 @@ impl<'a> Interpreter<'a> { match op { InterpreterMemOpKind::Push(context) => { self.generation_state.memory.contexts[context].segments - [Segment::Stack as usize] - .content - .pop(); + [Segment::Stack.unscale()] + .content + .pop(); } InterpreterMemOpKind::Pop(value, context) => { self.generation_state.memory.contexts[context].segments - [Segment::Stack as usize] - .content - .push(value) + [Segment::Stack.unscale()] + .content + .push(value) } InterpreterMemOpKind::Write(value, context, segment, offset) => { - self.generation_state.memory.contexts[context].segments[segment].content - [offset] = value + self.generation_state.memory.contexts[context].segments + [segment >> SEGMENT_SCALING_FACTOR] // we need to unscale the segment value + .content[offset] = value } } } @@ -267,8 +268,8 @@ impl<'a> Interpreter<'a> { offset_name, self.stack(), self.generation_state.memory.contexts[0].segments - [Segment::KernelGeneral as usize] - .content, + [Segment::KernelGeneral.unscale()] + .content, ); } self.rollback(checkpoint); @@ -289,7 +290,7 @@ impl<'a> Interpreter<'a> { fn code(&self) -> &MemorySegmentState { // The context is 0 if we are in kernel mode. &self.generation_state.memory.contexts[(1 - self.is_kernel() as usize) * self.context()] - .segments[Segment::Code as usize] + .segments[Segment::Code.unscale()] } fn code_slice(&self, n: usize) -> Vec { @@ -301,52 +302,76 @@ impl<'a> Interpreter<'a> { } pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { - self.generation_state.memory.contexts[0].segments[Segment::TxnFields as usize] - .get(field as usize) + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] + .get(field.unscale()) } pub(crate) fn set_txn_field(&mut self, field: NormalizedTxnField, value: U256) { - self.generation_state.memory.contexts[0].segments[Segment::TxnFields as usize] - .set(field as usize, value); + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] + .set(field.unscale(), value); } pub(crate) fn get_txn_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TxnData as usize].content + &self.generation_state.memory.contexts[0].segments[Segment::TxnData.unscale()].content + } + + pub(crate) fn get_context_metadata_field(&self, ctx: usize, field: ContextMetadata) -> U256 { + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] + .get(field.unscale()) + } + + pub(crate) fn set_context_metadata_field( + &mut self, + ctx: usize, + field: ContextMetadata, + value: U256, + ) { + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] + .set(field.unscale(), value) } pub(crate) fn get_global_metadata_field(&self, field: GlobalMetadata) -> U256 { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .get(field as usize) + // These fields are already scaled by their respective segment. + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .get(field) } pub(crate) fn set_global_metadata_field(&mut self, field: GlobalMetadata, value: U256) { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .set(field as usize, value) + // These fields are already scaled by their respective segment. + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .set(field, value) } pub(crate) fn set_global_metadata_multi_fields(&mut self, metadata: &[(GlobalMetadata, U256)]) { for &(field, value) in metadata { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .set(field as usize, value); + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .set(field, value); } } pub(crate) fn get_trie_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TrieData as usize].content + &self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content } pub(crate) fn get_trie_data_mut(&mut self) -> &mut Vec { - &mut self.generation_state.memory.contexts[0].segments[Segment::TrieData as usize].content + &mut self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content } pub(crate) fn get_memory_segment(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment as usize] + self.generation_state.memory.contexts[0].segments[segment.unscale()] .content .clone() } pub(crate) fn get_memory_segment_bytes(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment as usize] + self.generation_state.memory.contexts[0].segments[segment.unscale()] .content .iter() .map(|x| x.low_u32() as u8) @@ -355,9 +380,9 @@ impl<'a> Interpreter<'a> { pub(crate) fn get_current_general_memory(&self) -> Vec { self.generation_state.memory.contexts[self.context()].segments - [Segment::KernelGeneral as usize] - .content - .clone() + [Segment::KernelGeneral.unscale()] + .content + .clone() } pub(crate) fn get_kernel_general_memory(&self) -> Vec { @@ -370,16 +395,16 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_current_general_memory(&mut self, memory: Vec) { let context = self.context(); - self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral as usize] + self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral.unscale()] .content = memory; } pub(crate) fn set_memory_segment(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment as usize].content = memory; + self.generation_state.memory.contexts[0].segments[segment.unscale()].content = memory; } pub(crate) fn set_memory_segment_bytes(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment as usize].content = + self.generation_state.memory.contexts[0].segments[segment.unscale()].content = memory.into_iter().map(U256::from).collect(); } @@ -395,7 +420,7 @@ impl<'a> Interpreter<'a> { .contexts .push(MemoryContextState::default()); } - self.generation_state.memory.contexts[context].segments[Segment::Code as usize].content = + self.generation_state.memory.contexts[context].segments[Segment::Code.unscale()].content = code.into_iter().map(U256::from).collect(); } @@ -406,7 +431,7 @@ impl<'a> Interpreter<'a> { } pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { - self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] .content .iter() .map(|x| x.bit(0)) @@ -421,9 +446,9 @@ impl<'a> Interpreter<'a> { match self.stack_len().cmp(&1) { Ordering::Greater => { let mut stack = self.generation_state.memory.contexts[self.context()].segments - [Segment::Stack as usize] - .content - .clone(); + [Segment::Stack.unscale()] + .content + .clone(); stack.truncate(self.stack_len() - 1); stack.push( self.stack_top() @@ -443,7 +468,7 @@ impl<'a> Interpreter<'a> { } fn stack_segment_mut(&mut self) -> &mut Vec { let context = self.context(); - &mut self.generation_state.memory.contexts[context].segments[Segment::Stack as usize] + &mut self.generation_state.memory.contexts[context].segments[Segment::Stack.unscale()] .content } @@ -642,8 +667,8 @@ impl<'a> Interpreter<'a> { if !self.is_kernel() { let gas_limit_address = MemoryAddress { context: self.context(), - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), }; let gas_limit = u256_to_usize(self.generation_state.memory.get(gas_limit_address))? as u64; @@ -828,11 +853,11 @@ impl<'a> Interpreter<'a> { } fn run_keccak_general(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); + // Not strictly needed but here to avoid surprises with MSIZE. assert_ne!(segment, Segment::MainMemory, "Call KECCAK256 instead."); - let offset = self.pop()?.as_usize(); let size = self.pop()?.as_usize(); let bytes = (offset..offset + size) .map(|i| { @@ -983,7 +1008,7 @@ impl<'a> Interpreter<'a> { let mem_write_op = InterpreterMemOpKind::Write( old_value, self.context(), - Segment::Stack as usize, + Segment::Stack.unscale(), len - n as usize - 1, ); self.memops.push(mem_write_op); @@ -992,16 +1017,17 @@ impl<'a> Interpreter<'a> { } fn run_get_context(&mut self) -> anyhow::Result<(), ProgramError> { - self.push(self.context().into()) + self.push(U256::from(self.context()) << CONTEXT_SCALING_FACTOR) } fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { - let new_ctx = self.pop()?.as_usize(); + let x = self.pop()?; + let new_ctx = (x >> CONTEXT_SCALING_FACTOR).as_usize(); let sp_to_save = self.stack_len().into(); let old_ctx = self.context(); - let sp_field = ContextMetadata::StackSize as usize; + let sp_field = ContextMetadata::StackSize.unscale(); let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); @@ -1011,8 +1037,8 @@ impl<'a> Interpreter<'a> { if new_sp > 0 { let new_stack_top = self.generation_state.memory.contexts[new_ctx].segments - [Segment::Stack as usize] - .content[new_sp - 1]; + [Segment::Stack.unscale()] + .content[new_sp - 1]; self.generation_state.registers.stack_top = new_stack_top; } self.set_context(new_ctx); @@ -1021,9 +1047,8 @@ impl<'a> Interpreter<'a> { } fn run_mload_general(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let value = self .generation_state .memory @@ -1033,9 +1058,8 @@ impl<'a> Interpreter<'a> { } fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let len = self.pop()?.as_usize(); if len > 32 { return Err(ProgramError::IntegerTooLarge); @@ -1054,9 +1078,8 @@ impl<'a> Interpreter<'a> { fn run_mstore_general(&mut self) -> anyhow::Result<(), ProgramError> { let value = self.pop()?; - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let memop = self .generation_state .memory @@ -1066,9 +1089,8 @@ impl<'a> Interpreter<'a> { } fn run_mstore_32bytes(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let value = self.pop()?; let mut bytes = vec![0; 32]; @@ -1086,7 +1108,7 @@ impl<'a> Interpreter<'a> { self.memops.push(memop); } - self.push(U256::from(offset + n as usize)) + self.push(addr + U256::from(n)) } fn run_exit_kernel(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1447,14 +1469,28 @@ fn get_mnemonic(opcode: u8) -> &'static str { } } +#[macro_use] +macro_rules! unpack_address { + ($addr:ident) => {{ + let offset = $addr.low_u32() as usize; + let segment = Segment::all()[($addr >> SEGMENT_SCALING_FACTOR).low_u32() as usize]; + let context = ($addr >> CONTEXT_SCALING_FACTOR).low_u32() as usize; + (context, segment, offset) + }}; +} +pub(crate) use unpack_address; + #[cfg(test)] mod tests { use std::collections::HashMap; + use ethereum_types::U256; + use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::interpreter::{run, Interpreter}; use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; + use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_run() -> anyhow::Result<()> { @@ -1491,8 +1527,9 @@ mod tests { interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 100_000.into()); + interpreter.generation_state.memory.contexts[1].segments + [Segment::ContextMetadata.unscale()] + .set(ContextMetadata::GasLimit.unscale(), 100_000.into()); // Set context and kernel mode. interpreter.set_context(1); interpreter.set_is_kernel(false); @@ -1501,7 +1538,7 @@ mod tests { MemoryAddress::new( 1, Segment::ContextMetadata, - ContextMetadata::ParentProgramCounter as usize, + ContextMetadata::ParentProgramCounter.unscale(), ), 0xdeadbeefu32.into(), ); @@ -1509,9 +1546,9 @@ mod tests { MemoryAddress::new( 1, Segment::ContextMetadata, - ContextMetadata::ParentContext as usize, + ContextMetadata::ParentContext.unscale(), ), - 1.into(), + U256::one() << CONTEXT_SCALING_FACTOR, ); interpreter.run()?; @@ -1522,12 +1559,12 @@ mod tests { assert_eq!(interpreter.stack(), &[0xff.into(), 0xff00.into()]); assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize] + interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] .get(0x27), 0x42.into() ); assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize] + interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] .get(0x1f), 0xff.into() ); diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 20c98bf976..28b9ae7d97 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -17,6 +17,7 @@ use crate::generation::mpt::{load_all_mpts, AccountRlp}; use crate::generation::TrieInputs; use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::Node; pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieInputs) { @@ -24,27 +25,14 @@ pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieI let (trie_root_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - let state_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::StateTrieRoot as usize, - ); - - let txn_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::TransactionTrieRoot as usize, - ); - let receipts_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::ReceiptTrieRoot as usize, - ); - let len_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::TrieDataSize as usize, - ); + let state_addr = + MemoryAddress::new_bundle((GlobalMetadata::StateTrieRoot as usize).into()).unwrap(); + let txn_addr = + MemoryAddress::new_bundle((GlobalMetadata::TransactionTrieRoot as usize).into()).unwrap(); + let receipts_addr = + MemoryAddress::new_bundle((GlobalMetadata::ReceiptTrieRoot as usize).into()).unwrap(); + let len_addr = + MemoryAddress::new_bundle((GlobalMetadata::TrieDataSize as usize).into()).unwrap(); let to_set = [ (state_addr, trie_root_ptrs.state_root_ptr.into()), @@ -202,8 +190,8 @@ fn test_extcodecopy() -> Result<()> { let context = interpreter.context(); interpreter.generation_state.memory.contexts[context].segments - [Segment::ContextMetadata as usize] - .set(GasLimit as usize, U256::from(1000000000000u64)); + [Segment::ContextMetadata.unscale()] + .set(GasLimit.unscale(), U256::from(1000000000000u64)); let extcodecopy = KERNEL.global_labels["sys_extcodecopy"]; @@ -211,11 +199,11 @@ fn test_extcodecopy() -> Result<()> { let mut rng = thread_rng(); for i in 0..2000 { interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .set(i, U256::from(rng.gen::())); + [Segment::MainMemory.unscale()] + .set(i, U256::from(rng.gen::())); interpreter.generation_state.memory.contexts[context].segments - [Segment::KernelAccountCode as usize] - .set(i, U256::from(rng.gen::())); + [Segment::KernelAccountCode.unscale()] + .set(i, U256::from(rng.gen::())); } // Random inputs @@ -251,8 +239,8 @@ fn test_extcodecopy() -> Result<()> { // Check that the code was correctly copied to memory. for i in 0..size { let memory = interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .get(dest_offset + i); + [Segment::MainMemory.unscale()] + .get(dest_offset + i); assert_eq!( memory, code.get(offset + i).copied().unwrap_or_default().into() @@ -277,30 +265,23 @@ fn prepare_interpreter_all_accounts( // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = 0; interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set( - ContextMetadata::Address as usize, - U256::from_big_endian(&addr), - ); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 100_000.into()); + interpreter.set_context_metadata_field( + 1, + ContextMetadata::Address, + U256::from_big_endian(&addr), + ); + interpreter.set_context_metadata_field(1, ContextMetadata::GasLimit, 100_000.into()); interpreter.set_context(1); interpreter.set_is_kernel(false); - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentProgramCounter as usize, - ), + interpreter.set_context_metadata_field( + 1, + ContextMetadata::ParentProgramCounter, 0xdeadbeefu32.into(), ); - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentContext as usize, - ), - 1.into(), + interpreter.set_context_metadata_field( + 1, + ContextMetadata::ParentContext, + U256::one() << CONTEXT_SCALING_FACTOR, // ctx = 1 ); Ok(()) diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 9ba65db2c9..1e71d60dbe 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -16,7 +16,7 @@ use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::proof::TrieRoots; use crate::util::h2u; @@ -199,8 +199,7 @@ fn test_add11_yml() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } @@ -331,8 +330,7 @@ fn test_add11_yml_with_exception() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter .run() diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs index c62d48656b..69dd2d27d4 100644 --- a/evm/src/cpu/kernel/tests/core/access_lists.rs +++ b/evm/src/cpu/kernel/tests/core/access_lists.rs @@ -9,7 +9,7 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata::{ AccessedAddressesLen, AccessedStorageKeysLen, }; use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys, GlobalMetadata}; +use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys}; use crate::witness::memory::MemoryAddress; #[test] @@ -42,17 +42,16 @@ fn test_insert_accessed_addresses() -> Result<()> { .set(MemoryAddress::new(0, AccessedAddresses, i), addr); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), U256::from(n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[U256::zero()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedAddressesLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), U256::from(n) ); @@ -67,17 +66,16 @@ fn test_insert_accessed_addresses() -> Result<()> { .set(MemoryAddress::new(0, AccessedAddresses, i), addr); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), U256::from(n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[U256::one()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedAddressesLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), U256::from(n + 1) ); assert_eq!( @@ -134,17 +132,16 @@ fn test_insert_accessed_storage_keys() -> Result<()> { ); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), U256::from(3 * n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[storage_key_in_list.2, U256::zero()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedStorageKeysLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), U256::from(3 * n) ); @@ -172,7 +169,7 @@ fn test_insert_accessed_storage_keys() -> Result<()> { ); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), U256::from(3 * n), ); interpreter.run()?; @@ -181,11 +178,10 @@ fn test_insert_accessed_storage_keys() -> Result<()> { &[storage_key_not_in_list.2, U256::one()] ); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedStorageKeysLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), U256::from(3 * (n + 1)) ); assert_eq!( diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 022a18d729..1d686d623d 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -1,8 +1,10 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_jumpdest_analysis() -> Result<()> { @@ -28,7 +30,11 @@ fn test_jumpdest_analysis() -> Result<()> { let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true]; // Contract creation transaction. - let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + code.len().into(), + U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, + ]; let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); interpreter.set_code(CONTEXT, code); interpreter.run()?; diff --git a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs index c13b812220..e51e60ab46 100644 --- a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs +++ b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs @@ -1,7 +1,9 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn hex_prefix_even_nonterminated() -> Result<()> { @@ -11,11 +13,11 @@ fn hex_prefix_even_nonterminated() -> Result<()> { let terminated = 0.into(); let packed_nibbles = 0xABCDEF.into(); let num_nibbles = 6.into(); - let rlp_pos = 0.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![5.into()]); + assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(5)]); assert_eq!( interpreter.get_rlp_memory(), @@ -39,11 +41,11 @@ fn hex_prefix_odd_terminated() -> Result<()> { let terminated = 1.into(); let packed_nibbles = 0xABCDE.into(); let num_nibbles = 5.into(); - let rlp_pos = 0.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![4.into()]); + assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(4)]); assert_eq!( interpreter.get_rlp_memory(), @@ -66,11 +68,14 @@ fn hex_prefix_odd_terminated_tiny() -> Result<()> { let terminated = 1.into(); let packed_nibbles = 0xA.into(); let num_nibbles = 1.into(); - let rlp_pos = 2.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![3.into()]); + assert_eq!( + interpreter.stack(), + vec![U256::from(Segment::RlpRaw as usize + 3)] + ); assert_eq!( interpreter.get_rlp_memory(), diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs index 43ca9b5fc2..5517001f0d 100644 --- a/evm/src/cpu/kernel/tests/packing.rs +++ b/evm/src/cpu/kernel/tests/packing.rs @@ -11,10 +11,8 @@ fn test_mload_packing_1_byte() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 1.into(); - let offset = 2.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64 + 2).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB]); @@ -31,10 +29,8 @@ fn test_mload_packing_3_bytes() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 3.into(); - let offset = 2.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64 + 2).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB, 0xCD, 0xEF]); @@ -51,10 +47,8 @@ fn test_mload_packing_32_bytes() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 32.into(); - let offset = 0.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0xFF; 32]); @@ -72,15 +66,13 @@ fn test_mstore_unpacking() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 4.into(); let value = 0xABCD1234u32.into(); - let offset = 0.into(); - let segment = (Segment::TxnData as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, value, offset, segment, context]; + let addr = (Segment::TxnData as u64).into(); + let initial_stack = vec![retdest, len, value, addr]; let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![4.into()]); + assert_eq!(interpreter.stack(), vec![addr + U256::from(4)]); assert_eq!( &interpreter.get_txn_data(), &[0xAB.into(), 0xCD.into(), 0x12.into(), 0x34.into()] diff --git a/evm/src/cpu/kernel/tests/rlp/decode.rs b/evm/src/cpu/kernel/tests/rlp/decode.rs index a1ca3609ad..1f3260e56f 100644 --- a/evm/src/cpu/kernel/tests/rlp/decode.rs +++ b/evm/src/cpu/kernel/tests/rlp/decode.rs @@ -1,20 +1,25 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn test_decode_rlp_string_len_short() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by "0x70" which is its own encoding. interpreter.set_rlp_memory(vec![123, 234, 0x70]); interpreter.run()?; - let expected_stack = vec![1.into(), 2.into()]; // len, pos + let expected_stack = vec![1.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -24,14 +29,17 @@ fn test_decode_rlp_string_len_short() -> Result<()> { fn test_decode_rlp_string_len_medium() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by the RLP encoding of "1 2 3 4 5". interpreter.set_rlp_memory(vec![123, 234, 0x85, 1, 2, 3, 4, 5]); interpreter.run()?; - let expected_stack = vec![5.into(), 3.into()]; // len, pos + let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 3)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -41,7 +49,10 @@ fn test_decode_rlp_string_len_medium() -> Result<()> { fn test_decode_rlp_string_len_long() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // The RLP encoding of the string "1 2 3 ... 56". @@ -52,7 +63,7 @@ fn test_decode_rlp_string_len_long() -> Result<()> { ]); interpreter.run()?; - let expected_stack = vec![56.into(), 4.into()]; // len, pos + let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 4)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -62,14 +73,14 @@ fn test_decode_rlp_string_len_long() -> Result<()> { fn test_decode_rlp_list_len_short() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, 2, [3, 4]]. interpreter.set_rlp_memory(vec![0xc5, 1, 2, 0xc2, 3, 4]); interpreter.run()?; - let expected_stack = vec![5.into(), 1.into()]; // len, pos + let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 1)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -79,7 +90,7 @@ fn test_decode_rlp_list_len_short() -> Result<()> { fn test_decode_rlp_list_len_long() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, ..., 56]. @@ -90,7 +101,7 @@ fn test_decode_rlp_list_len_long() -> Result<()> { ]); interpreter.run()?; - let expected_stack = vec![56.into(), 2.into()]; // len, pos + let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -100,14 +111,14 @@ fn test_decode_rlp_list_len_long() -> Result<()> { fn test_decode_rlp_scalar() -> Result<()> { let decode_rlp_scalar = KERNEL.global_labels["decode_rlp_scalar"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); // The RLP encoding of "12 34 56". interpreter.set_rlp_memory(vec![0x83, 0x12, 0x34, 0x56]); interpreter.run()?; - let expected_stack = vec![0x123456.into(), 4.into()]; // scalar, pos + let expected_stack = vec![0x123456.into(), U256::from(Segment::RlpRaw as usize + 4)]; // scalar, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs index 2771dea0f9..505c99df88 100644 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -1,7 +1,9 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn test_encode_rlp_scalar_small() -> Result<()> { @@ -9,12 +11,12 @@ fn test_encode_rlp_scalar_small() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let scalar = 42.into(); - let pos = 2.into(); + let pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, scalar, pos]; let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); interpreter.run()?; - let expected_stack = vec![3.into()]; // pos' = pos + rlp_len = 2 + 1 + let expected_stack = vec![pos + U256::from(1)]; // pos' = pos + rlp_len = 2 + 1 let expected_rlp = vec![0, 0, 42]; assert_eq!(interpreter.stack(), expected_stack); assert_eq!(interpreter.get_rlp_memory(), expected_rlp); @@ -28,12 +30,12 @@ fn test_encode_rlp_scalar_medium() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let scalar = 0x12345.into(); - let pos = 2.into(); + let pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, scalar, pos]; let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); interpreter.run()?; - let expected_stack = vec![6.into()]; // pos' = pos + rlp_len = 2 + 4 + let expected_stack = vec![pos + U256::from(4)]; // pos' = pos + rlp_len = 2 + 4 let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45]; assert_eq!(interpreter.stack(), expected_stack); assert_eq!(interpreter.get_rlp_memory(), expected_rlp); @@ -47,12 +49,12 @@ fn test_encode_rlp_160() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); - let pos = 0.into(); + let pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, string, pos]; let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack); interpreter.run()?; - let expected_stack = vec![(1 + 20).into()]; // pos' + let expected_stack = vec![pos + U256::from(1 + 20)]; // pos' #[rustfmt::skip] let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; assert_eq!(interpreter.stack(), expected_stack); @@ -67,12 +69,12 @@ fn test_encode_rlp_256() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); - let pos = 0.into(); + let pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, string, pos]; let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack); interpreter.run()?; - let expected_stack = vec![(1 + 32).into()]; // pos' + let expected_stack = vec![pos + U256::from(1 + 32)]; // pos' #[rustfmt::skip] let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; assert_eq!(interpreter.stack(), expected_stack); @@ -86,8 +88,8 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> { let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; let retdest = 0xDEADBEEFu32.into(); - let start_pos = 9.into(); - let end_pos = (9 + 5).into(); + let start_pos = U256::from(Segment::RlpRaw as usize + 9); + let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 5); let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); interpreter.set_rlp_memory(vec![ @@ -100,7 +102,7 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> { interpreter.run()?; let expected_rlp_len = 6.into(); - let expected_start_pos = 8.into(); + let expected_start_pos = U256::from(Segment::RlpRaw as usize + 8); let expected_stack = vec![expected_rlp_len, expected_start_pos]; let expected_rlp = vec![0, 0, 0, 0, 0, 0, 0, 0, 0xc0 + 5, 1, 2, 3, 4, 5]; @@ -115,8 +117,8 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; let retdest = 0xDEADBEEFu32.into(); - let start_pos = 9.into(); - let end_pos = (9 + 60).into(); + let start_pos = U256::from(Segment::RlpRaw as usize + 9); + let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 60); let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); @@ -136,7 +138,7 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { interpreter.run()?; let expected_rlp_len = 62.into(); - let expected_start_pos = 7.into(); + let expected_start_pos = U256::from(Segment::RlpRaw as usize + 7); let expected_stack = vec![expected_rlp_len, expected_start_pos]; #[rustfmt::skip] diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 304bb3de1c..2073e182e1 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -5,23 +5,17 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use super::cpu_stark::get_addr; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cpu::stack; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; const fn get_addr_load(lv: &CpuColumnsView) -> (T, T, T) { - let addr_context = lv.mem_channels[0].value[0]; - let addr_segment = lv.mem_channels[1].value[0]; - let addr_virtual = lv.mem_channels[2].value[0]; - (addr_context, addr_segment, addr_virtual) + get_addr(lv, 0) } const fn get_addr_store(lv: &CpuColumnsView) -> (T, T, T) { - let addr_context = lv.mem_channels[1].value[0]; - let addr_segment = lv.mem_channels[2].value[0]; - let addr_virtual = lv.mem_channels[3].value[0]; - (addr_context, addr_segment, addr_virtual) + get_addr(lv, 1) } /// Evaluates constraints for MLOAD_GENERAL. @@ -36,7 +30,7 @@ fn eval_packed_load( let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); // Check that we are loading the correct value from the correct address. - let load_channel = lv.mem_channels[3]; + let load_channel = lv.mem_channels[1]; yield_constr.constraint(filter * (load_channel.used - P::ONES)); yield_constr.constraint(filter * (load_channel.is_read - P::ONES)); yield_constr.constraint(filter * (load_channel.addr_context - addr_context)); @@ -53,7 +47,7 @@ fn eval_packed_load( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS] { + for &channel in &lv.mem_channels[2..] { yield_constr.constraint(filter * channel.used); } yield_constr.constraint(filter * lv.partial_channel.used); @@ -83,7 +77,7 @@ fn eval_ext_circuit_load, const D: usize>( let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); // Check that we are loading the correct value from the correct channel. - let load_channel = lv.mem_channels[3]; + let load_channel = lv.mem_channels[1]; { let constr = builder.mul_sub_extension(filter, load_channel.used, filter); yield_constr.constraint(builder, constr); @@ -117,7 +111,7 @@ fn eval_ext_circuit_load, const D: usize>( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } @@ -157,13 +151,13 @@ fn eval_packed_store( yield_constr.constraint(filter * (store_channel.addr_virtual - addr_virtual)); // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { yield_constr.constraint(filter * channel.used); } // Stack constraints. // Pops. - for i in 1..4 { + for i in 1..2 { let channel = lv.mem_channels[i]; yield_constr.constraint(filter * (channel.used - P::ONES)); @@ -171,19 +165,21 @@ fn eval_packed_store( yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( - filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + filter + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); } // Constrain `stack_inv_aux`. - let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(4); + let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(2); yield_constr.constraint( lv.op.m_op_general * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), ); - // If stack_len != 4 and MSTORE, read new top of the stack in nv.mem_channels[0]. + // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. let top_read_channel = nv.mem_channels[0]; let is_top_read = lv.general.stack().stack_inv_aux * (P::ONES - lv.opcode_bits[0]); // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * opcode_bits[0]`. @@ -196,12 +192,11 @@ fn eval_packed_store( yield_constr.constraint_transition( new_filter * (top_read_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); - - // If stack_len == 4 or MLOAD, disable the channel. + // If stack_len == 2 or MLOAD, disable the channel. yield_constr.constraint( lv.op.m_op_general * (lv.general.stack().stack_inv_aux - P::ONES) * top_read_channel.used, ); @@ -245,14 +240,14 @@ fn eval_ext_circuit_store, const D: usize>( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } // Stack constraints // Pops. - for i in 1..4 { + for i in 1..2 { let channel = lv.mem_channels[i]; { @@ -271,7 +266,7 @@ fn eval_ext_circuit_store, const D: usize>( { let diff = builder.add_const_extension( channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); @@ -285,7 +280,7 @@ fn eval_ext_circuit_store, const D: usize>( } // Constrain `stack_inv_aux`. { - let len_diff = builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(4)); + let len_diff = builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(2)); let diff = builder.mul_sub_extension( len_diff, lv.general.stack().stack_inv, @@ -294,7 +289,7 @@ fn eval_ext_circuit_store, const D: usize>( let constr = builder.mul_extension(lv.op.m_op_general, diff); yield_constr.constraint(builder, constr); } - // If stack_len != 4 and MSTORE, read new top of the stack in nv.mem_channels[0]. + // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. let top_read_channel = nv.mem_channels[0]; let is_top_read = builder.mul_extension(lv.general.stack().stack_inv_aux, lv.opcode_bits[0]); let is_top_read = builder.sub_extension(lv.general.stack().stack_inv_aux, is_top_read); @@ -321,7 +316,7 @@ fn eval_ext_circuit_store, const D: usize>( { let diff = builder.add_const_extension( top_read_channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); @@ -332,7 +327,7 @@ fn eval_ext_circuit_store, const D: usize>( let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); } - // If stack_len == 4 or MLOAD, disable the channel. + // If stack_len == 2 or MLOAD, disable the channel. { let diff = builder.mul_sub_extension( lv.op.m_op_general, diff --git a/evm/src/cpu/shift.rs b/evm/src/cpu/shift.rs index 3d97c2f1a1..29baa5ea63 100644 --- a/evm/src/cpu/shift.rs +++ b/evm/src/cpu/shift.rs @@ -24,7 +24,7 @@ pub(crate) fn eval_packed( // let val = lv.mem_channels[0]; // let output = lv.mem_channels[NUM_GP_CHANNELS - 1]; - let shift_table_segment = P::Scalar::from_canonical_u64(Segment::ShiftTable as u64); + let shift_table_segment = P::Scalar::from_canonical_usize(Segment::ShiftTable.unscale()); // Only lookup the shifting factor when displacement is < 2^32. // two_exp.used is true (1) if the high limbs of the displacement are @@ -73,7 +73,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let displacement = lv.mem_channels[0]; let two_exp = lv.mem_channels[2]; - let shift_table_segment = F::from_canonical_u64(Segment::ShiftTable as u64); + let shift_table_segment = F::from_canonical_usize(Segment::ShiftTable.unscale()); // Only lookup the shifting factor when displacement is < 2^32. // two_exp.used is true (1) if the high limbs of the displacement are diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 0497b228a4..9acf1f3af0 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -83,13 +83,13 @@ pub(crate) const JUMPI_OP: Option = Some(StackBehavior { }); /// `StackBehavior` for MLOAD_GENERAL. pub(crate) const MLOAD_GENERAL_OP: Option = Some(StackBehavior { - num_pops: 3, + num_pops: 1, pushes: true, disable_other_channels: false, }); pub(crate) const KECCAK_GENERAL_OP: StackBehavior = StackBehavior { - num_pops: 4, + num_pops: 2, pushes: true, disable_other_channels: true, }; @@ -132,7 +132,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol dup_swap: None, context_op: None, m_op_32bytes: Some(StackBehavior { - num_pops: 4, + num_pops: 2, pushes: true, disable_other_channels: false, }), @@ -186,7 +186,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); @@ -212,7 +213,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); yield_constr.constraint_transition( new_filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); @@ -238,7 +240,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint(new_filter * (channel.addr_context - lv.context)); yield_constr.constraint( new_filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = lv.stack_len - P::ONES; yield_constr.constraint(new_filter * (channel.addr_virtual - addr_virtual)); @@ -343,7 +346,7 @@ pub(crate) fn eval_packed( yield_constr.constraint_transition( new_filter * (top_read_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); @@ -397,7 +400,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), filter, channel.addr_segment, filter, @@ -454,7 +457,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -507,7 +510,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -674,7 +677,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( { let diff = builder.add_const_extension( top_read_channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index 45302b9e71..501b114ff7 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -45,7 +45,7 @@ pub(crate) fn eval_packed( } // Look up the handler in memory - let code_segment = P::Scalar::from_canonical_usize(Segment::Code as usize); + let code_segment = P::Scalar::from_canonical_usize(Segment::Code.unscale()); let opcode: P = lv .opcode_bits @@ -153,7 +153,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( } // Look up the handler in memory - let code_segment = F::from_canonical_usize(Segment::Code as usize); + let code_segment = F::from_canonical_usize(Segment::Code.unscale()); let opcode = lv .opcode_bits diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d691d34e61..8ae487b0c0 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -155,7 +155,8 @@ fn apply_metadata_and_tries_memops, const D: usize> .map(|(field, val)| { mem_write_log( channel, - MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + // These fields are already scaled by their segment, and are in context 0 (kernel). + MemoryAddress::new_bundle(U256::from(field as usize)).unwrap(), state, val, ) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b2a8f0cea0..b60233d9d0 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -20,6 +20,7 @@ use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; use crate::witness::errors::ProgramError; use crate::witness::errors::ProverInputError::*; use crate::witness::memory::MemoryAddress; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::witness::util::{current_context_peek, stack_peek}; /// Prover input function represented as a scoped function name. @@ -138,7 +139,7 @@ impl GenerationState { fn run_account_code(&mut self) -> Result { // stack: codehash, ctx, ... let codehash = stack_peek(self, 0)?; - let context = stack_peek(self, 1)?; + let context = stack_peek(self, 1)? >> CONTEXT_SCALING_FACTOR; let context = u256_to_usize(context)?; let mut address = MemoryAddress::new(context, Segment::Code, 0); let code = self @@ -189,11 +190,11 @@ impl GenerationState { m_start_loc: usize, ) -> (Vec, Vec) { let n = self.memory.contexts.len(); - let a = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let a = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [a_start_loc..a_start_loc + len]; - let b = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let b = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [b_start_loc..b_start_loc + len]; - let m = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let m = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [m_start_loc..m_start_loc + len]; let a_biguint = mem_vec_to_biguint(a); diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 89ff0c5af9..fec2e11ca8 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -57,7 +57,7 @@ impl GenerationState { let (trie_roots_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - self.memory.contexts[0].segments[Segment::TrieData as usize].content = trie_data; + self.memory.contexts[0].segments[Segment::TrieData.unscale()].content = trie_data; trie_roots_ptrs } @@ -131,13 +131,11 @@ impl GenerationState { } let ctx = self.registers.context; - let returndata_size_addr = MemoryAddress::new( - ctx, - Segment::ContextMetadata, - ContextMetadata::ReturndataSize as usize, - ); + let returndata_offset = ContextMetadata::ReturndataSize.unscale(); + let returndata_size_addr = + MemoryAddress::new(ctx, Segment::ContextMetadata, returndata_offset); let returndata_size = u256_to_usize(self.memory.get(returndata_size_addr))?; - let code = self.memory.contexts[ctx].segments[Segment::Returndata as usize].content + let code = self.memory.contexts[ctx].segments[Segment::Returndata.unscale()].content [..returndata_size] .iter() .map(|x| x.low_u32() as u8) diff --git a/evm/src/generation/trie_extractor.rs b/evm/src/generation/trie_extractor.rs index a7c97e1006..d55a1fbf98 100644 --- a/evm/src/generation/trie_extractor.rs +++ b/evm/src/generation/trie_extractor.rs @@ -58,7 +58,7 @@ pub(crate) fn read_trie_helper( ) -> Result<(), ProgramError> { let load = |offset| memory.get(MemoryAddress::new(0, Segment::TrieData, offset)); let load_slice_from = |init_offset| { - &memory.contexts[0].segments[Segment::TrieData as usize].content[init_offset..] + &memory.contexts[0].segments[Segment::TrieData.unscale()].content[init_offset..] }; let trie_type = PartialTrieType::all()[u256_to_usize(load(ptr))?]; diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 2cfc3409ec..46ab717999 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -859,11 +859,7 @@ mod tests { let expected_output = keccak(&input); let op = KeccakSpongeOp { - base_address: MemoryAddress { - context: 0, - segment: Segment::Code as usize, - virt: 0, - }, + base_address: MemoryAddress::new(0, Segment::Code, 0), timestamp: 0, input, }; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index c2af69b542..e596f421ea 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -368,7 +368,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { + *self as usize >> SEGMENT_SCALING_FACTOR + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::Code, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 633a8d331d..5c10e3b3db 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -431,78 +431,94 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, // Add metadata writes. let block_fields_scalars = [ ( - GlobalMetadata::BlockTimestamp as usize, + GlobalMetadata::BlockTimestamp, public_values.block_metadata.block_timestamp, ), ( - GlobalMetadata::BlockNumber as usize, + GlobalMetadata::BlockNumber, public_values.block_metadata.block_number, ), ( - GlobalMetadata::BlockDifficulty as usize, + GlobalMetadata::BlockDifficulty, public_values.block_metadata.block_difficulty, ), ( - GlobalMetadata::BlockGasLimit as usize, + GlobalMetadata::BlockGasLimit, public_values.block_metadata.block_gaslimit, ), ( - GlobalMetadata::BlockChainId as usize, + GlobalMetadata::BlockChainId, public_values.block_metadata.block_chain_id, ), ( - GlobalMetadata::BlockGasUsed as usize, + GlobalMetadata::BlockGasUsed, public_values.block_metadata.block_gas_used, ), ( - GlobalMetadata::BlockGasUsedBefore as usize, + GlobalMetadata::BlockGasUsedBefore, public_values.extra_block_data.gas_used_before, ), ( - GlobalMetadata::BlockGasUsedAfter as usize, + GlobalMetadata::BlockGasUsedAfter, public_values.extra_block_data.gas_used_after, ), ( - GlobalMetadata::TxnNumberBefore as usize, + GlobalMetadata::TxnNumberBefore, public_values.extra_block_data.txn_number_before, ), ( - GlobalMetadata::TxnNumberAfter as usize, + GlobalMetadata::TxnNumberAfter, public_values.extra_block_data.txn_number_after, ), ]; - let beneficiary_random_base_fee_cur_hash_fields: [(usize, &[Target]); 4] = [ + let beneficiary_random_base_fee_cur_hash_fields: [(GlobalMetadata, &[Target]); 4] = [ ( - GlobalMetadata::BlockBeneficiary as usize, + GlobalMetadata::BlockBeneficiary, &public_values.block_metadata.block_beneficiary, ), ( - GlobalMetadata::BlockRandom as usize, + GlobalMetadata::BlockRandom, &public_values.block_metadata.block_random, ), ( - GlobalMetadata::BlockBaseFee as usize, + GlobalMetadata::BlockBaseFee, &public_values.block_metadata.block_base_fee, ), ( - GlobalMetadata::BlockCurrentHash as usize, + GlobalMetadata::BlockCurrentHash, &public_values.block_hashes.cur_hash, ), ]; - let metadata_segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); + let metadata_segment = + builder.constant(F::from_canonical_usize(Segment::GlobalMetadata.unscale())); block_fields_scalars.map(|(field, target)| { // Each of those fields fit in 32 bits, hence in a single Target. - sum = add_data_write(builder, challenge, sum, metadata_segment, field, &[target]); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + &[target], + ); }); beneficiary_random_base_fee_cur_hash_fields.map(|(field, targets)| { - sum = add_data_write(builder, challenge, sum, metadata_segment, field, targets); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + targets, + ); }); // Add block hashes writes. - let block_hashes_segment = builder.constant(F::from_canonical_u32(Segment::BlockHashes as u32)); + let block_hashes_segment = + builder.constant(F::from_canonical_usize(Segment::BlockHashes.unscale())); for i in 0..256 { sum = add_data_write( builder, @@ -515,7 +531,8 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, } // Add block bloom filters writes. - let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32)); + let bloom_segment = + builder.constant(F::from_canonical_usize(Segment::GlobalBlockBloom.unscale())); for i in 0..8 { sum = add_data_write( builder, @@ -530,33 +547,40 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, // Add trie roots writes. let trie_fields = [ ( - GlobalMetadata::StateTrieRootDigestBefore as usize, + GlobalMetadata::StateTrieRootDigestBefore, public_values.trie_roots_before.state_root, ), ( - GlobalMetadata::TransactionTrieRootDigestBefore as usize, + GlobalMetadata::TransactionTrieRootDigestBefore, public_values.trie_roots_before.transactions_root, ), ( - GlobalMetadata::ReceiptTrieRootDigestBefore as usize, + GlobalMetadata::ReceiptTrieRootDigestBefore, public_values.trie_roots_before.receipts_root, ), ( - GlobalMetadata::StateTrieRootDigestAfter as usize, + GlobalMetadata::StateTrieRootDigestAfter, public_values.trie_roots_after.state_root, ), ( - GlobalMetadata::TransactionTrieRootDigestAfter as usize, + GlobalMetadata::TransactionTrieRootDigestAfter, public_values.trie_roots_after.transactions_root, ), ( - GlobalMetadata::ReceiptTrieRootDigestAfter as usize, + GlobalMetadata::ReceiptTrieRootDigestAfter, public_values.trie_roots_after.receipts_root, ), ]; trie_fields.map(|(field, targets)| { - sum = add_data_write(builder, challenge, sum, metadata_segment, field, &targets); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + &targets, + ); }); // Add kernel hash and kernel length. @@ -567,7 +591,7 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, challenge, sum, metadata_segment, - GlobalMetadata::KernelHash as usize, + GlobalMetadata::KernelHash.unscale(), &kernel_hash_targets, ); let kernel_len_target = builder.constant(F::from_canonical_usize(KERNEL.code.len())); @@ -576,7 +600,7 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, challenge, sum, metadata_segment, - GlobalMetadata::KernelLen as usize, + GlobalMetadata::KernelLen.unscale(), &[kernel_len_target], ); diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 7ca564f282..b69cd9274b 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -239,19 +239,22 @@ where (GlobalMetadata::KernelLen, KERNEL.code.len().into()), ]; - let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); - fields.map(|(field, val)| sum = add_data_write(challenge, segment, sum, field as usize, val)); + fields.map(|(field, val)| { + // These fields are already scaled by their segment, and are in context 0 (kernel). + sum = add_data_write(challenge, segment, sum, field.unscale(), val) + }); // Add block bloom writes. - let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); for index in 0..8 { let val = public_values.block_metadata.block_bloom[index]; sum = add_data_write(challenge, bloom_segment, sum, index, val); } // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); for index in 0..256 { let val = h2u(public_values.block_hashes.prev_hashes[index]); sum = add_data_write(challenge, block_hashes_segment, sum, index, val); @@ -547,22 +550,22 @@ pub(crate) mod testutils { (GlobalMetadata::KernelLen, KERNEL.code.len().into()), ]; - let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); let mut extra_looking_rows = Vec::new(); fields.map(|(field, val)| { - extra_looking_rows.push(add_extra_looking_row(segment, field as usize, val)) + extra_looking_rows.push(add_extra_looking_row(segment, field.unscale(), val)) }); // Add block bloom writes. - let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); for index in 0..8 { let val = public_values.block_metadata.block_bloom[index]; extra_looking_rows.push(add_extra_looking_row(bloom_segment, index, val)); } // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); for index in 0..256 { let val = h2u(public_values.block_hashes.prev_hashes[index]); extra_looking_rows.push(add_extra_looking_row(block_hashes_segment, index, val)); diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index 8cb7daf704..ff4c96149c 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -11,8 +11,9 @@ pub(crate) enum MemoryChannel { use MemoryChannel::{Code, GeneralPurpose, PartialChannel}; +use super::operation::CONTEXT_SCALING_FACTOR; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; use crate::witness::errors::ProgramError; use crate::witness::errors::ProgramError::MemoryError; @@ -41,7 +42,8 @@ impl MemoryAddress { pub(crate) const fn new(context: usize, segment: Segment, virt: usize) -> Self { Self { context, - segment: segment as usize, + // segment is scaled + segment: segment.unscale(), virt, } } @@ -69,6 +71,17 @@ impl MemoryAddress { }) } + /// Creates a new `MemoryAddress` from a bundled address fitting a `U256`. + /// It will recover the virtual offset as the lowest 32-bit limb, the segment + /// as the next limb, and the context as the next one. + pub(crate) fn new_bundle(addr: U256) -> Result { + let virt = addr.low_u32().into(); + let segment = (addr >> SEGMENT_SCALING_FACTOR).low_u32().into(); + let context = (addr >> CONTEXT_SCALING_FACTOR).low_u32().into(); + + Self::new_u256s(context, segment, virt) + } + pub(crate) fn increment(&mut self) { self.virt = self.virt.saturating_add(1); } @@ -153,7 +166,7 @@ impl MemoryState { pub(crate) fn new(kernel_code: &[u8]) -> Self { let code_u256s = kernel_code.iter().map(|&x| x.into()).collect(); let mut result = Self::default(); - result.contexts[0].segments[Segment::Code as usize].content = code_u256s; + result.contexts[0].segments[Segment::Code.unscale()].content = code_u256s; result } @@ -204,12 +217,9 @@ impl MemoryState { self.contexts[address.context].segments[address.segment].set(address.virt, val); } + // These fields are already scaled by their respective segment. pub(crate) fn read_global_metadata(&self, field: GlobalMetadata) -> U256 { - self.get(MemoryAddress::new( - 0, - Segment::GlobalMetadata, - field as usize, - )) + self.get(MemoryAddress::new_bundle(U256::from(field as usize)).unwrap()) } } diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 848dae8532..c9dea2307b 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -19,9 +19,8 @@ use crate::extension_tower::BN_BASE; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::util::u256_to_usize; -use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; +use crate::witness::errors::MemoryError::VirtTooLarge; use crate::witness::errors::ProgramError; -use crate::witness::errors::ProgramError::MemoryError; use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; use crate::witness::operation::MemoryChannel::GeneralPurpose; use crate::witness::transition::fill_stack_fields; @@ -59,6 +58,10 @@ pub(crate) enum Operation { MstoreGeneral, } +// Contexts in the kernel are shifted by 2^64, so that they can be combined with +// the segment and virtual address components in a single U256 word. +pub(crate) const CONTEXT_SCALING_FACTOR: usize = 64; + /// Adds a CPU row filled with the two inputs and the output of a logic operation. /// Generates a new logic operation and adds it to the vector of operation in `LogicStark`. /// Adds three memory read operations to `MemoryStark`: for the two inputs and the output. @@ -129,11 +132,10 @@ pub(crate) fn generate_keccak_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; let len = u256_to_usize(len)?; - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; let input = (0..len) .map(|i| { let address = MemoryAddress { @@ -152,8 +154,6 @@ pub(crate) fn generate_keccak_general( keccak_sponge_log(state, base_address, input); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } @@ -191,7 +191,7 @@ pub(crate) fn generate_pop( ) -> Result<(), ProgramError> { let [(_, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let diff = row.stack_len - F::from_canonical_usize(1); + let diff = row.stack_len - F::ONE; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -352,7 +352,11 @@ pub(crate) fn generate_get_context( let res = mem_write_gp_log_and_fill(3, address, state, &mut row, state.registers.stack_top); Some(res) }; - push_no_write(state, state.registers.context.into()); + push_no_write( + state, + // The fetched value needs to be scaled before being pushed. + U256::from(state.registers.context) << CONTEXT_SCALING_FACTOR, + ); if let Some(log) = write { state.traces.push_memory(log); } @@ -369,9 +373,10 @@ pub(crate) fn generate_set_context( let sp_to_save = state.registers.stack_len.into(); let old_ctx = state.registers.context; - let new_ctx = u256_to_usize(ctx)?; + // The popped value needs to be scaled down. + let new_ctx = u256_to_usize(ctx >> CONTEXT_SCALING_FACTOR)?; - let sp_field = ContextMetadata::StackSize as usize; + let sp_field = ContextMetadata::StackSize.unscale(); let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); @@ -390,7 +395,7 @@ pub(crate) fn generate_set_context( channel.used = F::ONE; channel.is_read = F::ONE; channel.addr_context = F::from_canonical_usize(new_ctx); - channel.addr_segment = F::from_canonical_usize(Segment::ContextMetadata as usize); + channel.addr_segment = F::from_canonical_usize(Segment::ContextMetadata.unscale()); channel.addr_virtual = F::from_canonical_usize(new_sp_addr.virt); let val_limbs: [u64; 4] = sp_to_save.0; for (i, limb) in val_limbs.into_iter().enumerate() { @@ -433,6 +438,7 @@ pub(crate) fn generate_set_context( state.traces.push_memory(log_write_old_sp); state.traces.push_memory(log_read_new_sp); state.traces.push_cpu(row); + Ok(()) } @@ -575,7 +581,7 @@ pub(crate) fn generate_not( // This is necessary for the stack constraints for POP, // since the two flags are combined. - let diff = row.stack_len - F::from_canonical_usize(1); + let diff = row.stack_len - F::ONE; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -808,18 +814,16 @@ pub(crate) fn generate_mload_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (virt, log_in2)] = - stack_pop_with_log_and_fill::<3, _>(state, &mut row)?; + let [(addr, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let (val, log_read) = mem_read_gp_with_log_and_fill( - 3, - MemoryAddress::new_u256s(context, segment, virt)?, - state, - &mut row, - ); + let (val, log_read) = + mem_read_gp_with_log_and_fill(1, MemoryAddress::new_bundle(addr)?, state, &mut row); push_no_write(state, val); - let diff = row.stack_len - F::from_canonical_usize(4); + // Because MLOAD_GENERAL performs 1 pop and 1 push, it does not make use of the `stack_inv_aux` general columns. + // We hence can set the diff to 2 (instead of 1) so that the stack constraint for MSTORE_GENERAL applies to both + // operations, which are combined into a single CPU flag. + let diff = row.stack_len - F::TWO; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -828,8 +832,6 @@ pub(crate) fn generate_mload_general( row.general.stack_mut().stack_inv_aux = F::ZERO; } - state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); state.traces.push_memory(log_read); state.traces.push_cpu(row); Ok(()) @@ -839,15 +841,14 @@ pub(crate) fn generate_mload_32bytes( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; let len = u256_to_usize(len)?; if len > 32 { // The call to `U256::from_big_endian()` would panic. return Err(ProgramError::IntegerTooLarge); } - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; if usize::MAX - base_address.virt < len { return Err(ProgramError::MemoryError(VirtTooLarge { virt: base_address.virt.into(), @@ -870,8 +871,6 @@ pub(crate) fn generate_mload_32bytes( byte_packing_log(state, base_address, bytes); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } @@ -880,23 +879,12 @@ pub(crate) fn generate_mstore_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(val, _), (context, log_in1), (segment, log_in2), (virt, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(val, _), (addr, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let address = MemoryAddress { - context: context - .try_into() - .map_err(|_| MemoryError(ContextTooLarge { context }))?, - segment: segment - .try_into() - .map_err(|_| MemoryError(SegmentTooLarge { segment }))?, - virt: virt - .try_into() - .map_err(|_| MemoryError(VirtTooLarge { virt }))?, - }; + let address = MemoryAddress::new_bundle(addr)?; let log_write = mem_write_partial_log_and_fill(address, state, &mut row, val); - let diff = row.stack_len - F::from_canonical_usize(4); + let diff = row.stack_len - F::TWO; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -908,8 +896,6 @@ pub(crate) fn generate_mstore_general( } state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_memory(log_write); state.traces.push_cpu(row); @@ -922,19 +908,16 @@ pub(crate) fn generate_mstore_32bytes( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (val, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (val, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; byte_unpacking_log(state, base_address, val, n as usize); - let new_offset = base_virt + n; - push_no_write(state, new_offset); + let new_addr = addr + n; + push_no_write(state, new_addr); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbeba..835ff59372 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -299,11 +299,11 @@ fn perform_op( state.registers.gas_used += gas_to_charge(op); - let gas_limit_address = MemoryAddress { - context: state.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, - }; + let gas_limit_address = MemoryAddress::new( + state.registers.context, + Segment::ContextMetadata, + ContextMetadata::GasLimit.unscale(), // context offsets are already scaled + ); if !state.registers.is_kernel { let gas_limit = TryInto::::try_into(state.memory.get(gas_limit_address)); match gas_limit { @@ -345,14 +345,14 @@ pub(crate) fn fill_stack_fields( channel.used = F::ONE; channel.is_read = F::ONE; channel.addr_context = F::from_canonical_usize(state.registers.context); - channel.addr_segment = F::from_canonical_usize(Segment::Stack as usize); + channel.addr_segment = F::from_canonical_usize(Segment::Stack.unscale()); channel.addr_virtual = F::from_canonical_usize(state.registers.stack_len - 1); - let address = MemoryAddress { - context: state.registers.context, - segment: Segment::Stack as usize, - virt: state.registers.stack_len - 1, - }; + let address = MemoryAddress::new( + state.registers.context, + Segment::Stack, + state.registers.stack_len - 1, + ); let mem_op = MemoryOp::new( GeneralPurpose(0), @@ -494,7 +494,7 @@ pub(crate) fn transition(state: &mut GenerationState) -> anyhow::Re e, offset_name, state.stack(), - state.memory.contexts[0].segments[Segment::KernelGeneral as usize].content, + state.memory.contexts[0].segments[Segment::KernelGeneral.unscale()].content, ); } state.rollback(checkpoint); diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index b23b4402f9..8d56c6bd6f 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -442,7 +442,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 13..16, 15..18, 14..15, 9..10, 12..13, 17..20], + &[16..17, 13..16, 15..18, 14..15, 10..11, 12..13, 17..20], &config, ); From 47b428569d303eae8d51ea736ec60df3f033d5a3 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Mon, 8 Jan 2024 13:53:52 +0100 Subject: [PATCH 075/175] Remove unused macro --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 8f66b369a8..aa37d23d3a 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -264,34 +264,6 @@ return: %pop3 JUMP - - - -// Check if the opcode pointed by proof_prefix address is -// less than max and increment proof_prefix_addr -%macro check_and_step(max) - %stack - (proof_prefix_addr, ctx, jumpdest) -> - (ctx, @SEGMENT_CODE, proof_prefix_addr, proof_prefix_addr, ctx, jumpdest) - MLOAD_GENERAL - // stack: opcode, ctx, proof_prefix_addr, jumpdest - DUP1 - %gt_const(127) - %jumpi(%%ok) - %jumpi_lt_const($max, return) - // stack: proof_prefix_addr, ctx, jumpdest - PUSH 0 // We need something to pop -%%ok: - POP - %increment -%endmacro - -%macro write_table_if_jumpdest - %stack (proof, addr, ctx) -> (proof, addr, ctx, %%after) - %jump(write_table_if_jumpdest) -%%after: -%endmacro - // Write the jumpdest table. This is done by // non-deterministically guessing the sequence of jumpdest // addresses used during program execution within the current context. From cb19f2199443d9a22c6ab31f685bd0419d41ab2b Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:08:53 +0100 Subject: [PATCH 076/175] Add crate-level documentation (#1444) * Add crate-level documentation * Revert change * Skip * Typo * Apply comments * Rephrase paragraph * Apply comments --- evm/src/config.rs | 9 ++ evm/src/fixed_recursive_verifier.rs | 164 ++++++++++++++++++++++++++- evm/src/generation/mod.rs | 10 +- evm/src/lib.rs | 168 ++++++++++++++++++++++++++++ evm/tests/empty_txn_list.rs | 2 +- field/src/goldilocks_field.rs | 2 +- plonky2/src/fri/mod.rs | 3 + 7 files changed, 352 insertions(+), 6 deletions(-) diff --git a/evm/src/config.rs b/evm/src/config.rs index 24ddb6a78f..3f88d99f5d 100644 --- a/evm/src/config.rs +++ b/evm/src/config.rs @@ -1,16 +1,25 @@ use plonky2::fri::reduction_strategies::FriReductionStrategy; use plonky2::fri::{FriConfig, FriParams}; +/// A configuration containing the different parameters to be used by the STARK prover. pub struct StarkConfig { + /// The targeted security level for the proofs generated with this configuration. pub security_bits: usize, /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly) /// `degree / |F|`. pub num_challenges: usize, + /// The configuration of the FRI sub-protocol. pub fri_config: FriConfig, } +impl Default for StarkConfig { + fn default() -> Self { + Self::standard_fast_config() + } +} + impl StarkConfig { /// A typical configuration with a rate of 2, resulting in fast but large proofs. /// Targets ~100 bit conjectured security. diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 3f405e5246..58db998731 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -69,8 +69,10 @@ where { /// The EVM root circuit, which aggregates the (shrunk) per-table recursive proofs. pub root: RootCircuitData, + /// The aggregation circuit, which verifies two proofs that can either be root or + /// aggregation proofs. pub aggregation: AggregationCircuitData, - /// The block circuit, which verifies an aggregation root proof and a previous block proof. + /// The block circuit, which verifies an aggregation root proof and an optional previous block proof. pub block: BlockCircuitData, /// Holds chains of circuits for each table and for each initial `degree_bits`. pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], @@ -236,6 +238,8 @@ impl AggregationChildTarget { } } +/// Data for the block circuit, which is used to generate a final block proof, +/// and compress it with an optional parent proof if present. #[derive(Eq, PartialEq, Debug)] pub struct BlockCircuitData where @@ -298,6 +302,16 @@ where C: GenericConfig + 'static, C::Hasher: AlgebraicHasher, { + /// Serializes all these preprocessed circuits into a sequence of bytes. + /// + /// # Arguments + /// + /// - `skip_tables`: a boolean indicating whether to serialize only the upper circuits + /// or the entire prover state, including recursive circuits to shrink STARK proofs. + /// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits + /// common data. + /// - `generator_serializer`: a custom generator serializer needed to serialize recursive + /// circuits proving data. pub fn to_bytes( &self, skip_tables: bool, @@ -320,6 +334,17 @@ where Ok(buffer) } + /// Deserializes a sequence of bytes into an entire prover state containing all recursive circuits. + /// + /// # Arguments + /// + /// - `bytes`: a slice of bytes to deserialize this prover state from. + /// - `skip_tables`: a boolean indicating whether to deserialize only the upper circuits + /// or the entire prover state, including recursive circuits to shrink STARK proofs. + /// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits + /// common data. + /// - `generator_serializer`: a custom generator serializer needed to serialize recursive + /// circuits proving data. pub fn from_bytes( bytes: &[u8], skip_tables: bool, @@ -373,6 +398,19 @@ where } /// Preprocess all recursive circuits used by the system. + /// + /// # Arguments + /// + /// - `all_stark`: a structure defining the logic of all STARK modules and their associated + /// cross-table lookups. + /// - `degree_bits_ranges`: the logarithmic ranges to be supported for the recursive tables. + /// Transactions may yield arbitrary trace lengths for each STARK module (within some bounds), + /// unknown prior generating the witness to create a proof. Thus, for each STARK module, we + /// construct a map from `2^{degree_bits} = length` to a chain of shrinking recursion circuits, + /// starting from that length, for each `degree_bits` in the range specified for this STARK module. + /// Specifying a wide enough range allows a prover to cover all possible scenarios. + /// - `stark_config`: the configuration to be used for the STARK prover. It will usually be a fast + /// one yielding large proofs. pub fn new( all_stark: &AllStark, degree_bits_ranges: &[Range; NUM_TABLES], @@ -450,6 +488,19 @@ where /// Outputs the `VerifierCircuitData` needed to verify any block proof /// generated by an honest prover. + /// While the [`AllRecursiveCircuits`] prover state can also verify proofs, verifiers + /// only need a fraction of the state to verify proofs. This allows much less powerful + /// entities to behave as verifiers, by only loading the necessary data to verify block proofs. + /// + /// # Usage + /// + /// ```ignore + /// let prover_state = AllRecursiveCircuits { ... }; + /// let verifier_state = prover_state.final_verifier_data(); + /// + /// // Verify a provided block proof + /// assert!(verifier_state.verify(&block_proof).is_ok()); + /// ``` pub fn final_verifier_data(&self) -> VerifierCircuitData { self.block.circuit.verifier_data() } @@ -912,7 +963,29 @@ where } } - /// Create a proof for each STARK, then combine them, eventually culminating in a root proof. + /// For a given transaction payload passed as [`GenerationInputs`], create a proof + /// for each STARK module, then recursively shrink and combine them, eventually + /// culminating in a transaction proof, also called root proof. + /// + /// # Arguments + /// + /// - `all_stark`: a structure defining the logic of all STARK modules and their associated + /// cross-table lookups. + /// - `config`: the configuration to be used for the STARK prover. It will usually be a fast + /// one yielding large proofs. + /// - `generation_inputs`: a transaction and auxiliary data needed to generate a proof, provided + /// in Intermediary Representation. + /// - `timing`: a profiler defining a scope hierarchy and the time consumed by each one. + /// - `abort_signal`: an optional [`AtomicBool`] wrapped behind an [`Arc`], to send a kill signal + /// early. This is only necessary in a distributed setting where a worker may be blocking the entire + /// queue. + /// + /// # Outputs + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only + /// the proof with public inputs is necessary for a verifier to assert correctness of the computation, + /// but the public values are output for the prover convenience, as these are necessary during proof + /// aggregation. pub fn prove_root( &self, all_stark: &AllStark, @@ -981,6 +1054,50 @@ where /// From an initial set of STARK proofs passed with their associated recursive table circuits, /// generate a recursive transaction proof. /// It is aimed at being used when preprocessed table circuits have not been loaded to memory. + /// + /// **Note**: + /// The type of the `table_circuits` passed as arguments is + /// `&[(RecursiveCircuitsForTableSize, u8); NUM_TABLES]`. In particular, for each STARK + /// proof contained within the `AllProof` object provided to this method, we need to pass a tuple + /// of [`RecursiveCircuitsForTableSize`] and a [`u8`]. The former is the recursive chain + /// corresponding to the initial degree size of the associated STARK proof. The latter is the + /// index of this degree in the range that was originally passed when constructing the entire prover + /// state. + /// + /// # Usage + /// + /// ```ignore + /// // Load a prover state without its recursive table circuits. + /// let gate_serializer = DefaultGateSerializer; + /// let generator_serializer = DefaultGeneratorSerializer::::new(); + /// let initial_ranges = [16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30]; + /// let prover_state = AllRecursiveCircuits::::new( + /// &all_stark, + /// &initial_ranges, + /// &config, + /// ); + /// + /// // Generate a proof from the provided inputs. + /// let stark_proof = prove::(&all_stark, &config, inputs, &mut timing, abort_signal).unwrap(); + /// + /// // Read the degrees of the internal STARK proofs. + /// // Indices to be passed along the recursive tables + /// // can be easily recovered as `initial_ranges[i]` - `degrees[i]`. + /// let degrees = proof.degree_bits(&config); + /// + /// // Retrieve the corresponding recursive table circuits for each table with the corresponding degree. + /// let table_circuits = { ... }; + /// + /// // Finally shrink the STARK proof. + /// let (proof, public_values) = prove_root_after_initial_stark( + /// &all_stark, + /// &config, + /// &stark_proof, + /// &table_circuits, + /// &mut timing, + /// abort_signal, + /// ).unwrap(); + /// ``` pub fn prove_root_after_initial_stark( &self, all_stark: &AllStark, @@ -1031,6 +1148,31 @@ where self.root.circuit.verify(agg_proof) } + /// Create an aggregation proof, combining two contiguous proofs into a single one. The combined + /// proofs can either be transaction (aka root) proofs, or other aggregation proofs, as long as + /// their states are contiguous, meaning that the final state of the left child proof is the initial + /// state of the right child proof. + /// + /// While regular transaction proofs can only assert validity of a single transaction, aggregation + /// proofs can cover an arbitrary range, up to an entire block with all its transactions. + /// + /// # Arguments + /// + /// - `lhs_is_agg`: a boolean indicating whether the left child proof is an aggregation proof or + /// a regular transaction proof. + /// - `lhs_proof`: the left child proof. + /// - `lhs_public_values`: the public values associated to the right child proof. + /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an aggregation proof or + /// a regular transaction proof. + /// - `rhs_proof`: the right child proof. + /// - `rhs_public_values`: the public values associated to the right child proof. + /// + /// # Outputs + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only + /// the proof with public inputs is necessary for a verifier to assert correctness of the computation, + /// but the public values are output for the prover convenience, as these are necessary during proof + /// aggregation. pub fn prove_aggregation( &self, lhs_is_agg: bool, @@ -1097,6 +1239,23 @@ where ) } + /// Create a final block proof, once all transactions of a given block have been combined into a + /// single aggregation proof. + /// + /// Block proofs can either be generated as standalone, or combined with a previous block proof + /// to assert validity of a range of blocks. + /// + /// # Arguments + /// + /// - `opt_parent_block_proof`: an optional parent block proof. Passing one will generate a proof of + /// validity for both the block range covered by the previous proof and the current block. + /// - `agg_root_proof`: the final aggregation proof containing all transactions within the current block. + /// - `public_values`: the public values associated to the aggregation proof. + /// + /// # Outputs + /// + /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only + /// the proof with public inputs is necessary for a verifier to assert correctness of the computation. pub fn prove_block( &self, opt_parent_block_proof: Option<&ProofWithPublicInputs>, @@ -1245,6 +1404,7 @@ where } } +/// A map between initial degree sizes and their associated shrinking recursion circuits. #[derive(Eq, PartialEq, Debug)] pub struct RecursiveCircuitsForTable where diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 8ae487b0c0..515238ec2a 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -43,13 +43,17 @@ use crate::witness::util::mem_write_log; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] pub struct GenerationInputs { + /// The index of the transaction being proven within its block. pub txn_number_before: U256, + /// The cumulative gas used through the execution of all transactions prior the current one. pub gas_used_before: U256, + /// The cumulative gas used after the execution of the current transaction. The exact gas used + /// by the current transaction is `gas_used_after` - `gas_used_before`. pub gas_used_after: U256, - // A None would yield an empty proof, otherwise this contains the encoding of a transaction. + /// A None would yield an empty proof, otherwise this contains the encoding of a transaction. pub signed_txn: Option>, - // Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895. + /// Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895. pub withdrawals: Vec<(Address, U256)>, pub tries: TrieInputs, /// Expected trie roots after the transactions are executed. @@ -64,8 +68,10 @@ pub struct GenerationInputs { /// All account smart contracts that are invoked will have an entry present. pub contract_code: HashMap>, + /// Information contained in the block header. pub block_metadata: BlockMetadata, + /// The hash of the current block, and a list of the 256 previous block hashes. pub block_hashes: BlockHashes, } diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 42547c3eeb..025fc8e63d 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -1,3 +1,164 @@ +//! An implementation of a Type 1 zk-EVM by Polygon Zero. +//! +//! Following the [zk-EVM classification of V. Buterin](https://vitalik.eth.limo/general/2022/08/04/zkevm.html), +//! the plonky2_evm crate aims at providing an efficient solution for the problem of generating cryptographic +//! proofs of Ethereum-like transactions with *full Ethereum capability*. +//! +//! To this end, the plonky2 zk-EVM is tailored for an AIR-based STARK system satisfying degree 3 constraints, +//! with support for recursive aggregation leveraging plonky2 circuits with FRI-based plonkish arithmetization. +//! These circuits require a one-time, offline preprocessing phase. +//! See the [`fixed_recursive_verifier`] module for more details on how this works. +//! These preprocessed circuits are gathered within the [`AllRecursiveCircuits`] prover state, +//! and can be generated as such: +//! +//! ```ignore +//! // Specify the base field to use. +//! type F = GoldilocksField; +//! // Specify the extension degree to use. +//! const D: usize = 2; +//! // Specify the recursive configuration to use, here leveraging Poseidon hash +//! // over the Goldilocks field both natively and in-circuit. +//! type C = PoseidonGoldilocksConfig; +//! +//! let all_stark = AllStark::::default(); +//! let config = StarkConfig::standard_fast_config(); +//! +//! // Generate all the recursive circuits needed to generate succinct proofs for blocks. +//! // The ranges correspond to the supported table sizes for each individual STARK component. +//! let prover_state = AllRecursiveCircuits::::new( +//! &all_stark, +//! &[16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30], +//! &config, +//! ); +//! ``` +//! +//! # Inputs type +//! +//! Transactions need to be processed into an Intermediary Representation (IR) format for the prover +//! to be able to generate proofs of valid state transition. This involves passing the encoded transaction, +//! the header of the block in which it was included, some information on the state prior execution +//! of this transaction, etc. +//! This intermediary representation is called [`GenerationInputs`]. +//! +//! +//! # Generating succinct proofs +//! +//! ## Transaction proofs +//! +//! To generate a proof for a transaction, given its [`GenerationInputs`] and an [`AllRecursiveCircuits`] +//! prover state, one can simply call the [prove_root](AllRecursiveCircuits::prove_root) method. +//! +//! ```ignore +//! let mut timing = TimingTree::new("prove", log::Level::Debug); +//! let kill_signal = None; // Useful only with distributed proving to kill hanging jobs. +//! let (proof, public_values) = +//! prover_state.prove_root(all_stark, config, inputs, &mut timing, kill_signal); +//! ``` +//! +//! This outputs a transaction proof and its associated public values. These are necessary during the +//! aggregation levels (see below). If one were to miss the public values, they are also retrievable directly +//! from the proof's encoded public inputs, as such: +//! +//! ```ignore +//! let public_values = PublicValues::from_public_inputs(&proof.public_inputs); +//! ``` +//! +//! ## Aggregation proofs +//! +//! Because the plonky2 zkEVM generates proofs on a transaction basis, we then need to aggregate them for succinct +//! verification. This is done in a binary tree fashion, where each inner node proof verifies two children proofs, +//! through the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method. +//! Note that the tree does *not* need to be complete, as this aggregation process can take as inputs both regular +//! transaction proofs and aggregation proofs. We only need to specify for each child if it is an aggregation proof +//! or a regular one. +//! +//! ```ignore +//! let (proof_1, pv_1) = +//! prover_state.prove_root(all_stark, config, inputs_1, &mut timing, None); +//! let (proof_2, pv_2) = +//! prover_state.prove_root(all_stark, config, inputs_2, &mut timing, None); +//! let (proof_3, pv_3) = +//! prover_state.prove_root(all_stark, config, inputs_3, &mut timing, None); +//! +//! // Now aggregate proofs for txn 1 and 2. +//! let (agg_proof_1_2, pv_1_2) = +//! prover_state.prove_aggregation(false, proof_1, pv_1, false, proof_2, pv_2); +//! +//! // Now aggregate the newly generated aggregation proof with the last regular txn proof. +//! let (agg_proof_1_3, pv_1_3) = +//! prover_state.prove_aggregation(true, agg_proof_1_2, pv_1_2, false, proof_3, pv_3); +//! ``` +//! +//! **Note**: The proofs provided to the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method *MUST* have contiguous states. +//! Trying to combine `proof_1` and `proof_3` from the example above would fail. +//! +//! ## Block proofs +//! +//! Once all transactions of a block have been proven and we are left with a single aggregation proof and its public values, +//! we can then wrap it into a final block proof, attesting validity of the entire block. +//! This [prove_block](AllRecursiveCircuits::prove_block) method accepts an optional previous block proof as argument, +//! which will then try combining the previously proven block with the current one, generating a validity proof for both. +//! Applying this process from genesis would yield a single proof attesting correctness of the entire chain. +//! +//! ```ignore +//! let previous_block_proof = { ... }; +//! let (block_proof, block_public_values) = +//! prover_state.prove_block(Some(&previous_block_proof), &agg_proof, agg_pv)?; +//! ``` +//! +//! ### Checkpoint heights +//! +//! The process of always providing a previous block proof when generating a proof for the current block may yield some +//! undesirable issues. For this reason, the plonky2 zk-EVM supports checkpoint heights. At given block heights, +//! the prover does not have to pass a previous block proof. This would in practice correspond to block heights at which +//! a proof has been generated and sent to L1 for settlement. +//! +//! The only requirement when generating a block proof without passing a previous one as argument is to have the +//! `checkpoint_state_trie_root` metadata in the `PublicValues` of the final aggregation proof be matching the state +//! trie before applying all the included transactions. If this condition is not met, the prover will fail to generate +//! a valid proof. +//! +//! +//! ```ignore +//! let (block_proof, block_public_values) = +//! prover_state.prove_block(None, &agg_proof, agg_pv)?; +//! ``` +//! +//! # Prover state serialization +//! +//! Because the recursive circuits only need to be generated once, they can be saved to disk once the preprocessing phase +//! completed successfully, and deserialized on-demand. +//! The plonky2 zk-EVM provides serialization methods to convert the entire prover state to a vector of bytes, and vice-versa. +//! This requires the use of custom serializers for gates and generators for proper recursive circuit encoding. This crate provides +//! default serializers supporting all custom gates and associated generators defined within the [`plonky2`] crate. +//! +//! ```ignore +//! let prover_state = AllRecursiveCircuits::::new(...); +//! +//! // Default serializers +//! let gate_serializer = DefaultGateSerializer; +//! let generator_serializer = DefaultGeneratorSerializer:: { +//! _phantom: PhantomData::, +//! }; +//! +//! // Serialize the prover state to a sequence of bytes +//! let bytes = prover_state.to_bytes(false, &gate_serializer, &generator_serializer).unwrap(); +//! +//! // Deserialize the bytes into a prover state +//! let recovered_prover_state = AllRecursiveCircuits::::from_bytes( +//! &all_circuits_bytes, +//! false, +//! &gate_serializer, +//! &generator_serializer, +//! ).unwrap(); +//! +//! assert_eq!(prover_state, recovered_prover_state); +//! ``` +//! +//! Note that an entire prover state built with wide ranges may be particularly large (up to ~25 GB), hence serialization methods, +//! while faster than doing another preprocessing, may take some non-negligible time. + +#![cfg_attr(docsrs, feature(doc_cfg))] #![allow(clippy::needless_range_loop)] #![allow(clippy::too_many_arguments)] #![allow(clippy::field_reassign_with_default)] @@ -43,4 +204,11 @@ use jemallocator::Jemalloc; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; +// Public definitions and re-exports + pub type Node = eth_trie_utils::partial_trie::Node; + +pub use all_stark::AllStark; +pub use config::StarkConfig; +pub use fixed_recursive_verifier::AllRecursiveCircuits; +pub use generation::GenerationInputs; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 5904b8a9a8..ff4e7637cc 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -83,7 +83,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { { let gate_serializer = DefaultGateSerializer; - let generator_serializer = DefaultGeneratorSerializer { + let generator_serializer = DefaultGeneratorSerializer:: { _phantom: PhantomData::, }; diff --git a/field/src/goldilocks_field.rs b/field/src/goldilocks_field.rs index 36c6aad24f..4e459c9082 100644 --- a/field/src/goldilocks_field.rs +++ b/field/src/goldilocks_field.rs @@ -104,7 +104,7 @@ impl Field for GoldilocksField { /// Therefore $a^(p-2) = a^-1 (mod p)$ /// /// The following code has been adapted from winterfell/math/src/field/f64/mod.rs - /// located at https://github.com/facebook/winterfell. + /// located at . fn try_inverse(&self) -> Option { if self.is_zero() { return None; diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 100ae8513f..da434d61bd 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -15,6 +15,7 @@ mod validate_shape; pub mod verifier; pub mod witness_util; +/// A configuration for the FRI protocol. #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct FriConfig { /// `rate = 2^{-rate_bits}`. @@ -23,8 +24,10 @@ pub struct FriConfig { /// Height of Merkle tree caps. pub cap_height: usize, + /// Number of bits used for grinding. pub proof_of_work_bits: u32, + /// The reduction strategy to be applied at each layer during the commit phase. pub reduction_strategy: FriReductionStrategy, /// Number of query rounds to perform. From 77f510950800ef241fa5be7b45bde502087ec8c8 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Mon, 8 Jan 2024 14:34:26 +0100 Subject: [PATCH 077/175] Adress reviewer comments --- evm/src/cpu/kernel/constants/mod.rs | 2 +- evm/src/cpu/kernel/tests/account_code.rs | 64 +++++++----------------- evm/src/cpu/kernel/tests/add11.rs | 11 +--- 3 files changed, 22 insertions(+), 55 deletions(-) diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index fa25fa8704..de4dfd4bcc 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -94,7 +94,7 @@ const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ hex!("0000000000000000000000000000000100000000000000000000000000000000"), ), // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is - // equal to usize::MAX so that all rlp pointers all much smalled than that + // equal to u32::MAX so that all rlp pointers are much smaller than that ( "ENCODED_EMPTY_NODE_POS", hex!("00000000000000000000000000000000000000000000000000000000FFFFFFFF"), diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index f985ebd445..20c98bf976 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -201,36 +201,21 @@ fn test_extcodecopy() -> Result<()> { prepare_interpreter(&mut interpreter, address, &account)?; let context = interpreter.context(); - interpreter.generation_state.memory.set( - MemoryAddress { - context, - segment: Segment::ContextMetadata as usize, - virt: GasLimit as usize, - }, - U256::from(1000000000000u64), - ); + interpreter.generation_state.memory.contexts[context].segments + [Segment::ContextMetadata as usize] + .set(GasLimit as usize, U256::from(1000000000000u64)); let extcodecopy = KERNEL.global_labels["sys_extcodecopy"]; // Put random data in main memory and the `KernelAccountCode` segment for realism. let mut rng = thread_rng(); for i in 0..2000 { - interpreter.generation_state.memory.set( - MemoryAddress { - context, - segment: Segment::MainMemory as usize, - virt: i, - }, - U256::from(rng.gen::()), - ); - interpreter.generation_state.memory.set( - MemoryAddress { - context, - segment: Segment::KernelAccountCode as usize, - virt: i, - }, - U256::from(rng.gen::()), - ); + interpreter.generation_state.memory.contexts[context].segments + [Segment::MainMemory as usize] + .set(i, U256::from(rng.gen::())); + interpreter.generation_state.memory.contexts[context].segments + [Segment::KernelAccountCode as usize] + .set(i, U256::from(rng.gen::())); } // Random inputs @@ -265,11 +250,9 @@ fn test_extcodecopy() -> Result<()> { assert!(interpreter.stack().is_empty()); // Check that the code was correctly copied to memory. for i in 0..size { - let memory = interpreter.generation_state.memory.get(MemoryAddress { - context, - segment: Segment::MainMemory as usize, - virt: dest_offset + i, - }); + let memory = interpreter.generation_state.memory.contexts[context].segments + [Segment::MainMemory as usize] + .get(dest_offset + i); assert_eq!( memory, code.get(offset + i).copied().unwrap_or_default().into() @@ -294,22 +277,13 @@ fn prepare_interpreter_all_accounts( // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = 0; interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.set( - MemoryAddress { - context: 1, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::Address as usize, - }, - U256::from_big_endian(&addr), - ); - interpreter.generation_state.memory.set( - MemoryAddress { - context: 1, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, - }, - 100_000.into(), - ); + interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] + .set( + ContextMetadata::Address as usize, + U256::from_big_endian(&addr), + ); + interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] + .set(ContextMetadata::GasLimit as usize, 100_000.into()); interpreter.set_context(1); interpreter.set_is_kernel(false); interpreter.generation_state.memory.set( diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index b85bfe66c0..9ba65db2c9 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -19,7 +19,6 @@ use crate::generation::TrieInputs; use crate::memory::segments::Segment; use crate::proof::TrieRoots; use crate::util::h2u; -use crate::witness::memory::MemoryAddress; // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by loading the initial MPTs and @@ -200,14 +199,8 @@ fn test_add11_yml() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.set( - MemoryAddress { - context: 0, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, - }, - 1_000_000.into(), - ); + interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] + .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } From 82804e4201f675fda91119414c706d07e4ecd4fe Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 8 Jan 2024 15:14:26 +0100 Subject: [PATCH 078/175] Add some more + module doc --- plonky2/src/fri/mod.rs | 3 +++ plonky2/src/gadgets/mod.rs | 3 ++- plonky2/src/gates/mod.rs | 22 +++++++++++++++++++ plonky2/src/hash/mod.rs | 3 +++ plonky2/src/lib.rs | 1 + plonky2/src/plonk/circuit_builder.rs | 4 ++-- plonky2/src/plonk/mod.rs | 5 +++++ plonky2/src/recursion/mod.rs | 6 +++++ plonky2/src/util/mod.rs | 2 ++ .../util/serialization/gate_serialization.rs | 2 +- .../serialization/generator_serialization.rs | 2 +- 11 files changed, 48 insertions(+), 5 deletions(-) diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 100ae8513f..11d469f42a 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -1,3 +1,6 @@ +//! Fast Reed-Solomon IOP (FRI) protocol and its circuit version +//! of the FRI verifier for recursive proof composition. + use alloc::vec::Vec; use serde::Serialize; diff --git a/plonky2/src/gadgets/mod.rs b/plonky2/src/gadgets/mod.rs index ba19e667f8..cc14a83550 100644 --- a/plonky2/src/gadgets/mod.rs +++ b/plonky2/src/gadgets/mod.rs @@ -1,4 +1,5 @@ -//! Gadgets provide additional methods to [`CircuitBuilder`] +//! Helper gadgets providing additional methods to +//! [CircuitBuilder](crate::plonk::circuit_builder::CircuitBuilder), //! to ease circuit creation. pub mod arithmetic; diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index 432f026470..446f1aeda2 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -1,3 +1,25 @@ +//! plonky2 custom gates. +//! +//! Vanilla Plonk arithmetization only supports basic fan-in 2 / fan-out 1 arithmetic gates, +//! each of the form +//! +//! $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$ +//! +//! where: +//! - q_M, q_L, q_R and q_O are boolean selectors, +//! - a, b and c are values used as inputs and output respectively, +//! - q_C is a constant (possibly 0). +//! +//! This allows expressing simple operations like multiplication, addition, etc. For +//! instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. +//! Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. +//! +//! However, such gate is fairly limited for more complex computations. Hence, when a computation may +//! require too many of these "vanilla" gates, or when a computation arises often within the same circuit, +//! one may want to construct a tailored custom gate. These custom gates can use more selectors and are +//! not necessarily limited to 2 inputs + 1 output = 3 wires. +//! For instance, plonky2 supports natively a custom Poseidon hash gate that uses 135 wires. + // Gates have `new` methods that return `GateRef`s. pub mod arithmetic_base; diff --git a/plonky2/src/hash/mod.rs b/plonky2/src/hash/mod.rs index b829392063..c98c57069c 100644 --- a/plonky2/src/hash/mod.rs +++ b/plonky2/src/hash/mod.rs @@ -1,3 +1,6 @@ +//! plonky2 hashing logic for in-circuit hashing and Merkle proof verification +//! as well as specific hash functions implementation. + mod arch; pub mod hash_types; pub mod hashing; diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index b02fc21bfb..44bc2cf638 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -4,6 +4,7 @@ pub extern crate alloc; +/// Re-export of `plonky2_field`. #[doc(inline)] pub use plonky2_field as field; diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index c0e0d870d1..4a1eee90d7 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -136,7 +136,7 @@ pub struct LookupWire { /// assert!(circuit_data.verify(proof).is_ok()); /// ``` pub struct CircuitBuilder, const D: usize> { - /// Circuit configuration to be used by this `CircuitBuilder`. + /// Circuit configuration to be used by this [`CircuitBuilder`]. pub config: CircuitConfig, /// A domain separator, which is included in the initial Fiat-Shamir seed. This is generally not @@ -179,7 +179,7 @@ pub struct CircuitBuilder, const D: usize> { /// List of constant generators used to fill the constant wires. constant_generators: Vec>, - /// Rows for each LUT: LookupWire contains: first `LookupGate`, first `LookupTableGate`, last `LookupTableGate`. + /// Rows for each LUT: LookupWire contains: first `LookupGate`, first [`LookupTableGate`], last `LookupTableGate`. lookup_rows: Vec, /// For each LUT index, vector of `(looking_in, looking_out)` pairs. diff --git a/plonky2/src/plonk/mod.rs b/plonky2/src/plonk/mod.rs index 604c1f7992..565b1c57c2 100644 --- a/plonky2/src/plonk/mod.rs +++ b/plonky2/src/plonk/mod.rs @@ -1,3 +1,8 @@ +//! plonky2 proving system. +//! +//! This module also defines the [CircuitBuilder](circuit_builder::CircuitBuilder) +//! structure, used to build custom plonky2 circuits satisfying arbitrary statements. + pub mod circuit_builder; pub mod circuit_data; pub mod config; diff --git a/plonky2/src/recursion/mod.rs b/plonky2/src/recursion/mod.rs index 0e9cd2ccb3..438f600763 100644 --- a/plonky2/src/recursion/mod.rs +++ b/plonky2/src/recursion/mod.rs @@ -1,3 +1,9 @@ +//! Recursion logic for verifying recursively plonky2 circuits. +//! +//! This module also provides ways to perform conditional recursive verification +//! (between two different circuits, depending on a condition), and cyclic +//! recursion where a circuit implements its own verification logic. + pub mod conditional_recursive_verifier; pub mod cyclic_recursion; pub mod dummy_circuit; diff --git a/plonky2/src/util/mod.rs b/plonky2/src/util/mod.rs index fc2c4f0932..e0f71f1272 100644 --- a/plonky2/src/util/mod.rs +++ b/plonky2/src/util/mod.rs @@ -1,3 +1,5 @@ +//! Utility module for helper methods and plonky2 serialization logic. + use alloc::vec::Vec; use plonky2_maybe_rayon::*; diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index d858a05764..c5763fb0bf 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -59,7 +59,7 @@ macro_rules! get_gate_tag_impl { } #[macro_export] -/// Macro implementing the `GateSerializer` trait. +/// Macro implementing the [`GateSerializer`] trait. /// To serialize a list of gates used for a circuit, /// this macro should be called with a struct on which to implement /// this as first argument, followed by all the targeted gates. diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index 19a22a77c3..bad24cebf2 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -63,7 +63,7 @@ macro_rules! get_generator_tag_impl { } #[macro_export] -/// Macro implementing the `WitnessGeneratorSerializer` trait. +/// Macro implementing the [`WitnessGeneratorSerializer`] trait. /// To serialize a list of generators used for a circuit, /// this macro should be called with a struct on which to implement /// this as first argument, followed by all the targeted generators. From dcbfef6df9750f39d00cf7466743d810926d5665 Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:33:17 +0800 Subject: [PATCH 079/175] chore: fix typos (#1451) --- evm/src/extension_tower.rs | 2 +- evm/src/keccak_sponge/columns.rs | 2 +- evm/src/memory/segments.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index 4e5f54e67b..da7cca554e 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1068,7 +1068,7 @@ where /// (Prod_{i=1}^11 x_i) / phi /// The 6th Frob map is nontrivial but leaves Fp6 fixed and hence must be the conjugate: /// x_6 = (a + bz)_6 = a - bz = x.conj() - /// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expresed as: + /// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expressed as: /// [(prod_17) * (prod_17)_2] * (prod_17)_4 * [(prod_17) * (prod_17)_2]_1 /// By Galois theory, both the following are in Fp2 and are complex conjugates /// prod_odds, prod_evens diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs index 7ba0cf4559..bcd34b9c7f 100644 --- a/evm/src/keccak_sponge/columns.rs +++ b/evm/src/keccak_sponge/columns.rs @@ -32,7 +32,7 @@ pub(crate) struct KeccakSpongeColumnsView { /// not a padding byte; 0 otherwise. pub is_full_input_block: T, - /// The context of the base addresss at which we will read the input block. + /// The context of the base address at which we will read the input block. pub context: T, /// The segment of the base address at which we will read the input block. pub segment: T, diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index d38b2e5deb..f3de1a215c 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -3,7 +3,7 @@ use num::traits::AsPrimitive; pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; /// This contains all the existing memory segments. The values in the enum are shifted by 32 bits -/// to allow for convenient address components (context / segement / virtual) bundling in the kernel. +/// to allow for convenient address components (context / segment / virtual) bundling in the kernel. #[allow(dead_code)] #[allow(clippy::enum_clike_unportable_variant)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] From f4be34dc6d2bb7c336ed237304728dad067ec6c8 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 9 Jan 2024 10:59:00 +0100 Subject: [PATCH 080/175] Some more --- plonky2/src/gadgets/arithmetic.rs | 16 ++++++++++------ plonky2/src/plonk/circuit_builder.rs | 2 ++ plonky2/src/plonk/circuit_data.rs | 14 ++++++++++++++ plonky2/src/plonk/config.rs | 8 ++++++++ plonky2/src/plonk/plonk_common.rs | 2 ++ plonky2/src/plonk/proof.rs | 6 ++++++ plonky2/src/plonk/prover.rs | 2 ++ plonky2/src/plonk/vars.rs | 2 ++ plonky2/src/plonk/verifier.rs | 2 ++ 9 files changed, 48 insertions(+), 6 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index e3af60e9c5..9982628e02 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -191,7 +191,7 @@ impl, const D: usize> CircuitBuilder { self.arithmetic(F::ONE, F::ONE, x, one, y) } - /// Add `n` `Target`s. + /// Adds `n` `Target`s. pub fn add_many(&mut self, terms: impl IntoIterator) -> Target where T: Borrow, @@ -224,7 +224,7 @@ impl, const D: usize> CircuitBuilder { .fold(self.one(), |acc, t| self.mul(acc, *t.borrow())) } - /// Exponentiate `base` to the power of `2^power_log`. + /// Exponentiates `base` to the power of `2^power_log`. pub fn exp_power_of_2(&mut self, base: Target, power_log: usize) -> Target { if power_log > self.num_base_arithmetic_ops_per_gate() { // Cheaper to just use `ExponentiateGate`. @@ -239,7 +239,7 @@ impl, const D: usize> CircuitBuilder { } // TODO: Test - /// Exponentiate `base` to the power of `exponent`, given by its little-endian bits. + /// Exponentiates `base` to the power of `exponent`, given by its little-endian bits. pub fn exp_from_bits( &mut self, base: Target, @@ -264,7 +264,7 @@ impl, const D: usize> CircuitBuilder { } // TODO: Test - /// Exponentiate `base` to the power of `exponent`, where `exponent < 2^num_bits`. + /// Exponentiates `base` to the power of `exponent`, where `exponent < 2^num_bits`. pub fn exp(&mut self, base: Target, exponent: Target, num_bits: usize) -> Target { let exponent_bits = self.split_le(exponent, num_bits); @@ -303,7 +303,7 @@ impl, const D: usize> CircuitBuilder { product } - /// Exponentiate `base` to the power of a known `exponent`. + /// Exponentiates `base` to the power of a known `exponent`. // TODO: Test pub fn exp_u64(&mut self, base: Target, mut exponent: u64) -> Target { let mut exp_bits = Vec::new(); @@ -330,28 +330,32 @@ impl, const D: usize> CircuitBuilder { self.inverse_extension(x_ext).0[0] } + /// Computes the logical NOT of the provided [`BoolTarget`]. pub fn not(&mut self, b: BoolTarget) -> BoolTarget { let one = self.one(); let res = self.sub(one, b.target); BoolTarget::new_unsafe(res) } + /// Computes the logical AND of the provided [`BoolTarget`]s. pub fn and(&mut self, b1: BoolTarget, b2: BoolTarget) -> BoolTarget { BoolTarget::new_unsafe(self.mul(b1.target, b2.target)) } - /// computes the arithmetic extension of logical "or": `b1 + b2 - b1 * b2` + /// Computes the logical OR through the arithmetic expression: `b1 + b2 - b1 * b2`. pub fn or(&mut self, b1: BoolTarget, b2: BoolTarget) -> BoolTarget { let res_minus_b2 = self.arithmetic(-F::ONE, F::ONE, b1.target, b2.target, b1.target); BoolTarget::new_unsafe(self.add(res_minus_b2, b2.target)) } + /// Outputs `x` if `b` is true, and else `y`, through the formula: `b*x + (1-b)*y`. pub fn _if(&mut self, b: BoolTarget, x: Target, y: Target) -> Target { let not_b = self.not(b); let maybe_x = self.mul(b.target, x); self.mul_add(not_b.target, y, maybe_x) } + /// Checks whether `x` and `y` are equal and outputs the boolean result. pub fn is_equal(&mut self, x: Target, y: Target) -> BoolTarget { let zero = self.zero(); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 4a1eee90d7..be905cc273 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1,3 +1,5 @@ +//! Logic for building plonky2 circuits. + use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec; diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index c0c6022093..4a2efe060c 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -1,3 +1,17 @@ +//! Circuit data specific to the prover and the verifier. +//! +//! This module also defines a [`CircuitConfig`] to be customized +//! when building circuits for arbitrary statements. +//! +//! After building a circuit, one obtains an instance of [`CircuitData`]. +//! This contains both prover and verifier data, allowing to generate +//! proofs for the given circuit and verify them. +//! +//! Most of the [`CircuitData`] is actually prover-specific, and can be +//! extracted by calling [`CircuitData::prover_data`] method. +//! The verifier data can similarly be extracted by calling [`CircuitData::verifier_data`]. +//! This is useful to allow even small devices to verify plonky2 proofs. + use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index 2391ef6cef..c4e73356b3 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -1,3 +1,11 @@ +//! Hashing configuration to be used when building a circuit. +//! +//! This module defines a [`Hasher`] trait as well as its recursive +//! counterpart [`AlgebraicHasher`] for in-circuit hashing. It also +//! provides concrete configurations, one fully recursive leveraging +//! Poseidon hash function both internally and natively, and one mixing +//! Poseidon internally and truncated Keccak externally. + use alloc::vec; use alloc::vec::Vec; use core::fmt::Debug; diff --git a/plonky2/src/plonk/plonk_common.rs b/plonky2/src/plonk/plonk_common.rs index f064cbac50..ca8ea9196a 100644 --- a/plonky2/src/plonk/plonk_common.rs +++ b/plonky2/src/plonk/plonk_common.rs @@ -1,3 +1,5 @@ +//! Utility methods and constants for Plonk. + use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index bd93523397..c010414e25 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -1,3 +1,9 @@ +//! plonky2 proof definition. +//! +//! Proofs can be later compressed to reduce their size, into either +//! [`CompressedProof`] or [`CompressedProofWithPublicInputs`] formats. +//! The latter can be directly passed to a verifier to assert its correctness. + use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 41aebdb1e9..e0c8d727ff 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -1,3 +1,5 @@ +//! plonky2 prover implementation. + use alloc::vec::Vec; use alloc::{format, vec}; use core::cmp::min; diff --git a/plonky2/src/plonk/vars.rs b/plonky2/src/plonk/vars.rs index 727f76514e..b9d6d790ff 100644 --- a/plonky2/src/plonk/vars.rs +++ b/plonky2/src/plonk/vars.rs @@ -1,3 +1,5 @@ +//! Logic for evaluating constraints. + use core::ops::Range; use crate::field::extension::algebra::ExtensionAlgebra; diff --git a/plonky2/src/plonk/verifier.rs b/plonky2/src/plonk/verifier.rs index b160fddc28..fa1bc14b84 100644 --- a/plonky2/src/plonk/verifier.rs +++ b/plonky2/src/plonk/verifier.rs @@ -1,3 +1,5 @@ +//! plonky2 verifier implementation. + use anyhow::{ensure, Result}; use crate::field::extension::Extendable; From bd02117cdcf56ca09c4ef9bd3978ca3b7073616e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Jan 2024 11:15:53 +0100 Subject: [PATCH 081/175] Fix `after_mpt_delete_extension_branch` (#1449) * Fix after_mpt_delete_extension_branch * Rename test * PR feedback --- .../asm/mpt/delete/delete_extension.asm | 14 ++------- evm/src/cpu/kernel/tests/mpt/delete.rs | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm b/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm index 149b971d76..0627fcba6a 100644 --- a/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm +++ b/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm @@ -37,18 +37,10 @@ after_mpt_delete_extension_branch: // stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest POP // stack: updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - SWAP1 - // stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest - PUSH @MPT_NODE_EXTENSION DUP2 %mstore_trie_data - // stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest - DUP3 DUP2 %mstore_trie_data // Append node_len to our node - // stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest - DUP4 DUP2 %mstore_trie_data // Append node_key to our node - // stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest - SWAP1 DUP2 %mstore_trie_data // Append updated_child_node_ptr to our node - // stack: extension_ptr, node_len, node_key, retdest + DUP2 %add_const(2) %mstore_trie_data + // stack: node_payload_ptr, node_len, node_key, retdest + %decrement %stack (extension_ptr, node_len, node_key, retdest) -> (retdest, extension_ptr) - // stack: extension_ptr, retdest JUMP after_mpt_delete_extension_extension: diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs index acb75b7b21..38342dc99c 100644 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm/src/cpu/kernel/tests/mpt/delete.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Result}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{BigEndianHash, H256}; +use ethereum_types::{BigEndianHash, H256, U512}; +use rand::random; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -48,6 +49,32 @@ fn mpt_delete_branch_into_hash() -> Result<()> { test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) } +#[test] +fn test_after_mpt_delete_extension_branch() -> Result<()> { + let hash = Node::Hash(H256::random()); + let branch = Node::Branch { + children: std::array::from_fn(|i| { + if i == 0 { + Node::Empty.into() + } else { + hash.clone().into() + } + }), + value: vec![], + }; + let nibbles = Nibbles::from_bytes_be(&random::<[u8; 5]>()).unwrap(); + let state_trie = Node::Extension { + nibbles, + child: branch.into(), + } + .into(); + let key = nibbles.merge_nibbles(&Nibbles { + packed: U512::zero(), + count: 64 - nibbles.count, + }); + test_state_trie(state_trie, key, test_account_2()) +} + /// Note: The account's storage_root is ignored, as we can't insert a new storage_root without the /// accompanying trie data. An empty trie's storage_root is used instead. fn test_state_trie( From 5b71eb4ee0f96d8b563222b4c9c3013fe7e647d3 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Tue, 9 Jan 2024 11:48:30 +0100 Subject: [PATCH 082/175] Address review comments --- evm/src/cpu/kernel/constants/mod.rs | 2 +- evm/src/cpu/kernel/interpreter.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 3386865a67..f6f021db76 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -97,7 +97,7 @@ const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ hex!("0000000000000000000000000000000100000000000000000000000000000000"), ), // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is - // equal to u32::MAX + @SEGMENT_RLP_RAW so that all rlp pointers are much smaller than that + // equal to u32::MAX + @SEGMENT_RLP_RAW so that all rlp pointers are much smaller than that. ( "ENCODED_EMPTY_NODE_POS", hex!("0000000000000000000000000000000000000000000000000000000CFFFFFFFF"), diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index b85ac1761b..b99ccaebc5 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1196,7 +1196,7 @@ impl<'a> Interpreter<'a> { self.generation_state.registers.context = context; } - /// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS + /// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS. pub(crate) fn initialize_rlp_segment(&mut self) { self.generation_state.memory.set( MemoryAddress::new(0, Segment::RlpRaw, 0xFFFFFFFF), From 54a13135884e0da7c1d1787a3511a16d3518f0ba Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:25:31 +0100 Subject: [PATCH 083/175] Improve some calls to `%mstore_rlp` (#1452) * Improve some calls to mstore_rlp * Remove comment --- evm/src/cpu/kernel/asm/memory/core.asm | 3 +- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 12 ++-- .../asm/mpt/hash/hash_trie_specific.asm | 4 +- evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 12 ++-- evm/src/cpu/kernel/asm/mpt/util.asm | 4 +- evm/src/cpu/kernel/asm/rlp/encode.asm | 72 +++++++++---------- 6 files changed, 50 insertions(+), 57 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index 3a3a17a500..eef7ee1a49 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -409,8 +409,7 @@ %mstore_u32 %endmacro -// Store a single byte to @SEGMENT_RLP_RAW. -%macro mstore_rlp +%macro swap_mstore // stack: addr, value SWAP1 MSTORE_GENERAL diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 29348f9707..15fdd6a967 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -151,8 +151,8 @@ global encode_node_branch: // No value; append the empty string (0x80). // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start) - %mstore_rlp + %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (0x80, rlp_pos, rlp_pos, rlp_start) + MSTORE_GENERAL // stack: rlp_pos', rlp_start, cur_len, retdest %increment // stack: rlp_pos'', rlp_start, cur_len, retdest @@ -192,9 +192,9 @@ encode_node_branch_prepend_prefix: SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + DUP4 // rlp_pos PUSH 160 - DUP5 // rlp_pos - %mstore_rlp + MSTORE_GENERAL SWAP3 %increment SWAP3 // rlp_pos += 1 %%unpack: %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) @@ -233,9 +233,9 @@ encode_node_extension_after_hex_prefix: // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. DUP4 %sub_const(32) %jumpi(encode_node_extension_unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. + DUP1 // rlp_pos PUSH 160 - DUP2 // rlp_pos - %mstore_rlp + MSTORE_GENERAL %increment // rlp_pos += 1 encode_node_extension_unpack: %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 6abca0b687..cd07c01fdc 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -326,8 +326,8 @@ encode_nonzero_receipt_type: // stack: rlp_receipt_len, txn_type, rlp_addr, value_ptr, retdest DUP3 %encode_rlp_multi_byte_string_prefix // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest - DUP2 DUP2 - %mstore_rlp + DUP1 DUP3 + MSTORE_GENERAL %increment // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest %stack (rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest) -> (rlp_addr, value_ptr, retdest) diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index 7dd02c34f9..cee87deb0a 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -27,7 +27,7 @@ first_byte: // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // get the first nibble, if num_nibbles is odd, or zero otherwise SWAP2 - // stack: packed_nibbles, num_nibbbles, rlp_addr, terminated, retdest + // stack: packed_nibbles, num_nibbles, rlp_addr, terminated, retdest DUP2 DUP1 %mod_const(2) // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_addr, terminated, retdest @@ -50,7 +50,7 @@ first_byte: ADD // stack: first_byte, rlp_addr, retdest DUP2 - %mstore_rlp + %swap_mstore %increment // stack: rlp_addr', retdest SWAP1 @@ -88,7 +88,7 @@ rlp_header_medium: // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest %add_const(0x80) // value = 0x80 + hp_len DUP2 - %mstore_rlp + %swap_mstore // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // rlp_addr += 1 %increment @@ -108,14 +108,14 @@ rlp_header_large: // In practice hex-prefix length will never exceed 256, so the length of the // length will always be 1 byte in this case. + DUP2 // rlp_addr PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8 - DUP3 - %mstore_rlp + MSTORE_GENERAL // stack: rlp_addr, value, hp_len, i, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest DUP2 %increment - %mstore_rlp + %swap_mstore // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // rlp_addr += 2 diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index 27418b1704..db13d8890d 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -11,9 +11,9 @@ %endmacro %macro initialize_rlp_segment - PUSH 0x80 PUSH @ENCODED_EMPTY_NODE_POS - %mstore_rlp + PUSH 0x80 + MSTORE_GENERAL %endmacro %macro alloc_rlp_block diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 2319e780b9..ccaa9f616c 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -29,12 +29,11 @@ global encode_rlp_256: // RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). global encode_rlp_fixed: // stack: len, rlp_addr, string, retdest - DUP1 + DUP2 + DUP2 %add_const(0x80) - // stack: first_byte, len, rlp_addr, string, retdest - DUP3 - // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest - %mstore_rlp + // stack: first_byte, rlp_addr, len, rlp_addr, string, retdest + MSTORE_GENERAL // stack: len, rlp_addr, string, retdest SWAP1 %increment // increment rlp_addr @@ -51,19 +50,17 @@ encode_rlp_fixed_finish: // I.e. writes encode(encode(string). Assumes string < 2^(8 * len). global doubly_encode_rlp_fixed: // stack: len, rlp_addr, string, retdest - DUP1 + DUP2 + DUP2 %add_const(0x81) - // stack: first_byte, len, rlp_addr, string, retdest - DUP3 - // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest - %mstore_rlp + // stack: first_byte, rlp_addr, len, rlp_addr, string, retdest + MSTORE_GENERAL // stack: len, rlp_addr, string, retdest - DUP1 + DUP2 %increment + DUP2 %add_const(0x80) - // stack: second_byte, len, original_rlp_addr, string, retdest - DUP3 %increment - // stack: rlp_addr', second_byte, len, rlp_addr, string, retdest - %mstore_rlp + // stack: second_byte, rlp_addr', len, original_rlp_addr, string, retdest + MSTORE_GENERAL // stack: len, rlp_addr, string, retdest SWAP1 %add_const(2) // advance past the two prefix bytes @@ -87,11 +84,10 @@ global encode_rlp_multi_byte_string_prefix: %jumpi(encode_rlp_multi_byte_string_prefix_large) // Medium case; prefix is 0x80 + str_len. // stack: rlp_addr, str_len, retdest - SWAP1 %add_const(0x80) - // stack: prefix, rlp_addr, retdest - DUP2 - // stack: rlp_addr, prefix, rlp_addr, retdest - %mstore_rlp + DUP1 + SWAP2 %add_const(0x80) + // stack: prefix, rlp_addr, rlp_addr, retdest + MSTORE_GENERAL // stack: rlp_addr, retdest %increment // stack: rlp_addr', retdest @@ -104,12 +100,11 @@ encode_rlp_multi_byte_string_prefix_large: %num_bytes // stack: len_of_len, rlp_addr, str_len, retdest SWAP1 - DUP2 // len_of_len + DUP1 // rlp_addr + DUP3 // len_of_len %add_const(0xb7) - // stack: first_byte, rlp_addr, len_of_len, str_len, retdest - DUP2 - // stack: rlp_addr, first_byte, rlp_addr, len_of_len, str_len, retdest - %mstore_rlp + // stack: first_byte, rlp_addr, rlp_addr, len_of_len, str_len, retdest + MSTORE_GENERAL // stack: rlp_addr, len_of_len, str_len, retdest %increment // stack: rlp_addr', len_of_len, str_len, retdest @@ -132,12 +127,11 @@ global encode_rlp_list_prefix: %jumpi(encode_rlp_list_prefix_large) // Small case: prefix is just 0xc0 + length. // stack: rlp_addr, payload_len, retdest - SWAP1 + DUP1 + SWAP2 %add_const(0xc0) - // stack: prefix, rlp_addr, retdest - DUP2 - // stack: rlp_addr, prefix, rlp_addr, retdest - %mstore_rlp + // stack: prefix, rlp_addr, rlp_addr, retdest + MSTORE_GENERAL // stack: rlp_addr, retdest %increment SWAP1 @@ -147,10 +141,10 @@ encode_rlp_list_prefix_large: // stack: rlp_addr, payload_len, retdest DUP2 %num_bytes // stack: len_of_len, rlp_addr, payload_len, retdest - DUP1 %add_const(0xf7) - // stack: first_byte, len_of_len, rlp_addr, payload_len, retdest - DUP3 // rlp_addr - %mstore_rlp + DUP2 + DUP2 %add_const(0xf7) + // stack: first_byte, rlp_addr, len_of_len, rlp_addr, payload_len, retdest + MSTORE_GENERAL // stack: len_of_len, rlp_addr, payload_len, retdest SWAP1 %increment // stack: rlp_addr', len_of_len, payload_len, retdest @@ -184,10 +178,10 @@ global prepend_rlp_list_prefix: // If we got here, we have a small list, so we prepend 0xc0 + len at rlp_address 8. // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 %add_const(0xc0) - // stack: prefix_byte, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP4 %decrement // offset of prefix - %mstore_rlp + DUP3 %decrement // offset of prefix + DUP2 %add_const(0xc0) + // stack: prefix_byte, start_rlp_addr-1, payload_len, end_rlp_addr, start_rlp_addr, retdest + MSTORE_GENERAL // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest %increment // stack: rlp_len, end_rlp_addr, start_rlp_addr, retdest @@ -207,7 +201,7 @@ prepend_rlp_list_prefix_big: DUP5 %decrement // start_rlp_addr - 1 SUB // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len + DUP2 %add_const(0xf7) DUP2 %swap_mstore // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %increment // start_len_rlp_addr = prefix_start_rlp_addr + 1 %stack (start_len_rlp_addr, prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest) From c8430dac39915494c90bd9d1063aaf00a8602640 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:35:31 +0100 Subject: [PATCH 084/175] Add Boolean constraints for `ArithmeticStark` (#1453) * Constrain IS_READ to be boolean * Constrain arithmetic flags to be boolean * Comment on memory stark --- evm/src/arithmetic/arithmetic_stark.rs | 15 ++++++++++++++- evm/src/arithmetic/columns.rs | 4 ++++ evm/src/memory/memory_stark.rs | 8 ++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 52fc2f7893..1d3af2b9af 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -11,7 +11,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::util::transpose; use static_assertions::const_assert; -use super::columns::NUM_ARITH_COLUMNS; +use super::columns::{op_flags, NUM_ARITH_COLUMNS}; use super::shift; use crate::all_stark::Table; use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; @@ -208,6 +208,12 @@ impl, const D: usize> Stark for ArithmeticSta let lv: &[P; NUM_ARITH_COLUMNS] = vars.get_local_values().try_into().unwrap(); let nv: &[P; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); + // Flags must be boolean. + for flag_idx in op_flags() { + let flag = lv[flag_idx]; + yield_constr.constraint(flag * (flag - P::ONES)); + } + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. let opcode_constraint = (P::ONES - lv[columns::IS_RANGE_CHECK]) * lv[columns::OPCODE_COL]; yield_constr.constraint(opcode_constraint); @@ -248,6 +254,13 @@ impl, const D: usize> Stark for ArithmeticSta let nv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); + // Flags must be boolean. + for flag_idx in op_flags() { + let flag = lv[flag_idx]; + let constraint = builder.mul_sub_extension(flag, flag, flag); + yield_constr.constraint(builder, constraint); + } + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. let opcode_constraint = builder.arithmetic_extension( F::NEG_ONE, diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs index bcfb29c177..dc535492cd 100644 --- a/evm/src/arithmetic/columns.rs +++ b/evm/src/arithmetic/columns.rs @@ -43,6 +43,10 @@ pub(crate) const IS_RANGE_CHECK: usize = IS_SHR + 1; pub(crate) const OPCODE_COL: usize = IS_RANGE_CHECK + 1; pub(crate) const START_SHARED_COLS: usize = OPCODE_COL + 1; +pub(crate) const fn op_flags() -> Range { + IS_ADD..IS_RANGE_CHECK + 1 +} + /// Within the Arithmetic Unit, there are shared columns which can be /// used by any arithmetic circuit, depending on which one is active /// this cycle. diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index e596f421ea..1283965b18 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -305,6 +305,10 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Date: Wed, 10 Jan 2024 08:54:13 +0100 Subject: [PATCH 085/175] Implement CTL bundling (#1439) * Implement CTL bundling * Cleanup * Clippy * Preallocate memory * Apply comments and remove unnecessary functions. * Start removing clones * Set columns and filters inside if condition. * Remove extra CTL helper columns * Add circuit version, with cleanup and fixes * Remove some overhead * Use refs * Pacify clippy --------- Co-authored-by: Robin Salen --- evm/src/all_stark.rs | 1 + evm/src/cpu/memio.rs | 2 +- evm/src/cross_table_lookup.rs | 872 +++++++++++++++++++++++++++------ evm/src/keccak/keccak_stark.rs | 3 +- evm/src/lookup.rs | 176 +++---- evm/src/proof.rs | 12 +- evm/src/prover.rs | 83 +++- evm/src/recursive_verifier.rs | 53 +- evm/src/stark.rs | 14 +- evm/src/vanishing_poly.rs | 15 +- evm/src/verifier.rs | 27 +- 11 files changed, 950 insertions(+), 308 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 74f211d6d9..a2996eb47b 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -1,5 +1,6 @@ use std::iter; +use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 2073e182e1..7cb4c38855 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -204,7 +204,7 @@ fn eval_packed_store( } /// Circuit version of `eval_packed_store`. -/// /// Evaluates constraints for MSTORE_GENERAL. +/// Evaluates constraints for MSTORE_GENERAL. fn eval_ext_circuit_store, const D: usize>( builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, lv: &CpuColumnsView>, diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 21f94126d3..428279ba89 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -28,11 +28,14 @@ //! the current and next row values -- when computing the linear combinations. use std::borrow::Borrow; +use std::cmp::min; use std::fmt::Debug; use std::iter::repeat; use anyhow::{ensure, Result}; +use hashbrown::HashMap; use itertools::Itertools; +use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -46,6 +49,7 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::plonk::plonk_common::{ reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, }; +use plonky2::util::ceil_div_usize; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use crate::all_stark::{Table, NUM_TABLES}; @@ -466,39 +470,66 @@ impl CrossTableLookup { } } - /// Given a `Table` t and the number of challenges, returns the number of Cross-table lookup polynomials associated to t, - /// i.e. the number of looking and looked tables among all CTLs whose columns are taken from t. - pub(crate) fn num_ctl_zs(ctls: &[Self], table: Table, num_challenges: usize) -> usize { + /// Given a table, returns: + /// - the total number of helper columns for this table, over all Cross-table lookups, + /// - the total number of z polynomials for this table, over all Cross-table lookups, + /// - the number of helper columns for this table, for each Cross-table lookup. + pub(crate) fn num_ctl_helpers_zs_all( + ctls: &[Self], + table: Table, + num_challenges: usize, + constraint_degree: usize, + ) -> (usize, usize, Vec) { + let mut num_helpers = 0; let mut num_ctls = 0; - for ctl in ctls { + let mut num_helpers_by_ctl = vec![0; ctls.len()]; + for (i, ctl) in ctls.iter().enumerate() { let all_tables = std::iter::once(&ctl.looked_table).chain(&ctl.looking_tables); - num_ctls += all_tables.filter(|twc| twc.table == table).count(); + let num_appearances = all_tables.filter(|twc| twc.table == table).count(); + let is_helpers = num_appearances > 2; + if is_helpers { + num_helpers_by_ctl[i] = ceil_div_usize(num_appearances, constraint_degree - 1); + num_helpers += num_helpers_by_ctl[i]; + } + + if num_appearances > 0 { + num_ctls += 1; + } } - num_ctls * num_challenges + ( + num_helpers * num_challenges, + num_ctls * num_challenges, + num_helpers_by_ctl, + ) } } /// Cross-table lookup data for one table. #[derive(Clone, Default)] -pub(crate) struct CtlData { +pub(crate) struct CtlData<'a, F: Field> { /// Data associated with all Z(x) polynomials for one table. - pub(crate) zs_columns: Vec>, + pub(crate) zs_columns: Vec>, } /// Cross-table lookup data associated with one Z(x) polynomial. +/// One Z(x) polynomial can be associated to multiple tables, +/// built from the same STARK. #[derive(Clone)] -pub(crate) struct CtlZData { +pub(crate) struct CtlZData<'a, F: Field> { + /// Helper columns to verify the Z polynomial values. + pub(crate) helper_columns: Vec>, /// Z polynomial values. pub(crate) z: PolynomialValues, /// Cross-table lookup challenge. pub(crate) challenge: GrandProductChallenge, - /// Column linear combination for the current table. - pub(crate) columns: Vec>, - /// Filter column for the current table. It evaluates to either 1 or 0. - pub(crate) filter: Option>, + /// Vector of column linear combinations for the current tables. + pub(crate) columns: Vec<&'a [Column]>, + /// Vector of filter columns for the current table. + /// Each filter evaluates to either 1 or 0. + pub(crate) filter: Vec>>, } -impl CtlData { +impl<'a, F: Field> CtlData<'a, F> { /// Returns the number of cross-table lookup polynomials. pub(crate) fn len(&self) -> usize { self.zs_columns.len() @@ -509,12 +540,38 @@ impl CtlData { self.zs_columns.is_empty() } - /// Returns all the cross-table lookup polynomials. - pub(crate) fn z_polys(&self) -> Vec> { - self.zs_columns + /// Returns all the cross-table lookup helper polynomials. + pub(crate) fn ctl_helper_polys(&self) -> Vec> { + let num_polys = self + .zs_columns .iter() - .map(|zs_columns| zs_columns.z.clone()) - .collect() + .fold(0, |acc, z| acc + z.helper_columns.len()); + let mut res = Vec::with_capacity(num_polys); + for z in &self.zs_columns { + res.extend(z.helper_columns.clone()); + } + + res + } + + /// Returns all the Z cross-table-lookup polynomials. + pub(crate) fn ctl_z_polys(&self) -> Vec> { + let mut res = Vec::with_capacity(self.zs_columns.len()); + for z in &self.zs_columns { + res.push(z.z.clone()); + } + + res + } + /// Returns the number of helper columns for each STARK in each + /// `CtlZData`. + pub(crate) fn num_ctl_helper_polys(&self) -> Vec { + let mut res = Vec::with_capacity(self.zs_columns.len()); + for z in &self.zs_columns { + res.push(z.helper_columns.len()); + } + + res } } @@ -640,16 +697,46 @@ pub(crate) fn get_grand_product_challenge_set_target< GrandProductChallengeSet { challenges } } +/// Returns the number of helper columns for each `Table`. +pub(crate) fn num_ctl_helper_columns_by_table( + ctls: &[CrossTableLookup], + constraint_degree: usize, +) -> Vec<[usize; NUM_TABLES]> { + let mut res = vec![[0; NUM_TABLES]; ctls.len()]; + for (i, ctl) in ctls.iter().enumerate() { + let CrossTableLookup { + looking_tables, + looked_table, + } = ctl; + let mut num_by_table = [0; NUM_TABLES]; + + let grouped_lookups = looking_tables.iter().group_by(|&a| a.table); + + for (table, group) in grouped_lookups.into_iter() { + let sum = group.count(); + if sum > 2 { + // We only need helper columns if there are more than 2 columns. + num_by_table[table as usize] = ceil_div_usize(sum, constraint_degree - 1); + } + } + + res[i] = num_by_table; + } + res +} + /// Generates all the cross-table lookup data, for all tables. /// - `trace_poly_values` corresponds to the trace values for all tables. /// - `cross_table_lookups` corresponds to all the cross-table lookups, i.e. the looked and looking tables, as described in `CrossTableLookup`. /// - `ctl_challenges` corresponds to the challenges used for CTLs. +/// - `constraint_degree` is the maximal constraint degree for the table. /// For each `CrossTableLookup`, and each looking/looked table, the partial products for the CTL are computed, and added to the said table's `CtlZData`. -pub(crate) fn cross_table_lookup_data( +pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize>( trace_poly_values: &[Vec>; NUM_TABLES], - cross_table_lookups: &[CrossTableLookup], + cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &GrandProductChallengeSet, -) -> [CtlData; NUM_TABLES] { + constraint_degree: usize, +) -> [CtlData<'a, F>; NUM_TABLES] { let mut ctl_data_per_table = [0; NUM_TABLES].map(|_| CtlData::default()); for CrossTableLookup { looking_tables, @@ -658,103 +745,232 @@ pub(crate) fn cross_table_lookup_data( { log::debug!("Processing CTL for {:?}", looked_table.table); for &challenge in &ctl_challenges.challenges { - let zs_looking = looking_tables.iter().map(|table| { - partial_sums( - &trace_poly_values[table.table as usize], - &table.columns, - &table.filter, - challenge, - ) - }); - let z_looked = partial_sums( + let helper_zs_looking = ctl_helper_zs_cols( + trace_poly_values, + looking_tables.clone(), + challenge, + constraint_degree, + ); + + let mut z_looked = partial_sums( &trace_poly_values[looked_table.table as usize], - &looked_table.columns, - &looked_table.filter, + &[(&looked_table.columns, &looked_table.filter)], challenge, + constraint_degree, ); - for (table, z) in looking_tables.iter().zip(zs_looking) { - ctl_data_per_table[table.table as usize] - .zs_columns - .push(CtlZData { - z, - challenge, - columns: table.columns.clone(), - filter: table.filter.clone(), - }); + + for (table, helpers_zs) in helper_zs_looking { + let num_helpers = helpers_zs.len() - 1; + let count = looking_tables + .iter() + .filter(|looking_table| looking_table.table as usize == table) + .count(); + let cols_filts = looking_tables.iter().filter_map(|looking_table| { + if looking_table.table as usize == table { + Some((&looking_table.columns, &looking_table.filter)) + } else { + None + } + }); + let mut columns = Vec::with_capacity(count); + let mut filter = Vec::with_capacity(count); + for (col, filt) in cols_filts { + columns.push(&col[..]); + filter.push(filt.clone()); + } + ctl_data_per_table[table].zs_columns.push(CtlZData { + helper_columns: helpers_zs[..num_helpers].to_vec(), + z: helpers_zs[num_helpers].clone(), + challenge, + columns, + filter, + }); } + // There is no helper column for the looking table. + let looked_poly = z_looked[0].clone(); ctl_data_per_table[looked_table.table as usize] .zs_columns .push(CtlZData { - z: z_looked, + helper_columns: vec![], + z: looked_poly, challenge, - columns: looked_table.columns.clone(), - filter: looked_table.filter.clone(), + columns: vec![&looked_table.columns[..]], + filter: vec![looked_table.filter.clone()], }); } } ctl_data_per_table } +type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); + +/// Given a STARK's trace, and the data associated to one lookup (either CTL or range check), +/// returns the associated helper polynomials. +pub(crate) fn get_helper_cols( + trace: &[PolynomialValues], + degree: usize, + columns_filters: &[ColumnFilter], + challenge: GrandProductChallenge, + constraint_degree: usize, +) -> Vec> { + let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); + + let mut helper_columns = Vec::with_capacity(num_helper_columns); + + let mut filter_index = 0; + for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { + let (first_col, first_filter) = cols_filts.next().unwrap(); + + let mut filter_col = Vec::with_capacity(degree); + let first_combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = first_filter { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = first_col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + let mut acc = F::batch_multiplicative_inverse(&first_combined); + for d in 0..degree { + if filter_col[d].is_zero() { + acc[d] = F::ZERO; + } + } + + for (col, filt) in cols_filts { + let mut filter_col = Vec::with_capacity(degree); + let mut combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = filt { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + combined = F::batch_multiplicative_inverse(&combined); + + for d in 0..degree { + if filter_col[d].is_zero() { + combined[d] = F::ZERO; + } + } + + batch_add_inplace(&mut acc, &combined); + } + + helper_columns.push(acc.into()); + } + assert_eq!(helper_columns.len(), num_helper_columns); + + helper_columns +} + +/// Computes helper columns and Z polynomials for all looking tables +/// of one cross-table lookup (i.e. for one looked table). +fn ctl_helper_zs_cols( + all_stark_traces: &[Vec>; NUM_TABLES], + looking_tables: Vec>, + challenge: GrandProductChallenge, + constraint_degree: usize, +) -> Vec<(usize, Vec>)> { + let grouped_lookups = looking_tables.iter().group_by(|a| a.table); + + grouped_lookups + .into_iter() + .map(|(table, group)| { + let degree = all_stark_traces[table as usize][0].len(); + let columns_filters = group + .map(|table| (&table.columns[..], &table.filter)) + .collect::], &Option>)>>(); + ( + table as usize, + partial_sums( + &all_stark_traces[table as usize], + &columns_filters, + challenge, + constraint_degree, + ), + ) + }) + .collect::>)>>() +} + /// Computes the cross-table lookup partial sums for one table and given column linear combinations. /// `trace` represents the trace values for the given table. -/// `columns` are all the column linear combinations to evaluate. -/// `filter_column` is a column linear combination used to determine whether a row should be selected. +/// `columns` is a vector of column linear combinations to evaluate. Each element in the vector represents columns that need to be combined. +/// `filter_cols` are column linear combinations used to determine whether a row should be selected. /// `challenge` is a cross-table lookup challenge. /// The initial sum `s` is 0. -/// For each row, if the `filter_column` evaluates to 1, then the rows is selected. All the column linear combinations are evaluated at said row. All those evaluations are combined using the challenge to get a value `v`. -/// The sum is updated: `s += 1/v`, and is pushed to the vector of partial sums. +/// For each row, if the `filter_column` evaluates to 1, then the row is selected. All the column linear combinations are evaluated at said row. +/// The evaluations of each elements of `columns` are then combined together to form a value `v`. +/// The values `v`` are grouped together, in groups of size `constraint_degree - 1` (2 in our case). For each group, we construct a helper +/// column: h = \sum_i 1/(v_i). +/// +/// The sum is updated: `s += \sum h_i`, and is pushed to the vector of partial sums `z``. +/// Returns the helper columns and `z`. fn partial_sums( trace: &[PolynomialValues], - columns: &[Column], - filter: &Option>, + columns_filters: &[ColumnFilter], challenge: GrandProductChallenge, -) -> PolynomialValues { - let mut partial_sum = F::ZERO; + constraint_degree: usize, +) -> Vec> { let degree = trace[0].len(); - let mut filters = Vec::with_capacity(degree); - let mut res = Vec::with_capacity(degree); - - for i in (0..degree).rev() { - if let Some(filter) = filter { - let filter_val = filter.eval_table(trace, i); - if filter_val.is_one() { - filters.push(true); - } else { - assert_eq!(filter_val, F::ZERO, "Non-binary filter?"); - filters.push(false); - } - } else { - filters.push(false); - }; + let mut z = Vec::with_capacity(degree); - let combined = if filters[filters.len() - 1] { - let evals = columns - .iter() - .map(|c| c.eval_table(trace, i)) - .collect::>(); - challenge.combine(evals.iter()) - } else { - // Dummy value. Cannot be zero since it will be batch-inverted. - F::ONE - }; - res.push(combined); - } - res = F::batch_multiplicative_inverse(&res); + let mut helper_columns = + get_helper_cols(trace, degree, columns_filters, challenge, constraint_degree); - if !filters[0] { - res[0] = F::ZERO; - } + let x = helper_columns + .iter() + .map(|col| col.values[degree - 1]) + .sum::(); + z.push(x); - for i in (1..degree) { - let mut cur_value = res[i - 1]; - if filters[i] { - cur_value += res[i]; - } - res[i] = cur_value; + for i in (0..degree - 1).rev() { + let x = helper_columns.iter().map(|col| col.values[i]).sum::(); + + z.push(z[z.len() - 1] + x); + } + z.reverse(); + if columns_filters.len() > 2 { + helper_columns.push(z.into()); + } else { + helper_columns = vec![z.into()]; } - res.reverse(); - res.into() + helper_columns } /// Data necessary to check the cross-table lookups of a given table. @@ -765,6 +981,9 @@ where FE: FieldExtension, P: PackedField, { + /// Helper columns to check that the Z polyomial + /// was constructed correctly. + pub(crate) helper_columns: Vec

, /// Evaluation of the trace polynomials at point `zeta`. pub(crate) local_z: P, /// Evaluation of the trace polynomials at point `g * zeta` @@ -772,9 +991,9 @@ where /// Cross-table lookup challenges. pub(crate) challenges: GrandProductChallenge, /// Column linear combinations of the `CrossTableLookup`s. - pub(crate) columns: &'a [Column], + pub(crate) columns: Vec<&'a [Column]>, /// Filter that evaluates to either 1 or 0. - pub(crate) filter: &'a Option>, + pub(crate) filter: Vec>>, } impl<'a, F: RichField + Extendable, const D: usize> @@ -786,45 +1005,107 @@ impl<'a, F: RichField + Extendable, const D: usize> cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_lookup_columns: &[usize; NUM_TABLES], + num_helper_ctl_columns: &Vec<[usize; NUM_TABLES]>, ) -> [Vec; NUM_TABLES] { + let mut total_num_helper_cols_by_table = [0; NUM_TABLES]; + for p_ctls in num_helper_ctl_columns { + for j in 0..NUM_TABLES { + total_num_helper_cols_by_table[j] += p_ctls[j] * ctl_challenges.challenges.len(); + } + } + // Get all cross-table lookup polynomial openings for each STARK proof. let mut ctl_zs = proofs .iter() .zip(num_lookup_columns) .map(|(p, &num_lookup)| { let openings = &p.proof.openings; - let ctl_zs = openings.auxiliary_polys.iter().skip(num_lookup); - let ctl_zs_next = openings.auxiliary_polys_next.iter().skip(num_lookup); - ctl_zs.zip(ctl_zs_next) + + let ctl_zs = &openings.auxiliary_polys[num_lookup..]; + let ctl_zs_next = &openings.auxiliary_polys_next[num_lookup..]; + ctl_zs.iter().zip(ctl_zs_next).collect::>() }) .collect::>(); // Put each cross-table lookup polynomial into the correct table data: if a CTL polynomial is extracted from looking/looked table t, then we add it to the `CtlCheckVars` of table t. + let mut start_indices = [0; NUM_TABLES]; + let mut z_indices = [0; NUM_TABLES]; let mut ctl_vars_per_table = [0; NUM_TABLES].map(|_| vec![]); - for CrossTableLookup { - looking_tables, - looked_table, - } in cross_table_lookups + for ( + CrossTableLookup { + looking_tables, + looked_table, + }, + num_ctls, + ) in cross_table_lookups.iter().zip(num_helper_ctl_columns) { for &challenges in &ctl_challenges.challenges { + // Group looking tables by `Table`, since we bundle the looking tables taken from the same `Table` together thanks to helper columns. + // We want to only iterate on each `Table` once. + let mut filtered_looking_tables = + Vec::with_capacity(min(looking_tables.len(), NUM_TABLES)); for table in looking_tables { - let (looking_z, looking_z_next) = ctl_zs[table.table as usize].next().unwrap(); - ctl_vars_per_table[table.table as usize].push(Self { + if !filtered_looking_tables.contains(&(table.table as usize)) { + filtered_looking_tables.push(table.table as usize); + } + } + + for (i, &table) in filtered_looking_tables.iter().enumerate() { + // We have first all the helper polynomials, then all the z polynomials. + let (looking_z, looking_z_next) = + ctl_zs[table][total_num_helper_cols_by_table[table] + z_indices[table]]; + + let count = looking_tables + .iter() + .filter(|looking_table| looking_table.table as usize == table) + .count(); + let cols_filts = looking_tables.iter().filter_map(|looking_table| { + if looking_table.table as usize == table { + Some((&looking_table.columns, &looking_table.filter)) + } else { + None + } + }); + let mut columns = Vec::with_capacity(count); + let mut filter = Vec::with_capacity(count); + for (col, filt) in cols_filts { + columns.push(&col[..]); + filter.push(filt.clone()); + } + let helper_columns = ctl_zs[table] + [start_indices[table]..start_indices[table] + num_ctls[table]] + .iter() + .map(|&(h, _)| *h) + .collect::>(); + + start_indices[table] += num_ctls[table]; + + z_indices[table] += 1; + ctl_vars_per_table[table].push(Self { + helper_columns, local_z: *looking_z, next_z: *looking_z_next, challenges, - columns: &table.columns, - filter: &table.filter, + columns, + filter, }); } - let (looked_z, looked_z_next) = ctl_zs[looked_table.table as usize].next().unwrap(); + let (looked_z, looked_z_next) = ctl_zs[looked_table.table as usize] + [total_num_helper_cols_by_table[looked_table.table as usize] + + z_indices[looked_table.table as usize]]; + + z_indices[looked_table.table as usize] += 1; + + let columns = vec![&looked_table.columns[..]]; + let filter = vec![looked_table.filter.clone()]; ctl_vars_per_table[looked_table.table as usize].push(Self { + helper_columns: vec![], local_z: *looked_z, next_z: *looked_z_next, challenges, - columns: &looked_table.columns, - filter: &looked_table.filter, + columns, + filter, }); } } @@ -832,6 +1113,61 @@ impl<'a, F: RichField + Extendable, const D: usize> } } +/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. +pub(crate) fn eval_helper_columns( + filter: &[Option>], + columns: &[Vec

], + local_values: &[P], + next_values: &[P], + helper_columns: &[P], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, +{ + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + match chunk.len() { + 2 => { + let combin0 = challenges.combine(&chunk[0]); + let combin1 = challenges.combine(chunk[1].iter()); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter(local_values, next_values) + } else { + P::ONES + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + + consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); + } + 1 => { + let combin = challenges.combine(&chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + consumer.constraint(combin * h - f0); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + /// Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. @@ -843,6 +1179,7 @@ pub(crate) fn eval_cross_table_lookup_checks, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, + constraint_degree: usize, ) where F: RichField + Extendable, FE: FieldExtension, @@ -854,6 +1191,7 @@ pub(crate) fn eval_cross_table_lookup_checks>() + }) .collect::>(); - let combined = challenges.combine(evals.iter()); - let local_filter = if let Some(combin) = filter { - combin.eval_filter(local_values, next_values) - } else { - P::ONES - }; - // Check value of `Z(g^(n-1))` - consumer.constraint_last_row(*local_z * combined - local_filter); - // Check `Z(w) = Z(gw) * (filter / combination)` - consumer.constraint_transition((*local_z - *next_z) * combined - local_filter); + // Check helper columns. + eval_helper_columns( + filter, + &evals, + local_values, + next_values, + helper_columns, + constraint_degree, + challenges, + consumer, + ); + + if !helper_columns.is_empty() { + let h_sum = helper_columns.iter().fold(P::ZEROS, |acc, x| acc + *x); + // Check value of `Z(g^(n-1))` + consumer.constraint_last_row(*local_z - h_sum); + // Check `Z(w) = Z(gw) + \sum h_i` + consumer.constraint_transition(*local_z - *next_z - h_sum); + } else if columns.len() > 1 { + let combin0 = challenges.combine(&evals[0]); + let combin1 = challenges.combine(&evals[1]); + + let f0 = if let Some(filter0) = &filter[0] { + filter0.eval_filter(local_values, next_values) + } else { + P::ONES + }; + let f1 = if let Some(filter1) = &filter[1] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + + consumer + .constraint_last_row(combin0 * combin1 * *local_z - f0 * combin1 - f1 * combin0); + consumer.constraint_transition( + combin0 * combin1 * (*local_z - *next_z) - f0 * combin1 - f1 * combin0, + ); + } else { + let combin0 = challenges.combine(&evals[0]); + let f0 = if let Some(filter0) = &filter[0] { + filter0.eval_filter(local_values, next_values) + } else { + P::ONES + }; + consumer.constraint_last_row(combin0 * *local_z - f0); + consumer.constraint_transition(combin0 * (*local_z - *next_z) - f0); + } } } /// Circuit version of `CtlCheckVars`. Data necessary to check the cross-table lookups of a given table. #[derive(Clone)] -pub(crate) struct CtlCheckVarsTarget<'a, F: Field, const D: usize> { +pub(crate) struct CtlCheckVarsTarget { + ///Evaluation of the helper columns to check that the Z polyomial + /// was constructed correctly. + pub(crate) helper_columns: Vec>, /// Evaluation of the trace polynomials at point `zeta`. pub(crate) local_z: ExtensionTarget, /// Evaluation of the trace polynomials at point `g * zeta`. @@ -890,12 +1273,12 @@ pub(crate) struct CtlCheckVarsTarget<'a, F: Field, const D: usize> { /// Cross-table lookup challenges. pub(crate) challenges: GrandProductChallenge, /// Column linear combinations of the `CrossTableLookup`s. - pub(crate) columns: &'a [Column], + pub(crate) columns: Vec>>, /// Filter that evaluates to either 1 or 0. - pub(crate) filter: &'a Option>, + pub(crate) filter: Vec>>, } -impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { +impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { /// Circuit version of `from_proofs`. Extracts the `CtlCheckVarsTarget` for each STARK. pub(crate) fn from_proof( table: Table, @@ -903,6 +1286,8 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_lookup_columns: usize, + total_num_helper_columns: usize, + num_helper_ctl_columns: &[usize], ) -> Vec { // Get all cross-table lookup polynomial openings for each STARK proof. let mut ctl_zs = { @@ -912,47 +1297,145 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { .auxiliary_polys_next .iter() .skip(num_lookup_columns); - ctl_zs.zip(ctl_zs_next) + ctl_zs.zip(ctl_zs_next).collect::>() }; // Put each cross-table lookup polynomial into the correct table data: if a CTL polynomial is extracted from looking/looked table t, then we add it to the `CtlCheckVars` of table t. + let mut z_index = 0; + let mut start_index = 0; let mut ctl_vars = vec![]; - for CrossTableLookup { - looking_tables, - looked_table, - } in cross_table_lookups + for ( + i, + CrossTableLookup { + looking_tables, + looked_table, + }, + ) in cross_table_lookups.iter().enumerate() { for &challenges in &ctl_challenges.challenges { - for looking_table in looking_tables { + // Group looking tables by `Table`, since we bundle the looking tables taken from the same `Table` together thanks to helper columns. + + let count = looking_tables + .iter() + .filter(|looking_table| looking_table.table == table) + .count(); + let cols_filts = looking_tables.iter().filter_map(|looking_table| { if looking_table.table == table { - let (looking_z, looking_z_next) = ctl_zs.next().unwrap(); - ctl_vars.push(Self { - local_z: *looking_z, - next_z: *looking_z_next, - challenges, - columns: &looking_table.columns, - filter: &looking_table.filter, - }); + Some((&looking_table.columns, &looking_table.filter)) + } else { + None } + }); + if count > 0 { + let mut columns = Vec::with_capacity(count); + let mut filter = Vec::with_capacity(count); + for (col, filt) in cols_filts { + columns.push(col.clone()); + filter.push(filt.clone()); + } + let (looking_z, looking_z_next) = ctl_zs[total_num_helper_columns + z_index]; + let helper_columns = ctl_zs + [start_index..start_index + num_helper_ctl_columns[i]] + .iter() + .map(|(&h, _)| h) + .collect::>(); + + start_index += num_helper_ctl_columns[i]; + z_index += 1; + // let columns = group.0.clone(); + // let filter = group.1.clone(); + ctl_vars.push(Self { + helper_columns, + local_z: *looking_z, + next_z: *looking_z_next, + challenges, + columns, + filter, + }); } if looked_table.table == table { - let (looked_z, looked_z_next) = ctl_zs.next().unwrap(); + let (looked_z, looked_z_next) = ctl_zs[total_num_helper_columns + z_index]; + z_index += 1; + + let columns = vec![looked_table.columns.clone()]; + let filter = vec![looked_table.filter.clone()]; ctl_vars.push(Self { + helper_columns: vec![], local_z: *looked_z, next_z: *looked_z_next, challenges, - columns: &looked_table.columns, - filter: &looked_table.filter, + columns, + filter, }); } } } - assert!(ctl_zs.next().is_none()); + ctl_vars } } +/// Circuit version of `eval_helper_columns`. +/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. +pub(crate) fn eval_helper_columns_circuit, const D: usize>( + builder: &mut CircuitBuilder, + filter: &[Option>], + columns: &[Vec>], + local_values: &[ExtensionTarget], + next_values: &[ExtensionTarget], + helper_columns: &[ExtensionTarget], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut RecursiveConstraintConsumer, +) { + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + let one = builder.one_extension(); + match chunk.len() { + 2 => { + let combin0 = challenges.combine_circuit(builder, &chunk[0]); + let combin1 = challenges.combine_circuit(builder, &chunk[1]); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + + let constr = builder.mul_sub_extension(combin0, h, f0); + let constr = builder.mul_extension(constr, combin1); + let f1_constr = builder.mul_extension(f1, combin0); + let constr = builder.sub_extension(constr, f1_constr); + + consumer.constraint(builder, constr); + } + 1 => { + let combin = challenges.combine_circuit(builder, &chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let constr = builder.mul_sub_extension(combin, h, f0); + consumer.constraint(builder, constr); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + /// Circuit version of `eval_cross_table_lookup_checks`. Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. @@ -969,12 +1452,16 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< vars: &S::EvaluationFrameTarget, ctl_vars: &[CtlCheckVarsTarget], consumer: &mut RecursiveConstraintConsumer, + constraint_degree: usize, ) { let local_values = vars.get_local_values(); let next_values = vars.get_next_values(); + let one = builder.one_extension(); + for lookup_vars in ctl_vars { let CtlCheckVarsTarget { + helper_columns, local_z, next_z, challenges, @@ -982,29 +1469,77 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< filter, } = lookup_vars; - let one = builder.one_extension(); - let local_filter = if let Some(combin) = filter { - combin.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - // Compute all linear combinations on the current table, and combine them using the challenge. let evals = columns .iter() - .map(|c| c.eval_with_next_circuit(builder, local_values, next_values)) + .map(|col| { + col.iter() + .map(|c| c.eval_with_next_circuit(builder, local_values, next_values)) + .collect::>() + }) .collect::>(); - let combined = challenges.combine_circuit(builder, &evals); + // Check helper columns. + eval_helper_columns_circuit( + builder, + filter, + &evals, + local_values, + next_values, + helper_columns, + constraint_degree, + challenges, + consumer, + ); - // Check value of `Z(g^(n-1))` - let last_row = builder.mul_sub_extension(*local_z, combined, local_filter); - consumer.constraint_last_row(builder, last_row); - // Check `Z(w) = Z(gw) * (filter / combination)` let z_diff = builder.sub_extension(*local_z, *next_z); - let lhs = builder.mul_extension(combined, z_diff); - let transition = builder.sub_extension(lhs, local_filter); - consumer.constraint_transition(builder, transition); + if !helper_columns.is_empty() { + // Check value of `Z(g^(n-1))` + let h_sum = builder.add_many_extension(helper_columns); + + let last_row = builder.sub_extension(*local_z, h_sum); + consumer.constraint_last_row(builder, last_row); + // Check `Z(w) = Z(gw) * (filter / combination)` + + let transition = builder.sub_extension(z_diff, h_sum); + consumer.constraint_transition(builder, transition); + } else if columns.len() > 1 { + let combin0 = challenges.combine_circuit(builder, &evals[0]); + let combin1 = challenges.combine_circuit(builder, &evals[1]); + + let f0 = if let Some(filter0) = &filter[0] { + filter0.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let f1 = if let Some(filter1) = &filter[1] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + + let combined = builder.mul_sub_extension(combin1, *local_z, f1); + let combined = builder.mul_extension(combined, combin0); + let constr = builder.arithmetic_extension(F::NEG_ONE, F::ONE, f0, combin1, combined); + consumer.constraint_last_row(builder, constr); + + let combined = builder.mul_sub_extension(combin1, z_diff, f1); + let combined = builder.mul_extension(combined, combin0); + let constr = builder.arithmetic_extension(F::NEG_ONE, F::ONE, f0, combin1, combined); + consumer.constraint_last_row(builder, constr); + } else { + let combin0 = challenges.combine_circuit(builder, &evals[0]); + let f0 = if let Some(filter0) = &filter[0] { + filter0.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + + let constr = builder.mul_sub_extension(combin0, *local_z, f0); + consumer.constraint_last_row(builder, constr); + let constr = builder.mul_sub_extension(combin0, z_diff, f0); + consumer.constraint_transition(builder, constr); + } } } @@ -1026,11 +1561,19 @@ pub(crate) fn verify_cross_table_lookups, const D: { // Get elements looking into `looked_table` that are not associated to any STARK. let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table as usize]; + // We want to iterate on each looking table only once. + let mut filtered_looking_tables = vec![]; + for table in looking_tables { + if !filtered_looking_tables.contains(&(table.table as usize)) { + filtered_looking_tables.push(table.table as usize); + } + } for c in 0..config.num_challenges { // Compute the combination of all looking table CTL polynomial openings. - let looking_zs_sum = looking_tables + + let looking_zs_sum = filtered_looking_tables .iter() - .map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()) + .map(|&table| *ctl_zs_openings[table].next().unwrap()) .sum::() + extra_sum_vec[c]; @@ -1065,12 +1608,19 @@ pub(crate) fn verify_cross_table_lookups_circuit, c { // Get elements looking into `looked_table` that are not associated to any STARK. let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table as usize]; + // We want to iterate on each looking table only once. + let mut filtered_looking_tables = vec![]; + for table in looking_tables { + if !filtered_looking_tables.contains(&(table.table as usize)) { + filtered_looking_tables.push(table.table as usize); + } + } for c in 0..inner_config.num_challenges { // Compute the combination of all looking table CTL polynomial openings. let mut looking_zs_sum = builder.add_many( - looking_tables + filtered_looking_tables .iter() - .map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()), + .map(|&table| *ctl_zs_openings[table].next().unwrap()), ); looking_zs_sum = builder.add(looking_zs_sum, extra_sum_vec[c]); diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index b3e31093c1..63098e793c 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -740,13 +740,14 @@ mod tests { // Fake CTL data. let ctl_z_data = CtlZData { + helper_columns: vec![PolynomialValues::zero(degree)], z: PolynomialValues::zero(degree), challenge: GrandProductChallenge { beta: F::ZERO, gamma: F::ZERO, }, columns: vec![], - filter: Some(Filter::new_simple(Column::constant(F::ZERO))), + filter: vec![Some(Filter::new_simple(Column::constant(F::ZERO)))], }; let ctl_data = CtlData { zs_columns: vec![ctl_z_data.clone(); config.num_challenges], diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index 8bf0c8f61b..4b735c6427 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -12,7 +12,10 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter}; +use crate::cross_table_lookup::{ + eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, Filter, + GrandProductChallenge, +}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::stark::Stark; @@ -64,48 +67,38 @@ pub(crate) fn lookup_helper_columns( let num_helper_columns = lookup.num_helper_columns(constraint_degree); let mut helper_columns: Vec> = Vec::with_capacity(num_helper_columns); + let looking_cols = lookup + .columns + .iter() + .map(|col| vec![col.clone()]) + .collect::>>>(); + + let grand_challenge = GrandProductChallenge { + beta: F::ONE, + gamma: challenge, + }; + + let columns_filters = looking_cols + .iter() + .zip(lookup.filter_columns.iter()) + .map(|(col, filter)| (&col[..], filter)) + .collect::>(); // For each batch of `constraint_degree-1` columns `fi`, compute `sum 1/(f_i+challenge)` and // add it to the helper columns. - // TODO: This does one batch inversion per column. It would also be possible to do one batch inversion - // for every group of columns, but that would require building a big vector of all the columns concatenated. - // Not sure which approach is better. // Note: these are the h_k(x) polynomials in the paper, with a few differences: // * Here, the first ratio m_0(x)/phi_0(x) is not included with the columns batched up to create the // h_k polynomials; instead there's a separate helper column for it (see below). // * Here, we use 1 instead of -1 as the numerator (and subtract later). // * Here, for now, the batch size (l) is always constraint_degree - 1 = 2. - for (i, mut col_inds) in (&lookup.columns.iter().chunks(constraint_degree - 1)) - .into_iter() - .enumerate() - { - let first = col_inds.next().unwrap(); - - let mut column = first.eval_all_rows(trace_poly_values); - let length = column.len(); - - for x in column.iter_mut() { - *x = challenge + *x; - } - let mut acc = F::batch_multiplicative_inverse(&column); - if let Some(filter) = &lookup.filter_columns[0] { - batch_multiply_inplace(&mut acc, &filter.eval_all_rows(trace_poly_values)); - } - - for (j, ind) in col_inds.enumerate() { - let mut column = ind.eval_all_rows(trace_poly_values); - for x in column.iter_mut() { - *x = challenge + *x; - } - column = F::batch_multiplicative_inverse(&column); - let filter_idx = (constraint_degree - 1) * i + j + 1; - if let Some(filter) = &lookup.filter_columns[filter_idx] { - batch_multiply_inplace(&mut column, &filter.eval_all_rows(trace_poly_values)); - } - batch_add_inplace(&mut acc, &column); - } - - helper_columns.push(acc.into()); - } + // * Here, there are filters for the columns, to only select some rows + // in a given column. + let mut helper_columns = get_helper_cols( + trace_poly_values, + trace_poly_values[0].len(), + &columns_filters, + grand_challenge, + constraint_degree, + ); // Add `1/(table+challenge)` to the helper columns. // This is 1/phi_0(x) = 1/(x + t(x)) from the paper. @@ -168,38 +161,30 @@ pub(crate) fn eval_packed_lookups_generic>>(); + // For each chunk, check that `h_i (x+f_2i) (x+f_{2i+1}) = (x+f_2i) * filter_{2i+1} + (x+f_{2i+1}) * filter_2i` if the chunk has length 2 // or if it has length 1, check that `h_i * (x+f_2i) = filter_2i`, where x is the challenge - for (j, chunk) in lookup.columns.chunks(degree - 1).enumerate() { - let mut x = lookup_vars.local_values[start + j]; - let mut y = P::ZEROS; - let col_values = chunk - .iter() - .map(|col| col.eval_with_next(local_values, next_values)); - let filters = lookup.filter_columns - [(degree - 1) * j..(degree - 1) * j + chunk.len()] - .iter() - .map(|maybe_filter| { - if let Some(filter) = maybe_filter { - filter.eval_filter(local_values, next_values) - } else { - P::ONES - } - }) - .rev() - .collect::>(); - let last_filter_value = filters[0]; - for (val, f) in col_values.zip_eq(filters) { - x *= val + challenge; - y += (val + challenge) * f; - } - match chunk.len() { - 2 => yield_constr.constraint(x - y), - 1 => yield_constr.constraint(x - last_filter_value), - _ => todo!("Allow other constraint degrees."), - } - } + eval_helper_columns( + &lookup.filter_columns, + &lookup_columns, + local_values, + next_values, + &lookup_vars.local_values[start..start + num_helper_columns - 1], + degree, + &grand_challenge, + yield_constr, + ); + + let challenge = FE::from_basefield(challenge); // Check the `Z` polynomial. let z = lookup_vars.local_values[start + num_helper_columns - 1]; @@ -245,45 +230,30 @@ pub(crate) fn eval_ext_lookups_circuit< let mut start = 0; for lookup in lookups { let num_helper_columns = lookup.num_helper_columns(degree); + let col_values = lookup + .columns + .iter() + .map(|col| vec![col.eval_with_next_circuit(builder, local_values, next_values)]) + .collect::>(); + for &challenge in &lookup_vars.challenges { + let grand_challenge = GrandProductChallenge { + beta: builder.one(), + gamma: challenge, + }; + + eval_helper_columns_circuit( + builder, + &lookup.filter_columns, + &col_values, + local_values, + next_values, + &lookup_vars.local_values[start..start + num_helper_columns - 1], + degree, + &grand_challenge, + yield_constr, + ); let challenge = builder.convert_to_ext(challenge); - for (j, chunk) in lookup.columns.chunks(degree - 1).enumerate() { - let mut x = lookup_vars.local_values[start + j]; - let mut y = builder.zero_extension(); - let col_values = chunk - .iter() - .map(|k| k.eval_with_next_circuit(builder, local_values, next_values)) - .collect::>(); - let filters = lookup.filter_columns - [(degree - 1) * j..(degree - 1) * j + chunk.len()] - .iter() - .map(|maybe_filter| { - if let Some(filter) = maybe_filter { - filter.eval_filter_circuit(builder, local_values, next_values) - } else { - one - } - }) - .rev() - .collect::>(); - let last_filter_value = filters[0]; - for (&val, f) in col_values.iter().zip_eq(filters) { - let tmp = builder.add_extension(val, challenge); - x = builder.mul_extension(x, tmp); - y = builder.mul_add_extension(f, tmp, y); - } - match chunk.len() { - 2 => { - let tmp = builder.sub_extension(x, y); - yield_constr.constraint(builder, tmp) - } - 1 => { - let tmp = builder.sub_extension(x, last_filter_value); - yield_constr.constraint(builder, tmp) - } - _ => todo!("Allow other constraint degrees."), - } - } let z = lookup_vars.local_values[start + num_helper_columns - 1]; let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 3768f98f90..4cc1867738 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -974,7 +974,10 @@ impl, const D: usize> StarkOpeningSet { auxiliary_polys_commitment: &PolynomialBatch, quotient_commitment: &PolynomialBatch, num_lookup_columns: usize, + num_ctl_polys: &[usize], ) -> Self { + let total_num_helper_cols: usize = num_ctl_polys.iter().sum(); + // Batch evaluates polynomials on the LDE, at a point `z`. let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { c.polynomials @@ -989,6 +992,9 @@ impl, const D: usize> StarkOpeningSet { .map(|p| p.eval(z)) .collect::>() }; + + let auxiliary_first = eval_commitment_base(F::ONE, auxiliary_polys_commitment); + let ctl_zs_first = auxiliary_first[num_lookup_columns + total_num_helper_cols..].to_vec(); // `g * zeta`. let zeta_next = zeta.scalar_mul(g); Self { @@ -996,9 +1002,7 @@ impl, const D: usize> StarkOpeningSet { next_values: eval_commitment(zeta_next, trace_commitment), auxiliary_polys: eval_commitment(zeta, auxiliary_polys_commitment), auxiliary_polys_next: eval_commitment(zeta_next, auxiliary_polys_commitment), - ctl_zs_first: eval_commitment_base(F::ONE, auxiliary_polys_commitment) - [num_lookup_columns..] - .to_vec(), + ctl_zs_first, quotient_polys: eval_commitment(zeta, quotient_commitment), } } @@ -1051,7 +1055,7 @@ pub(crate) struct StarkOpeningSetTarget { pub auxiliary_polys: Vec>, /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `g * zeta`. pub auxiliary_polys_next: Vec>, - /// /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1. + /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1. pub ctl_zs_first: Vec, /// `ExtensionTarget`s for the opening of quotient polynomials at `zeta`. pub quotient_polys: Vec>, diff --git a/evm/src/prover.rs b/evm/src/prover.rs index c90490b8a0..e642602315 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -135,6 +135,7 @@ where &trace_poly_values, &all_stark.cross_table_lookups, &ctl_challenges, + all_stark.arithmetic_stark.constraint_degree() ) ); @@ -375,9 +376,14 @@ where // We add CTLs to the permutation arguments so that we can batch commit to // all auxiliary polynomials. let auxiliary_polys = match lookup_helper_columns { - None => ctl_data.z_polys(), + None => { + let mut ctl_polys = ctl_data.ctl_helper_polys(); + ctl_polys.extend(ctl_data.ctl_z_polys()); + ctl_polys + } Some(mut lookup_columns) => { - lookup_columns.extend(ctl_data.z_polys()); + lookup_columns.extend(ctl_data.ctl_helper_polys()); + lookup_columns.extend(ctl_data.ctl_z_polys()); lookup_columns } }; @@ -402,6 +408,8 @@ where let alphas = challenger.get_n_challenges(config.num_challenges); + let num_ctl_polys = ctl_data.num_ctl_helper_polys(); + #[cfg(test)] { check_constraints( @@ -414,6 +422,7 @@ where alphas.clone(), degree_bits, num_lookup_columns, + &num_ctl_polys, ); } @@ -432,6 +441,7 @@ where alphas, degree_bits, num_lookup_columns, + &num_ctl_polys, config, ) ); @@ -486,6 +496,7 @@ where &auxiliary_polys_commitment, "ient_commitment, stark.num_lookup_helper_columns(config), + &num_ctl_polys, ); // Get the FRI openings and observe them. challenger.observe_openings(&openings.to_fri_openings()); @@ -502,7 +513,7 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, ctl_data.len(), config), + &stark.fri_instance(zeta, g, num_ctl_polys.iter().sum(), num_ctl_polys, config), &initial_merkle_trees, challenger, &fri_params, @@ -535,6 +546,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( alphas: Vec, degree_bits: usize, num_lookup_columns: usize, + num_ctl_columns: &[usize], config: &StarkConfig, ) -> Vec> where @@ -545,6 +557,7 @@ where { let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; + let total_num_helper_cols: usize = num_ctl_columns.iter().sum(); let quotient_degree_bits = log2_ceil(stark.quotient_degree_factor()); assert!( @@ -616,18 +629,35 @@ where // - for each CTL: // - the filter `Column` // - the `Column`s that form the looking/looked table. + + let mut start_index = 0; let ctl_vars = ctl_data .zs_columns .iter() .enumerate() - .map(|(i, zs_columns)| CtlCheckVars:: { - local_z: auxiliary_polys_commitment.get_lde_values_packed(i_start, step) - [num_lookup_columns + i], - next_z: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step) - [num_lookup_columns + i], - challenges: zs_columns.challenge, - columns: &zs_columns.columns, - filter: &zs_columns.filter, + .map(|(i, zs_columns)| { + let num_ctl_helper_cols = num_ctl_columns[i]; + let helper_columns = auxiliary_polys_commitment + .get_lde_values_packed(i_start, step)[num_lookup_columns + + start_index + ..num_lookup_columns + start_index + num_ctl_helper_cols] + .to_vec(); + + let ctl_vars = CtlCheckVars:: { + helper_columns, + local_z: auxiliary_polys_commitment.get_lde_values_packed(i_start, step) + [num_lookup_columns + total_num_helper_cols + i], + next_z: auxiliary_polys_commitment + .get_lde_values_packed(i_next_start, step) + [num_lookup_columns + total_num_helper_cols + i], + challenges: zs_columns.challenge, + columns: zs_columns.columns.clone(), + filter: zs_columns.filter.clone(), + }; + + start_index += num_ctl_helper_cols; + + ctl_vars }) .collect::>(); @@ -691,6 +721,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( alphas: Vec, degree_bits: usize, num_lookup_columns: usize, + num_ctl_helper_cols: &[usize], ) where F: RichField + Extendable, C: GenericConfig, @@ -699,6 +730,8 @@ fn check_constraints<'a, F, C, S, const D: usize>( let degree = 1 << degree_bits; let rate_bits = 0; // Set this to higher value to check constraint degree. + let total_num_helper_cols: usize = num_ctl_helper_cols.iter().sum(); + let size = degree << rate_bits; let step = 1 << rate_bits; @@ -754,18 +787,34 @@ fn check_constraints<'a, F, C, S, const D: usize>( }); // Get the local and next row evaluations for the current STARK's CTL Z polynomials. + let mut start_index = 0; let ctl_vars = ctl_data .zs_columns .iter() .enumerate() - .map(|(iii, zs_columns)| CtlCheckVars:: { - local_z: auxiliary_subgroup_evals[i][num_lookup_columns + iii], - next_z: auxiliary_subgroup_evals[i_next][num_lookup_columns + iii], - challenges: zs_columns.challenge, - columns: &zs_columns.columns, - filter: &zs_columns.filter, + .map(|(iii, zs_columns)| { + let num_helper_cols = num_ctl_helper_cols[iii]; + let helper_columns = auxiliary_subgroup_evals[i][num_lookup_columns + + start_index + ..num_lookup_columns + start_index + num_helper_cols] + .to_vec(); + let ctl_vars = CtlCheckVars:: { + helper_columns, + local_z: auxiliary_subgroup_evals[i] + [num_lookup_columns + total_num_helper_cols + iii], + next_z: auxiliary_subgroup_evals[i_next] + [num_lookup_columns + total_num_helper_cols + iii], + challenges: zs_columns.challenge, + columns: zs_columns.columns.clone(), + filter: zs_columns.filter.clone(), + }; + + start_index += num_helper_cols; + + ctl_vars }) .collect::>(); + // Evaluate the polynomial combining all constraints, including those associated // to the permutation and CTL arguments. eval_vanishing_poly::( diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 5c10e3b3db..8d10b49866 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -229,10 +229,24 @@ where let zero_target = builder.zero(); let num_lookup_columns = stark.num_lookup_helper_columns(inner_config); - let num_ctl_zs = - CrossTableLookup::num_ctl_zs(cross_table_lookups, table, inner_config.num_challenges); - let proof_target = - add_virtual_stark_proof(&mut builder, stark, inner_config, degree_bits, num_ctl_zs); + let (total_num_helpers, num_ctl_zs, num_helpers_by_ctl) = + CrossTableLookup::num_ctl_helpers_zs_all( + cross_table_lookups, + table, + inner_config.num_challenges, + stark.constraint_degree(), + ); + let num_ctl_helper_zs = num_ctl_zs + total_num_helpers; + + let proof_target = add_virtual_stark_proof( + &mut builder, + stark, + inner_config, + degree_bits, + num_ctl_helper_zs, + num_ctl_zs, + ); + builder.register_public_inputs( &proof_target .trace_cap @@ -257,6 +271,8 @@ where cross_table_lookups, &ctl_challenges_target, num_lookup_columns, + total_num_helpers, + &num_helpers_by_ctl, ); let init_challenger_state_target = @@ -330,6 +346,12 @@ fn verify_stark_proof_with_challenges_circuit< let zero = builder.zero(); let one = builder.one_extension(); + let num_ctl_polys = ctl_vars + .iter() + .map(|ctl| ctl.helper_columns.len()) + .sum::(); + let num_ctl_z_polys = ctl_vars.len(); + let StarkOpeningSetTarget { local_values, next_values, @@ -407,6 +429,7 @@ fn verify_stark_proof_with_challenges_circuit< builder, challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), + num_ctl_polys, ctl_zs_first.len(), inner_config, ); @@ -759,6 +782,7 @@ pub(crate) fn add_virtual_stark_proof< stark: &S, config: &StarkConfig, degree_bits: usize, + num_ctl_helper_zs: usize, num_ctl_zs: usize, ) -> StarkProofTarget { let fri_params = config.fri_params(degree_bits); @@ -766,7 +790,7 @@ pub(crate) fn add_virtual_stark_proof< let num_leaves_per_oracle = vec![ S::COLUMNS, - stark.num_lookup_helper_columns(config) + num_ctl_zs, + stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, stark.quotient_degree_factor() * config.num_challenges, ]; @@ -776,7 +800,13 @@ pub(crate) fn add_virtual_stark_proof< trace_cap: builder.add_virtual_cap(cap_height), auxiliary_polys_cap, quotient_polys_cap: builder.add_virtual_cap(cap_height), - openings: add_virtual_stark_opening_set::(builder, stark, num_ctl_zs, config), + openings: add_virtual_stark_opening_set::( + builder, + stark, + num_ctl_helper_zs, + num_ctl_zs, + config, + ), opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), } } @@ -784,6 +814,7 @@ pub(crate) fn add_virtual_stark_proof< fn add_virtual_stark_opening_set, S: Stark, const D: usize>( builder: &mut CircuitBuilder, stark: &S, + num_ctl_helper_zs: usize, num_ctl_zs: usize, config: &StarkConfig, ) -> StarkOpeningSetTarget { @@ -791,10 +822,12 @@ fn add_virtual_stark_opening_set, S: Stark, c StarkOpeningSetTarget { local_values: builder.add_virtual_extension_targets(S::COLUMNS), next_values: builder.add_virtual_extension_targets(S::COLUMNS), - auxiliary_polys: builder - .add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs), - auxiliary_polys_next: builder - .add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs), + auxiliary_polys: builder.add_virtual_extension_targets( + stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, + ), + auxiliary_polys_next: builder.add_virtual_extension_targets( + stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, + ), ctl_zs_first: builder.add_virtual_targets(num_ctl_zs), quotient_polys: builder .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 6099abdd38..6ee26f585a 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -92,7 +92,8 @@ pub trait Stark, const D: usize>: Sync { &self, zeta: F::Extension, g: F, - num_ctl_zs: usize, + num_ctl_helpers: usize, + num_ctl_zs: Vec, config: &StarkConfig, ) -> FriInstanceInfo { let trace_oracle = FriOracleInfo { @@ -102,7 +103,7 @@ pub trait Stark, const D: usize>: Sync { let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); let num_lookup_columns = self.num_lookup_helper_columns(config); - let num_auxiliary_polys = num_lookup_columns + num_ctl_zs; + let num_auxiliary_polys = num_lookup_columns + num_ctl_helpers + num_ctl_zs.len(); let auxiliary_oracle = FriOracleInfo { num_polys: num_auxiliary_polys, blinding: false, @@ -110,9 +111,10 @@ pub trait Stark, const D: usize>: Sync { let auxiliary_polys_info = FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); + let mut start_index = num_lookup_columns; let ctl_zs_info = FriPolynomialInfo::from_range( AUXILIARY_ORACLE_INDEX, - num_lookup_columns..num_lookup_columns + num_ctl_zs, + num_lookup_columns + num_ctl_helpers..num_auxiliary_polys, ); let num_quotient_polys = self.num_quotient_polys(config); @@ -152,6 +154,7 @@ pub trait Stark, const D: usize>: Sync { builder: &mut CircuitBuilder, zeta: ExtensionTarget, g: F, + num_ctl_helper_polys: usize, num_ctl_zs: usize, inner_config: &StarkConfig, ) -> FriInstanceInfoTarget { @@ -162,7 +165,7 @@ pub trait Stark, const D: usize>: Sync { let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); let num_lookup_columns = self.num_lookup_helper_columns(inner_config); - let num_auxiliary_polys = num_lookup_columns + num_ctl_zs; + let num_auxiliary_polys = num_lookup_columns + num_ctl_helper_polys + num_ctl_zs; let auxiliary_oracle = FriOracleInfo { num_polys: num_auxiliary_polys, blinding: false, @@ -172,7 +175,8 @@ pub trait Stark, const D: usize>: Sync { let ctl_zs_info = FriPolynomialInfo::from_range( AUXILIARY_ORACLE_INDEX, - num_lookup_columns..num_lookup_columns + num_ctl_zs, + num_lookup_columns + num_ctl_helper_polys + ..num_lookup_columns + num_ctl_helper_polys + num_ctl_zs, ); let num_quotient_polys = self.num_quotient_polys(inner_config); diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs index 15444c7e98..c1f2d0f92b 100644 --- a/evm/src/vanishing_poly.rs +++ b/evm/src/vanishing_poly.rs @@ -42,7 +42,12 @@ pub(crate) fn eval_vanishing_poly( ); } // Evaluate the STARK constraints related to the cross-table lookups. - eval_cross_table_lookup_checks::(vars, ctl_vars, consumer); + eval_cross_table_lookup_checks::( + vars, + ctl_vars, + consumer, + stark.constraint_degree(), + ); } /// Circuit version of `eval_vanishing_poly`. @@ -66,5 +71,11 @@ pub(crate) fn eval_vanishing_poly_circuit( eval_ext_lookups_circuit::(builder, stark, vars, lookup_vars, consumer); } // Evaluate all of the STARK's constraints related to the cross-table lookups. - eval_cross_table_lookup_checks_circuit::(builder, vars, ctl_vars, consumer); + eval_cross_table_lookup_checks_circuit::( + builder, + vars, + ctl_vars, + consumer, + stark.constraint_degree(), + ); } diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index b69cd9274b..9eb12100de 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -16,7 +16,8 @@ use crate::constraint_consumer::ConstraintConsumer; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{ - verify_cross_table_lookups, CtlCheckVars, GrandProductChallenge, GrandProductChallengeSet, + num_ctl_helper_columns_by_table, verify_cross_table_lookups, CtlCheckVars, + GrandProductChallenge, GrandProductChallengeSet, }; use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::LookupCheckVars; @@ -56,11 +57,17 @@ where cross_table_lookups, } = all_stark; + let num_ctl_helper_cols = num_ctl_helper_columns_by_table( + cross_table_lookups, + all_stark.arithmetic_stark.constraint_degree(), + ); + let ctl_vars_per_table = CtlCheckVars::from_proofs( &all_proof.stark_proofs, cross_table_lookups, &ctl_challenges, &num_lookup_columns, + &num_ctl_helper_cols, ); verify_stark_proof_with_challenges( @@ -300,7 +307,12 @@ pub(crate) fn verify_stark_proof_with_challenges< config: &StarkConfig, ) -> Result<()> { log::debug!("Checking proof: {}", type_name::()); - validate_proof_shape(stark, proof, config, ctl_vars.len())?; + let num_ctl_polys = ctl_vars + .iter() + .map(|ctl| ctl.helper_columns.len()) + .sum::(); + let num_ctl_z_polys = ctl_vars.len(); + validate_proof_shape(stark, proof, config, num_ctl_polys, num_ctl_z_polys)?; let StarkOpeningSet { local_values, next_values, @@ -374,11 +386,16 @@ pub(crate) fn verify_stark_proof_with_challenges< proof.quotient_polys_cap.clone(), ]; + let num_ctl_zs = ctl_vars + .iter() + .map(|ctl| ctl.helper_columns.len()) + .collect::>(); verify_fri_proof::( &stark.fri_instance( challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), - ctl_zs_first.len(), + num_ctl_polys, + num_ctl_zs, config, ), &proof.openings.to_fri_openings(), @@ -395,6 +412,7 @@ fn validate_proof_shape( stark: &S, proof: &StarkProof, config: &StarkConfig, + num_ctl_helpers: usize, num_ctl_zs: usize, ) -> anyhow::Result<()> where @@ -424,7 +442,8 @@ where let degree_bits = proof.recover_degree_bits(config); let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; - let num_auxiliary = num_ctl_zs + stark.num_lookup_helper_columns(config); + + let num_auxiliary = num_ctl_helpers + stark.num_lookup_helper_columns(config) + num_ctl_zs; ensure!(trace_cap.height() == cap_height); ensure!(auxiliary_polys_cap.height() == cap_height); From 85524cfacc4f7dcebd8f765108af643f24e5a7ff Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 10 Jan 2024 09:06:34 +0100 Subject: [PATCH 086/175] Intra doc link --- evm/src/curve_pairings.rs | 4 ++-- evm/src/lookup.rs | 2 +- plonky2/src/gates/arithmetic_base.rs | 2 +- plonky2/src/gates/arithmetic_extension.rs | 2 +- plonky2/src/gates/multiplication_extension.rs | 2 +- plonky2/src/iop/target.rs | 6 +++--- plonky2/src/plonk/circuit_builder.rs | 5 +++-- plonky2/src/plonk/prover.rs | 2 +- plonky2/src/plonk/vanishing_poly.rs | 4 ++-- 9 files changed, 15 insertions(+), 14 deletions(-) diff --git a/evm/src/curve_pairings.rs b/evm/src/curve_pairings.rs index 1380c96e9d..71188a216f 100644 --- a/evm/src/curve_pairings.rs +++ b/evm/src/curve_pairings.rs @@ -63,7 +63,7 @@ where } /// Standard addition formula for elliptic curves, restricted to the cases -/// https://en.wikipedia.org/wiki/Elliptic_curve#Algebraic_interpretation +/// impl Add for Curve { type Output = Self; @@ -201,7 +201,7 @@ pub(crate) fn bn_tate(p: Curve, q: Curve>) -> Fp12 { } /// Standard code for miller loop, can be found on page 99 at this url: -/// https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf#page=107 +/// /// where BN_EXP is a hardcoding of the array of Booleans that the loop traverses pub(crate) fn bn_miller_loop(p: Curve, q: Curve>) -> Fp12 { let mut r = p; diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index 8bf0c8f61b..5d5d1a7b33 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -40,7 +40,7 @@ impl Lookup { } } -/// logUp protocol from https://ia.cr/2022/1530 +/// logUp protocol from /// Compute the helper columns for the lookup argument. /// Given columns `f0,...,fk` and a column `t`, such that `∪fi ⊆ t`, and challenges `x`, /// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`, diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index f47bd18fd1..e06c58aff3 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -20,7 +20,7 @@ use crate::plonk::vars::{ }; use crate::util::serialization::{Buffer, IoResult, Read, Write}; -/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config +/// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config /// supports enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct ArithmeticGate { diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 084a14d6f0..37482a5d92 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -16,7 +16,7 @@ use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::util::serialization::{Buffer, IoResult, Read, Write}; -/// A gate which can perform a weighted multiply-add, i.e. `result = c0 x y + c1 z`. If the config +/// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config /// supports enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct ArithmeticExtensionGate { diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 11fd88ebad..1d85817ade 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -16,7 +16,7 @@ use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::util::serialization::{Buffer, IoResult, Read, Write}; -/// A gate which can perform a weighted multiplication, i.e. `result = c0 x y`. If the config +/// A gate which can perform a weighted multiplication, i.e. `result = c0.x.y`. If the config /// supports enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct MulExtensionGate { diff --git a/plonky2/src/iop/target.rs b/plonky2/src/iop/target.rs index f12257145e..b1fe9bf9b6 100644 --- a/plonky2/src/iop/target.rs +++ b/plonky2/src/iop/target.rs @@ -14,11 +14,11 @@ use crate::plonk::circuit_data::CircuitConfig; /// /// When generating a proof for a given circuit, the prover will "set" the values of some /// (or all) targets, so that they satisfy the circuit constraints. This is done through -/// the [`PartialWitness`] interface. +/// the [PartialWitness](crate::iop::witness::PartialWitness) interface. /// /// There are different "variants" of the `Target` type, namely [`ExtensionTarget`], -/// [`ExtensionAlgebraTarget`], but the `Target` type is the default one for most circuits -/// verifying some simple statement. +/// [ExtensionAlgebraTarget](crate::iop::ext_target::ExtensionAlgebraTarget), but the `Target` +/// type is the default one for most circuits verifying some simple statement. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] pub enum Target { /// A target that has a fixed location in the witness (seen as a `degree x num_wires` grid). diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index be905cc273..227af85bba 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -181,7 +181,8 @@ pub struct CircuitBuilder, const D: usize> { /// List of constant generators used to fill the constant wires. constant_generators: Vec>, - /// Rows for each LUT: LookupWire contains: first `LookupGate`, first [`LookupTableGate`], last `LookupTableGate`. + /// Rows for each LUT: [`LookupWire`] contains: first [`LookupGate`], first and last + /// [LookupTableGate](crate::gates::lookup_table::LookupTableGate). lookup_rows: Vec, /// For each LUT index, vector of `(looking_in, looking_out)` pairs. @@ -1007,7 +1008,7 @@ impl, const D: usize> CircuitBuilder { /// In PLONK's permutation argument, there's a slight chance of division by zero. We can /// mitigate this by randomizing some unused witness elements, so if proving fails with /// division by zero, the next attempt will have an (almost) independent chance of success. - /// See https://github.com/0xPolygonZero/plonky2/issues/456 + /// See . fn randomize_unused_pi_wires(&mut self, pi_gate: usize) { for wire in PublicInputGate::wires_public_inputs_hash().end..self.config.num_wires { self.add_simple_generator(RandomValueGenerator { diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index e0c8d727ff..153610dcb6 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -443,7 +443,7 @@ fn wires_permutation_partial_products_and_zs< } /// Computes lookup polynomials for a given challenge. -/// The polynomials hold the value of RE, Sum and Ldc of the Tip5 paper (https://eprint.iacr.org/2023/107.pdf). To reduce their +/// The polynomials hold the value of RE, Sum and Ldc of the Tip5 paper (). To reduce their /// numbers, we batch multiple slots in a single polynomial. Since RE only involves degree one constraints, we can batch /// all the slots of a row. For Sum and Ldc, batching increases the constraint degree, so we bound the number of /// partial polynomials according to `max_quotient_degree_factor`. diff --git a/plonky2/src/plonk/vanishing_poly.rs b/plonky2/src/plonk/vanishing_poly.rs index 2c53efcfd3..e3ddcf5b88 100644 --- a/plonky2/src/plonk/vanishing_poly.rs +++ b/plonky2/src/plonk/vanishing_poly.rs @@ -323,8 +323,8 @@ pub(crate) fn eval_vanishing_poly_base_batch, const res_batch } -/// Evaluates all lookup constraints, based on the logarithmic derivatives paper (https://eprint.iacr.org/2022/1530.pdf), -/// following the Tip5 paper's implementation (https://eprint.iacr.org/2023/107.pdf). +/// Evaluates all lookup constraints, based on the logarithmic derivatives paper (), +/// following the Tip5 paper's implementation (). /// /// There are three polynomials to check: /// - RE ensures the well formation of lookup tables; From c329b3681ce2f12621bb3af7278dc05c705acba2 Mon Sep 17 00:00:00 2001 From: yanziseeker <153156292+AdventureSeeker987@users.noreply.github.com> Date: Wed, 10 Jan 2024 18:40:39 +0800 Subject: [PATCH 087/175] chore(evm,field,plonky2):fix typos (#1454) --- evm/src/cpu/contextops.rs | 2 +- evm/src/cpu/kernel/stack/permutations.rs | 2 +- evm/src/util.rs | 4 ++-- field/src/extension/mod.rs | 2 +- plonky2/src/gates/selectors.rs | 2 +- plonky2/src/hash/poseidon_goldilocks.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 0afc070f09..77debc759c 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -105,7 +105,7 @@ fn eval_packed_get( } /// Circuit version of `eval_packed_get`. -/// Evalutes constraints for GET_CONTEXT. +/// Evaluates constraints for GET_CONTEXT. fn eval_ext_circuit_get, const D: usize>( builder: &mut CircuitBuilder, lv: &CpuColumnsView>, diff --git a/evm/src/cpu/kernel/stack/permutations.rs b/evm/src/cpu/kernel/stack/permutations.rs index 50be605d3a..69d5f2e672 100644 --- a/evm/src/cpu/kernel/stack/permutations.rs +++ b/evm/src/cpu/kernel/stack/permutations.rs @@ -59,7 +59,7 @@ fn apply_perm(permutation: Vec>, mut lst: Vec(lst_a: &[T], lst_b: &[T]) -> Vec> { - // We should check to ensure that A and B are indeed rearrangments of each other. + // We should check to ensure that A and B are indeed rearrangements of each other. assert!(is_permutation(lst_a, lst_b)); let n = lst_a.len(); diff --git a/evm/src/util.rs b/evm/src/util.rs index 3d9564b5ed..f4d80859e7 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -75,12 +75,12 @@ pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } -/// Converts a `U256` to a `u8`, erroring in case of overlow instead of panicking. +/// Converts a `U256` to a `u8`, erroring in case of overflow instead of panicking. pub(crate) fn u256_to_u8(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } -/// Converts a `U256` to a `bool`, erroring in case of overlow instead of panicking. +/// Converts a `U256` to a `bool`, erroring in case of overflow instead of panicking. pub(crate) fn u256_to_bool(u256: U256) -> Result { if u256 == U256::zero() { Ok(false) diff --git a/field/src/extension/mod.rs b/field/src/extension/mod.rs index bbbaca25e5..3586055e3f 100644 --- a/field/src/extension/mod.rs +++ b/field/src/extension/mod.rs @@ -15,7 +15,7 @@ pub trait OEF: FieldExtension { // Element W of BaseField, such that `X^d - W` is irreducible over BaseField. const W: Self::BaseField; - // Element of BaseField such that DTH_ROOT^D == 1. Implementors + // Element of BaseField such that DTH_ROOT^D == 1. Implementers // should set this to W^((p - 1)/D), where W is as above and p is // the order of the BaseField. const DTH_ROOT: Self::BaseField; diff --git a/plonky2/src/gates/selectors.rs b/plonky2/src/gates/selectors.rs index 1018ba755b..be9a9da84a 100644 --- a/plonky2/src/gates/selectors.rs +++ b/plonky2/src/gates/selectors.rs @@ -40,7 +40,7 @@ pub enum LookupSelectors { } /// Returns selector polynomials for each LUT. We have two constraint domains (remember that gates are stored upside down): -/// - [last_lut_row, first_lut_row] (Sum and RE transition contraints), +/// - [last_lut_row, first_lut_row] (Sum and RE transition constraints), /// - [last_lu_row, last_lut_row - 1] (LDC column transition constraints). /// We also add two more: /// - {first_lut_row + 1} where we check the initial values of sum and RE (which are 0), diff --git a/plonky2/src/hash/poseidon_goldilocks.rs b/plonky2/src/hash/poseidon_goldilocks.rs index 3e089e64ca..1fd5af1354 100644 --- a/plonky2/src/hash/poseidon_goldilocks.rs +++ b/plonky2/src/hash/poseidon_goldilocks.rs @@ -323,7 +323,7 @@ mod poseidon12_mds { let (u8, u9, u10) = fft4_real([s2, s5, s8, s11]); // This where the multiplication in frequency domain is done. More precisely, and with - // the appropriate permuations in between, the sequence of + // the appropriate permutations in between, the sequence of // 3-point FFTs --> multiplication by twiddle factors --> Hadamard multiplication --> // 3 point iFFTs --> multiplication by (inverse) twiddle factors // is "squashed" into one step composed of the functions "block1", "block2" and "block3". From 0bf9cd2f868303abb67e2ff10ab0e7802d054ae3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 10 Jan 2024 13:24:16 +0100 Subject: [PATCH 088/175] Use current context in ecrecover (#1456) --- .../asm/curve/bn254/field_arithmetic/util.asm | 8 ++-- .../kernel/asm/curve/secp256k1/ecrecover.asm | 4 +- .../asm/curve/secp256k1/precomputation.asm | 38 +++++++++---------- evm/src/memory/segments.rs | 4 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm index 6dbddddcea..86b179ba9e 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm @@ -1,7 +1,7 @@ // Load a single value from bn254 pairings memory. %macro mload_bn254_pairing // stack: offset - %mload_current(@SEGMENT_KERNEL_BN_PAIRING) + %mload_current(@SEGMENT_BN_PAIRING) // stack: value %endmacro @@ -9,14 +9,14 @@ // stack: PUSH $offset // stack: offset - %mload_current(@SEGMENT_KERNEL_BN_PAIRING) + %mload_current(@SEGMENT_BN_PAIRING) // stack: value %endmacro // Store a single value to bn254 pairings memory. %macro mstore_bn254_pairing // stack: offset, value - %mstore_current(@SEGMENT_KERNEL_BN_PAIRING) + %mstore_current(@SEGMENT_BN_PAIRING) // stack: %endmacro @@ -24,7 +24,7 @@ // stack: value PUSH $offset // stack: offset, value - %mstore_current(@SEGMENT_KERNEL_BN_PAIRING) + %mstore_current(@SEGMENT_BN_PAIRING) // stack: %endmacro diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm index c84536d807..cb07bf47c6 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm @@ -87,9 +87,9 @@ ecdsa_after_precompute_loop: %mul_const(2) ADD %mul_const(2) ADD %mul_const(2) ADD %stack (index, i, accx, accy, a0, a1, b0, b1, retdest) -> (index, index, i, accx, accy, a0, a1, b0, b1, retdest) %mul_const(2) %add_const(1) - %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mload_current(@SEGMENT_ECDSA_TABLE) SWAP1 %mul_const(2) - %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mload_current(@SEGMENT_ECDSA_TABLE) %stack (Px, Py, i, accx, accy, a0, a1, b0, b1, retdest) -> (Px, Py, accx, accy, ecdsa_after_precompute_loop_contd, i, a0, a1, b0, b1, retdest) %jump(secp_add_valid_points) ecdsa_after_precompute_loop_contd: diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm index 3cea031556..b6bed1b0a9 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm @@ -1,27 +1,27 @@ // Initial stack: Gneg, Qneg, Qx, Qy, retdest -// Compute a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4 and store its x-coordinate at location `2*(8a+4b+2c+d)` and its y-coordinate at location `2*(8a+4b+2c+d)+1` in the SEGMENT_KERNEL_ECDSA_TABLE segment. +// Compute a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4 and store its x-coordinate at location `2*(8a+4b+2c+d)` and its y-coordinate at location `2*(8a+4b+2c+d)+1` in the SEGMENT_ECDSA_TABLE segment. global secp_precompute_table: // First store G, ± phi(G), G ± phi(G) // Use Gneg for the ±, e.g., ±phi(G) is computed as `Gneg * (-phi(G)) + (1-Gneg)*phi(G)` (note only the y-coordinate needs to be filtered). // stack: Gneg, Qneg, Qx, Qy, retdest PUSH 32670510020758816978083085130507043184471273380659243275938904335757337482424 PUSH 17 PUSH 55066263022277343669578718895168534326250603453777594175500187360389116729240 PUSH 16 - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) DUP1 DUP1 %mul_const(32670510020758816978083085130507043184471273380659243275938904335757337482424) SWAP1 PUSH 1 SUB %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) ADD PUSH 9 PUSH 85340279321737800624759429340272274763154997815782306132637707972559913914315 PUSH 8 - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) DUP1 DUP1 %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) SWAP1 PUSH 1 SUB %mul_const(100652675408719987021357910538015346127426077519185866739835120963490438734674) ADD PUSH 25 - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) DUP1 %mul_const(91177636130617246552803821781935006617134368061721227770777272682868638699771) SWAP1 PUSH 1 SUB %mul_const(66837770201594535779099350687042404727408598709762866365333192677982385899440) ADD PUSH 24 - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) // Then store Q, ±phi(Q), Q ± phi(Q) %stack (Qneg, Qx, Qy, retdest) -> (4, Qx, 5, Qy, Qx, @SECP_BASE, Qneg, Qx, Qy, retdest) - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) // stack: Qx, @SECP_BASE, Qx, Qy, retdest PUSH @SECP_GLV_BETA MULMOD %stack (betaQx, Qneg, Qx, Qy, retdest) -> (Qneg, Qy, Qneg, betaQx, Qx, Qy, retdest) @@ -29,42 +29,42 @@ global secp_precompute_table: // stack: 1-Qneg, Qneg*Qy, betaQx, Qx, Qy, retdest DUP5 PUSH @SECP_BASE SUB MUL ADD %stack (selectQy, betaQx, Qx, Qy, retdest) -> (2, betaQx, 3, selectQy, betaQx, selectQy, Qx, Qy, precompute_table_contd, retdest) - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) %jump(secp_add_valid_points_no_edge_case) precompute_table_contd: %stack (x, y, retdest) -> (6, x, 7, y, retdest) - %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) PUSH 2 // Use a loop to store a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4. precompute_table_loop: // stack: i, retdest - DUP1 %increment %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + DUP1 %increment %mload_current(@SEGMENT_ECDSA_TABLE) %stack (y, i, retdest) -> (i, y, i, retdest) - %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + %mload_current(@SEGMENT_ECDSA_TABLE) PUSH precompute_table_loop_contd DUP3 DUP3 - PUSH 9 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) - PUSH 8 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + PUSH 9 %mload_current(@SEGMENT_ECDSA_TABLE) + PUSH 8 %mload_current(@SEGMENT_ECDSA_TABLE) // stack: Gx, Gy, x, y, precompute_table_loop_contd, x, y, i, retdest %jump(secp_add_valid_points) precompute_table_loop_contd: %stack (Rx, Ry, x, y, i, retdest) -> (i, 8, Rx, i, 9, Ry, x, y, i, retdest) - ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) DUP2 DUP2 - PUSH 17 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) - PUSH 16 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + PUSH 17 %mload_current(@SEGMENT_ECDSA_TABLE) + PUSH 16 %mload_current(@SEGMENT_ECDSA_TABLE) %stack (Gx, Gy, x, y, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd2, x, y, i, retdest) %jump(secp_add_valid_points) precompute_table_loop_contd2: %stack (Rx, Ry, x, y, i, retdest) -> (i, 16, Rx, i, 17, Ry, x, y, i, retdest) - ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) - PUSH 25 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) - PUSH 24 %mload_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) + PUSH 25 %mload_current(@SEGMENT_ECDSA_TABLE) + PUSH 24 %mload_current(@SEGMENT_ECDSA_TABLE) %stack (Gx, Gy, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd3, i, retdest) %jump(secp_add_valid_points) precompute_table_loop_contd3: %stack (Rx, Ry, i, retdest) -> (i, 24, Rx, i, 25, Ry, i, retdest) - ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) ADD %mstore_kernel(@SEGMENT_KERNEL_ECDSA_TABLE) + ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) %add_const(2) DUP1 %eq_const(8) %jumpi(precompute_table_end) %jump(precompute_table_loop) diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index ac90d2108e..6177d9962e 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -138,11 +138,11 @@ impl Segment { Segment::TrieData => "SEGMENT_TRIE_DATA", Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", - Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", + Segment::EcdsaTable => "SEGMENT_ECDSA_TABLE", Segment::BnWnafA => "SEGMENT_BN_WNAF_A", Segment::BnWnafB => "SEGMENT_BN_WNAF_B", Segment::BnTableQ => "SEGMENT_BN_TABLE_Q", - Segment::BnPairing => "SEGMENT_KERNEL_BN_PAIRING", + Segment::BnPairing => "SEGMENT_BN_PAIRING", Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES", Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS", Segment::SelfDestructList => "SEGMENT_SELFDESTRUCT_LIST", From 92aaa404da05bd51434cbea3f15415007ed8eb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Wed, 10 Jan 2024 13:55:09 +0100 Subject: [PATCH 089/175] Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 4 ++-- evm/src/generation/mod.rs | 2 +- evm/src/generation/prover_input.rs | 6 +++--- evm/src/generation/state.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index ed9bb06fbd..aed6c186c9 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -41,7 +41,7 @@ continue: proof_ok: // stack: i, ctx, final_pos, retdest - // We already know final pos is a jumpdest + // We already know final_pos is a jumpdest %stack (i, ctx, final_pos) -> (ctx, @SEGMENT_JUMPDEST_BITS, i) %build_address PUSH 1 @@ -99,7 +99,7 @@ code_bytes_to_skip: %endrep -// A proof attesting that jumpdest is a valid jump destinations is +// A proof attesting that jumpdest is a valid jump destination is // either 0 or an index 0 < i <= jumpdest - 32. // A proof is valid if: // - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 318c7d4f30..238011d8c4 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -364,7 +364,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( log::debug!("Simulating CPU for jumpdest analysis."); loop { - // skip jumdest table validations in simulations + // skip jumpdest table validations in simulations if state.registers.program_counter == KERNEL.global_labels["jumpdest_analysis"] { state.registers.program_counter = KERNEL.global_labels["jumpdest_analysis_end"] } diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b5541f1bcf..97177a8cfa 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -247,7 +247,8 @@ impl GenerationState { _ => Err(ProgramError::ProverInputError(InvalidInput)), } } - /// Return the next used jump addres + + /// Returns the next used jump address. fn run_next_jumpdest_table_address(&mut self) -> Result { let context = self.registers.context; let code_len = u256_to_usize(self.get_code_len()?.into()); @@ -299,7 +300,6 @@ impl GenerationState { let code = self.get_current_code()?; // We need to set the simulated jumpdest bits to one as otherwise // the simulation will fail. - // self.set_jumpdest_bits(&code); // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( @@ -383,7 +383,7 @@ impl GenerationState { /// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes /// and pushed bytes are PUSHXX and the address is in its range. It returns -/// a vector of even size containing proofs followed by their addresses +/// a vector of even size containing proofs followed by their addresses. fn get_proofs_and_jumpdests( code: &[u8], largest_address: usize, diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index f844c5ceb2..f15ab317e2 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -169,7 +169,7 @@ impl GenerationState { .collect() } - /// Clone everything but the traces + /// Clones everything but the traces. pub(crate) fn soft_clone(&self) -> GenerationState { Self { inputs: self.inputs.clone(), From ae4a720a746277c20136f20e8abe306fdf0d759a Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 10 Jan 2024 17:26:34 +0100 Subject: [PATCH 090/175] Address comments --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 8 ++++---- evm/src/generation/mod.rs | 6 +++--- evm/src/generation/prover_input.rs | 4 ++++ evm/src/util.rs | 10 +++++----- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index aed6c186c9..c197fb6a3a 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -2,7 +2,7 @@ // for the given context's code. // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) -global verify_path_and_write_table: +global verify_path_and_write_jumpdest_table: loop: // stack: i, ctx, final_pos, retdest DUP3 DUP2 EQ // i == final_pos @@ -10,7 +10,7 @@ loop: DUP3 DUP2 GT // i > final_pos %jumpi(proof_not_ok) - // stack: i, ctx, code_len, retdest + // stack: i, ctx, final_pos, retdest %stack (i, ctx) -> (ctx, i, i, ctx) ADD // combine context and offset to make an address (SEGMENT_CODE == 0) MLOAD_GENERAL @@ -124,7 +124,7 @@ global write_table_if_jumpdest: SWAP2 DUP1 // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest ISZERO - %jumpi(verify_path_and_write_table) + %jumpi(verify_path_and_write_jumpdest_table) // stack: proof_prefix_addr, ctx, jumpdest, retdest @@ -266,7 +266,7 @@ global write_table_if_jumpdest: %add_const(32) // check the remaining path - %jump(verify_path_and_write_table) + %jump(verify_path_and_write_jumpdest_table) return: // stack: proof_prefix_addr, jumpdest, ctx, retdest %pop3 diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 238011d8c4..e14de9b98a 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -379,7 +379,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( state.registers.program_counter, ))) else { log::debug!( - "Simulated CPU halted after {} cycles", + "Simulated CPU for jumpdest analysis halted after {} cycles", state.traces.clock() - initial_clock ); return Some(jumpdest_addresses); @@ -395,7 +395,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( // Avoid deeper calls to abort let Ok(jumpdest) = u256_to_usize(state.registers.stack_top) else { log::debug!( - "Simulated CPU halted after {} cycles", + "Simulated CPU for jumpdest analysis halted after {} cycles", state.traces.clock() - initial_clock ); return Some(jumpdest_addresses); @@ -416,7 +416,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } if halt || transition(state).is_err() { log::debug!( - "Simulated CPU halted after {} cycles", + "Simulated CPU for jumpdest analysis halted after {} cycles", state.traces.clock() - initial_clock ); return Some(jumpdest_addresses); diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 97177a8cfa..ccec9c4509 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -240,6 +240,7 @@ impl GenerationState { } } + /// Generate the either the next used jump address or the the proof for the last jump address. fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { match input_fn.0[1].as_str() { "next_address" => self.run_next_jumpdest_table_address(), @@ -293,6 +294,7 @@ impl GenerationState { } impl GenerationState { + /// Simulate the user's code and store all the jump addresses with their respective contexts. fn generate_jumpdest_proofs(&mut self) -> Result<(), ProgramError> { let checkpoint = self.checkpoint(); let memory = self.memory.clone(); @@ -322,6 +324,8 @@ impl GenerationState { Ok(()) } + /// Given a HashMap containing the contexts and the jumpdest addresses, compute their respective proofs, + /// by calling `get_proofs_and_jumpdests` pub(crate) fn set_proofs_and_jumpdests( &mut self, jumpdest_table: HashMap>, diff --git a/evm/src/util.rs b/evm/src/util.rs index ee2d960703..f4d80859e7 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -70,17 +70,17 @@ pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> )) } -/// Safe conversion from U256 to u8, which errors in case of overflow instead of panicking. -pub(crate) fn u256_to_u8(u256: U256) -> Result { +/// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. +pub(crate) fn u256_to_usize(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } -/// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. -pub(crate) fn u256_to_usize(u256: U256) -> Result { +/// Converts a `U256` to a `u8`, erroring in case of overflow instead of panicking. +pub(crate) fn u256_to_u8(u256: U256) -> Result { u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) } -/// Converts a `U256` to a `bool`, erroring in case of overlow instead of panicking. +/// Converts a `U256` to a `bool`, erroring in case of overflow instead of panicking. pub(crate) fn u256_to_bool(u256: U256) -> Result { if u256 == U256::zero() { Ok(false) From 99a1eb5c85cb8d6d094b916ae9db9c0df4c4ce72 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Wed, 10 Jan 2024 17:58:41 +0100 Subject: [PATCH 091/175] Missing review comments --- evm/src/generation/prover_input.rs | 16 +++++++++------- evm/src/generation/state.rs | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index ccec9c4509..c8b78e6298 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -312,7 +312,6 @@ impl GenerationState { self.jumpdest_proofs = Some(HashMap::new()); return Ok(()); }; - log::debug!("jumpdest_table = {:?}", jumpdest_table); // Return to the state before starting the simulation self.rollback(checkpoint); @@ -383,7 +382,7 @@ impl GenerationState { } } -/// For all address in `jumpdest_table`, each bounded by larges_address, +/// For all address in `jumpdest_table`, each bounded by `largest_address`, /// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes /// and pushed bytes are PUSHXX and the address is in its range. It returns @@ -403,11 +402,12 @@ fn get_proofs_and_jumpdests( .iter() .enumerate() .fold(true, |acc, (prefix_pos, &byte)| { - acc && (byte > PUSH32_OPCODE - || (prefix_start + prefix_pos) as i32 - + (byte as i32 - PUSH1_OPCODE as i32) - + 1 - < pos as i32) + let cond1 = byte > PUSH32_OPCODE; + let cond2 = (prefix_start + prefix_pos) as i32 + + (byte as i32 - PUSH1_OPCODE as i32) + + 1 + < pos as i32; + acc && (cond1 || cond2) }) } else { false @@ -425,6 +425,8 @@ fn get_proofs_and_jumpdests( proofs } +/// An iterator over the EVM code contained in `code`, which skips the bytes +/// that are the arguments of a PUSHXX opcode. struct CodeIterator<'a> { code: &'a [u8], pos: usize, diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index f15ab317e2..ddc2f35929 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -51,6 +51,10 @@ pub(crate) struct GenerationState { /// Pointers, within the `TrieData` segment, of the three MPTs. pub(crate) trie_root_ptrs: TrieRootPtrs, + /// A hash map where the key is a context in the user's code and the value is the set of + /// jump destinations with its corresponding "proof". A "proof" for a jump destination is + /// either 0 or an address i > 32 in the code (not necessarily pointing to an opcode) such that + /// for every j in [i, i+32] it holds that code[j] < 0x7f - j + i. pub(crate) jumpdest_proofs: Option>>, } From 606732a8b6aff5d4618667605479a812f6c57b17 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:58:09 +0100 Subject: [PATCH 092/175] Free up some CPU cycles (#1457) * Free up some cycles * Comments --- evm/src/cpu/kernel/asm/bignum/cmp.asm | 14 ++++++++------ evm/src/cpu/kernel/asm/bignum/shr.asm | 6 +++--- evm/src/cpu/kernel/asm/bloom_filter.asm | 7 ++++--- evm/src/cpu/kernel/asm/core/gas.asm | 2 +- .../cpu/kernel/asm/curve/secp256k1/ecrecover.asm | 5 +++-- evm/src/cpu/kernel/asm/exp.asm | 2 +- .../cpu/kernel/asm/hash/blake2/compression.asm | 5 +++-- evm/src/cpu/kernel/asm/hash/sha2/main.asm | 2 +- evm/src/cpu/kernel/asm/hash/sha2/ops.asm | 2 +- .../cpu/kernel/asm/hash/sha2/write_length.asm | 9 +++++---- evm/src/cpu/kernel/asm/journal/journal.asm | 3 ++- evm/src/cpu/kernel/asm/journal/log.asm | 3 ++- evm/src/cpu/kernel/asm/memory/metadata.asm | 7 ++++--- evm/src/cpu/kernel/asm/rlp/encode.asm | 10 ++++++---- evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm | 8 ++++++++ evm/src/cpu/kernel/asm/rlp/num_bytes.asm | 9 ++++++--- evm/src/cpu/kernel/asm/transactions/router.asm | 6 ++---- evm/src/cpu/kernel/asm/transactions/type_0.asm | 16 ++++++++-------- evm/src/cpu/kernel/asm/transactions/type_1.asm | 10 +++++----- evm/src/cpu/kernel/asm/transactions/type_2.asm | 12 ++++++------ evm/src/cpu/kernel/asm/util/basic_macros.asm | 11 +++++++---- evm/src/cpu/kernel/asm/util/math.asm | 4 ++-- 22 files changed, 88 insertions(+), 65 deletions(-) diff --git a/evm/src/cpu/kernel/asm/bignum/cmp.asm b/evm/src/cpu/kernel/asm/bignum/cmp.asm index d5abd238fe..cf6b547f4b 100644 --- a/evm/src/cpu/kernel/asm/bignum/cmp.asm +++ b/evm/src/cpu/kernel/asm/bignum/cmp.asm @@ -12,17 +12,19 @@ global cmp_bignum: // stack: len, a_start_loc, b_start_loc, retdest SWAP1 // stack: a_start_loc, len, b_start_loc, retdest - DUP2 - // stack: len, a_start_loc, len, b_start_loc, retdest + PUSH 1 + DUP3 + SUB + // stack: len-1, a_start_loc, len, b_start_loc, retdest ADD - %decrement // stack: a_end_loc, len, b_start_loc, retdest SWAP2 // stack: b_start_loc, len, a_end_loc, retdest - DUP2 - // stack: len, b_start_loc, len, a_end_loc, retdest + PUSH 1 + DUP3 + SUB + // stack: len-1, b_start_loc, len, a_end_loc, retdest ADD - %decrement // stack: b_end_loc, len, a_end_loc, retdest %stack (b, l, a) -> (l, a, b) // stack: len, a_end_loc, b_end_loc, retdest diff --git a/evm/src/cpu/kernel/asm/bignum/shr.asm b/evm/src/cpu/kernel/asm/bignum/shr.asm index adf9577084..16d8403c77 100644 --- a/evm/src/cpu/kernel/asm/bignum/shr.asm +++ b/evm/src/cpu/kernel/asm/bignum/shr.asm @@ -42,9 +42,9 @@ shr_loop: // stack: i, carry << 127 | a[i] >> 1, i, new_carry, start_loc, retdest %mstore_current_general // stack: i, new_carry, start_loc, retdest - DUP1 - // stack: i, i, new_carry, start_loc, retdest - %decrement + PUSH 1 + DUP2 + SUB // stack: i-1, i, new_carry, start_loc, retdest SWAP1 // stack: i, i-1, new_carry, start_loc, retdest diff --git a/evm/src/cpu/kernel/asm/bloom_filter.asm b/evm/src/cpu/kernel/asm/bloom_filter.asm index 0362d710d0..8fa84a80f5 100644 --- a/evm/src/cpu/kernel/asm/bloom_filter.asm +++ b/evm/src/cpu/kernel/asm/bloom_filter.asm @@ -142,10 +142,11 @@ logs_bloom_end: // Also updates the block bloom filter. %macro bloom_write_bit // stack: byte_index, byte_bit_index - DUP2 - // stack: byte_bit_index, byte_index, byte_bit_index + PUSH 1 + DUP3 + // stack: byte_bit_index, 1, byte_index, byte_bit_index PUSH 7 SUB - PUSH 1 SWAP1 SHL + SHL // Updates the current txn bloom filter. SWAP2 POP DUP1 %mload_kernel(@SEGMENT_TXN_BLOOM) diff --git a/evm/src/cpu/kernel/asm/core/gas.asm b/evm/src/cpu/kernel/asm/core/gas.asm index d5e4e9bb74..2e16c373e3 100644 --- a/evm/src/cpu/kernel/asm/core/gas.asm +++ b/evm/src/cpu/kernel/asm/core/gas.asm @@ -122,7 +122,7 @@ global sys_gasprice: // L(n) = n - floor(n / 64) %macro all_but_one_64th // stack: n - DUP1 %div_const(64) + DUP1 %shr_const(6) // stack: floor(n / 64), n SWAP1 SUB // stack: n - floor(n / 64) diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm index cb07bf47c6..c11031004f 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm @@ -97,8 +97,9 @@ ecdsa_after_precompute_loop_contd: ISZERO %jumpi(ecdsa_after_precompute_loop_end) %jump(secp_double) ecdsa_after_precompute_loop_contd2: - %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, a0, a1, b0, b1, retdest) - %decrement %jump(ecdsa_after_precompute_loop) + %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, 1, accx, accy, a0, a1, b0, b1, retdest) + SUB // i - 1 + %jump(ecdsa_after_precompute_loop) ecdsa_after_precompute_loop_end: // Check that the public key is not the point at infinity. See https://github.com/ethereum/eth-keys/pull/76 for discussion. DUP2 DUP2 ISZERO SWAP1 ISZERO MUL %jumpi(pk_is_infinity) diff --git a/evm/src/cpu/kernel/asm/exp.asm b/evm/src/cpu/kernel/asm/exp.asm index 5dd6736655..4b798e841c 100644 --- a/evm/src/cpu/kernel/asm/exp.asm +++ b/evm/src/cpu/kernel/asm/exp.asm @@ -86,7 +86,7 @@ sys_exp_gas_loop_enter: // stack: e >> shift, shift, x, e, return_info %jumpi(sys_exp_gas_loop) // stack: shift_bits, x, e, return_info - %div_const(8) + %shr_const(3) // stack: byte_size_of_e := shift_bits / 8, x, e, return_info %mul_const(@GAS_EXPBYTE) %add_const(@GAS_EXP) diff --git a/evm/src/cpu/kernel/asm/hash/blake2/compression.asm b/evm/src/cpu/kernel/asm/hash/blake2/compression.asm index 454e51280d..ba9ffc1343 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/compression.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/compression.asm @@ -21,10 +21,11 @@ compression_loop: // stack: addr, cur_block, retdest POP // stack: cur_block, retdest + PUSH 1 PUSH 0 %mload_current_general - // stack: num_blocks, cur_block, retdest - %decrement + // stack: num_blocks, 1, cur_block, retdest + SUB // stack: num_blocks - 1, cur_block, retdest DUP2 // stack: cur_block, num_blocks - 1, cur_block, retdest diff --git a/evm/src/cpu/kernel/asm/hash/sha2/main.asm b/evm/src/cpu/kernel/asm/hash/sha2/main.asm index b311262dbd..81ec391293 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/main.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/main.asm @@ -31,7 +31,7 @@ global sha2_pad: DUP1 // stack: num_bytes, num_bytes, retdest %add_const(8) - %div_const(64) + %shr_const(6) %increment // stack: num_blocks = (num_bytes+8)//64 + 1, num_bytes, retdest diff --git a/evm/src/cpu/kernel/asm/hash/sha2/ops.asm b/evm/src/cpu/kernel/asm/hash/sha2/ops.asm index 6a4c5e3b77..d50e5c9a89 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/ops.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/ops.asm @@ -34,7 +34,7 @@ // stack: rotr(x, 18), x, rotr(x, 7) SWAP1 // stack: x, rotr(x, 18), rotr(x, 7) - %div_const(8) // equivalent to %shr_const(3) + %shr_const(3) // stack: shr(x, 3), rotr(x, 18), rotr(x, 7) XOR XOR diff --git a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm index 438875fb8b..c412a666ff 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm @@ -17,11 +17,12 @@ %decrement SWAP1 // stack: length >> (8 * i), last_addr - i - 2 - %div_const(256) // equivalent to %shr_const(8) + %shr_const(8) // stack: length >> (8 * (i + 1)), last_addr - i - 2 - DUP1 - // stack: length >> (8 * (i + 1)), length >> (8 * (i + 1)), last_addr - i - 2 - %mod_const(256) + PUSH 256 + DUP2 + // stack: length >> (8 * (i + 1)), 256, length >> (8 * (i + 1)), last_addr - i - 2 + MOD // stack: (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 DUP3 // stack: last_addr - i - 2, (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 diff --git a/evm/src/cpu/kernel/asm/journal/journal.asm b/evm/src/cpu/kernel/asm/journal/journal.asm index a0e5502dc6..2715bc6b98 100644 --- a/evm/src/cpu/kernel/asm/journal/journal.asm +++ b/evm/src/cpu/kernel/asm/journal/journal.asm @@ -199,8 +199,9 @@ %endmacro %macro pop_checkpoint + PUSH 1 %mload_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) // stack: i - %decrement + SUB %mstore_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) %endmacro diff --git a/evm/src/cpu/kernel/asm/journal/log.asm b/evm/src/cpu/kernel/asm/journal/log.asm index 0b815faef6..e1794397b7 100644 --- a/evm/src/cpu/kernel/asm/journal/log.asm +++ b/evm/src/cpu/kernel/asm/journal/log.asm @@ -8,8 +8,9 @@ global revert_log: // stack: entry_type, ptr, retdest POP // First, reduce the number of logs. + PUSH 1 %mload_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - %decrement + SUB %mstore_global_metadata(@GLOBAL_METADATA_LOGS_LEN) // stack: ptr, retdest // Second, restore payload length. diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index dfbfb6460b..e69e292b64 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -359,7 +359,7 @@ zero_hash: // stack: num_bytes %add_const(31) // stack: 31 + num_bytes - %div_const(32) + %shr_const(5) // stack: (num_bytes + 31) / 32 %endmacro @@ -372,7 +372,7 @@ zero_hash: SWAP1 // stack: num_words, num_words * GAS_MEMORY %square - %div_const(512) + %shr_const(9) // stack: num_words^2 / 512, num_words * GAS_MEMORY ADD // stack: cost = num_words^2 / 512 + num_words * GAS_MEMORY @@ -422,8 +422,9 @@ zero_hash: %endmacro %macro decrement_call_depth + PUSH 1 %mload_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) - %decrement + SUB %mstore_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index ccaa9f616c..dcd2451582 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -84,8 +84,10 @@ global encode_rlp_multi_byte_string_prefix: %jumpi(encode_rlp_multi_byte_string_prefix_large) // Medium case; prefix is 0x80 + str_len. // stack: rlp_addr, str_len, retdest - DUP1 - SWAP2 %add_const(0x80) + PUSH 0x80 + DUP2 + // stack: rlp_addr, 0x80, rlp_addr, str_len, retdest + SWAP3 ADD // stack: prefix, rlp_addr, rlp_addr, retdest MSTORE_GENERAL // stack: rlp_addr, retdest @@ -178,7 +180,7 @@ global prepend_rlp_list_prefix: // If we got here, we have a small list, so we prepend 0xc0 + len at rlp_address 8. // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP3 %decrement // offset of prefix + PUSH 1 DUP4 SUB // offset of prefix DUP2 %add_const(0xc0) // stack: prefix_byte, start_rlp_addr-1, payload_len, end_rlp_addr, start_rlp_addr, retdest MSTORE_GENERAL @@ -198,7 +200,7 @@ prepend_rlp_list_prefix_big: DUP1 %num_bytes // stack: len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 - DUP5 %decrement // start_rlp_addr - 1 + PUSH 1 DUP6 SUB // start_rlp_addr - 1 SUB // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP2 %add_const(0xf7) DUP2 %swap_mstore // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm index 8196a452ab..d311a57ebc 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm @@ -85,6 +85,14 @@ encode_rlp_scalar_small: SWAP1 JUMP +// Convenience macro to call encode_rlp_scalar and return where we left off. +// It takes swapped inputs, i.e. `scalar, rlp_addr` instead of `rlp_addr, scalar`. +%macro encode_rlp_scalar_swapped_inputs + %stack (scalar, rlp_addr) -> (rlp_addr, scalar, %%after) + %jump(encode_rlp_scalar) +%%after: +%endmacro + // Convenience macro to call encode_rlp_scalar and return where we left off. %macro encode_rlp_scalar %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) diff --git a/evm/src/cpu/kernel/asm/rlp/num_bytes.asm b/evm/src/cpu/kernel/asm/rlp/num_bytes.asm index b2a1b9d620..de0a7ca966 100644 --- a/evm/src/cpu/kernel/asm/rlp/num_bytes.asm +++ b/evm/src/cpu/kernel/asm/rlp/num_bytes.asm @@ -5,8 +5,8 @@ global num_bytes: DUP1 ISZERO %jumpi(return_1) // Non-deterministically guess the number of bits PROVER_INPUT(num_bits) - %stack (num_bits, x) -> (num_bits, x, num_bits) - %decrement + %stack(num_bits, x) -> (num_bits, 1, x, num_bits) + SUB SHR // stack: 1, num_bits %assert_eq_const(1) @@ -17,7 +17,10 @@ global num_bytes: SWAP1 JUMP -return_1: POP PUSH 1 SWAP1 JUMP +return_1: + // stack: x, retdest + %stack(x, retdest) -> (retdest, 1) + JUMP // Convenience macro to call num_bytes and return where we left off. %macro num_bytes diff --git a/evm/src/cpu/kernel/asm/transactions/router.asm b/evm/src/cpu/kernel/asm/transactions/router.asm index 2ecccfe02b..edabfbc43a 100644 --- a/evm/src/cpu/kernel/asm/transactions/router.asm +++ b/evm/src/cpu/kernel/asm/transactions/router.asm @@ -5,10 +5,8 @@ global route_txn: // stack: txn_counter, num_nibbles, retdest // First load transaction data into memory, where it will be parsed. - PUSH read_txn_from_memory - SWAP2 SWAP1 - PUSH update_txn_trie - // stack: update_txn_trie, tx_counter, num_nibbles, read_txn_from_memory, retdest + %stack(txn_counter, num_nibbles) -> (update_txn_trie, txn_counter, num_nibbles, read_txn_from_memory) + // stack: update_txn_trie, txn_counter, num_nibbles, read_txn_from_memory, retdest %jump(read_rlp_to_memory) // At this point, the raw txn data is in memory. diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index 0cd22c10f2..a3f3bb0d25 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -61,7 +61,7 @@ process_v_new_style: %sub_const(35) DUP1 // stack: v - 35, v - 35, rlp_addr, retdest - %div_const(2) + %div2 // stack: chain_id, v - 35, rlp_addr, retdest %mstore_txn_field(@TXN_FIELD_CHAIN_ID) @@ -94,11 +94,11 @@ type_0_compute_signed_data: // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) @@ -108,12 +108,12 @@ type_0_compute_signed_data: %jump(after_to) zero_to: // stack: to, rlp_addr, rlp_addr_start, retdest - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. @@ -133,15 +133,15 @@ after_serializing_txn_data: // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest finish_rlp_list: diff --git a/evm/src/cpu/kernel/asm/transactions/type_1.asm b/evm/src/cpu/kernel/asm/transactions/type_1.asm index f9142bd4a4..e64a4aee03 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_1.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_1.asm @@ -48,15 +48,15 @@ type_1_compute_signed_data: // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) @@ -66,12 +66,12 @@ type_1_compute_signed_data: %jump(after_to) zero_to: // stack: to, rlp_addr, rlp_addr_start, retdest - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. diff --git a/evm/src/cpu/kernel/asm/transactions/type_2.asm b/evm/src/cpu/kernel/asm/transactions/type_2.asm index cd4f85e677..5074c57950 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_2.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_2.asm @@ -51,19 +51,19 @@ type_2_compute_signed_data: // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) @@ -73,12 +73,12 @@ type_2_compute_signed_data: %jump(after_to) zero_to: // stack: to, rlp_addr, rlp_start, retdest - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) - SWAP1 %encode_rlp_scalar + %encode_rlp_scalar_swapped_inputs // stack: rlp_addr, rlp_start, retdest // Encode txn data. diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index c331acb423..44d734a3a1 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -271,9 +271,9 @@ %macro ceil_div // stack: x, y - DUP2 - // stack: y, x, y - %decrement + PUSH 1 + DUP3 + SUB // y - 1 // stack: y - 1, x, y ADD DIV @@ -333,7 +333,10 @@ %endmacro %macro div2 - %div_const(2) + // stack: x + PUSH 1 + SHR + // stack: x >> 1 %endmacro %macro iseven diff --git a/evm/src/cpu/kernel/asm/util/math.asm b/evm/src/cpu/kernel/asm/util/math.asm index 98f7b0086d..4bdf690238 100644 --- a/evm/src/cpu/kernel/asm/util/math.asm +++ b/evm/src/cpu/kernel/asm/util/math.asm @@ -5,7 +5,7 @@ log2_floor_helper: ISZERO %jumpi(end) // stack: val, counter, retdest - %div_const(2) + %div2 // stack: val/2, counter, retdest SWAP1 %increment @@ -22,7 +22,7 @@ end: global log2_floor: // stack: val, retdest - %div_const(2) + %div2 // stack: val/2, retdest PUSH 0 // stack: 0, val/2, retdest From 6ef0a3c738d48a6ebda9dba267b203e31742a93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Thu, 11 Jan 2024 10:55:04 +0100 Subject: [PATCH 093/175] Apply suggestions from code review Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 6 +++--- evm/src/generation/prover_input.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index c197fb6a3a..267bf5284e 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -154,7 +154,7 @@ global write_table_if_jumpdest: // - (0xFF|X⁷)³² for the first 15 bytes // - (has_prefix => is_0_at_4 |X⁷)³² for the next 15 bytes // - (~has_prefix|X⁷)³² for the last byte - // Compute also ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~hash_prefix + // Compute also ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~has_prefix // for the second half but it takes less cycles if we do it. DUP2 %shl_const(3) NOT @@ -283,7 +283,7 @@ return: // addresses used during program execution within the current context. // For each jumpdest address we also non-deterministically guess // a proof, which is another address in the code such that -// is_jumpdest don't abort, when the proof is at the top of the stack +// is_jumpdest doesn't abort, when the proof is at the top of the stack // an the jumpdest address below. If that's the case we set the // corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. // @@ -297,7 +297,7 @@ global jumpdest_analysis: // If proof == 0 there are no more jump destinations to check POP // This is just a hook used for avoiding verification of the jumpdest -// table in another contexts. It is useful during proof generation, +// table in another context. It is useful during proof generation, // allowing the avoidance of table verification when simulating user code. global jumpdest_analysis_end: %pop2 diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index c8b78e6298..f3078239d8 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -240,7 +240,7 @@ impl GenerationState { } } - /// Generate the either the next used jump address or the the proof for the last jump address. + /// Generate either the next used jump address or the proof for the last jump address. fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { match input_fn.0[1].as_str() { "next_address" => self.run_next_jumpdest_table_address(), @@ -385,7 +385,7 @@ impl GenerationState { /// For all address in `jumpdest_table`, each bounded by `largest_address`, /// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes -/// and pushed bytes are PUSHXX and the address is in its range. It returns +/// and pushed bytes) are PUSHXX and the address is in its range. It returns /// a vector of even size containing proofs followed by their addresses. fn get_proofs_and_jumpdests( code: &[u8], From 233ddd4e0eed390a74f8fd912d6d2d5f9a80f168 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:05:57 +0100 Subject: [PATCH 094/175] Constrain syscall/exceptions filter to be boolean (#1458) --- evm/src/cpu/syscalls_exceptions.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index 501b114ff7..337a0a86ec 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -29,6 +29,12 @@ pub(crate) fn eval_packed( let filter_exception = lv.op.exception; let total_filter = filter_syscall + filter_exception; + // First, constrain filters to be boolean. + // Ensuring they are mutually exclusive is done in other modules + // through the `is_cpu_cycle` variable. + yield_constr.constraint(filter_syscall * (filter_syscall - P::ONES)); + yield_constr.constraint(filter_exception * (filter_exception - P::ONES)); + // If exception, ensure we are not in kernel mode yield_constr.constraint(filter_exception * lv.is_kernel_mode); @@ -132,6 +138,14 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let filter_exception = lv.op.exception; let total_filter = builder.add_extension(filter_syscall, filter_exception); + // First, constrain filters to be boolean. + // Ensuring they are mutually exclusive is done in other modules + // through the `is_cpu_cycle` variable. + let constr = builder.mul_sub_extension(filter_syscall, filter_syscall, filter_syscall); + yield_constr.constraint(builder, constr); + let constr = builder.mul_sub_extension(filter_exception, filter_exception, filter_exception); + yield_constr.constraint(builder, constr); + // Ensure that, if exception, we are not in kernel mode let constr = builder.mul_extension(filter_exception, lv.is_kernel_mode); yield_constr.constraint(builder, constr); From bead1d60a75d8ace1bdc03d05d1c0f8e078226a5 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 11 Jan 2024 13:32:22 +0100 Subject: [PATCH 095/175] Adress review comments --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 21 +++++++++++++------ evm/src/cpu/kernel/asm/util/basic_macros.asm | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 267bf5284e..fef192cef4 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -104,9 +104,18 @@ code_bytes_to_skip: // A proof is valid if: // - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b // - i > 0 and: -// - for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, -// - we can go from opcode i+32 to jumpdest, -// - code[jumpdest] = 0x5b. +// a) for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, +// b) we can go from opcode i+32 to jumpdest, +// c) code[jumpdest] = 0x5b. +// To reduce the number of instructions, when i > 32 we load all the bytes code[j], ..., +// code[j + 31] in a single 32-byte word, and check a) directly on the packed bytes. +// We perform the "packed verification" computing a boolean formula evaluated on the bits of +// code[j],..., code[j+31] of the form p_1 AND p_2 AND p_3 AND p_4 AND p_5, where: +// - p_k is either TRUE, for one subset of the j's which depends on k (for example, +// for k = 1, it is TRUE for the first 15 positions), or has_prefix_k => bit_{k + 1}_is_0 +// for the j's not in the subset. +// - has_prefix_k is a predicate that is TRUE if and only if code[j] has the same prefix of size k + 2 +// as PUSH{32-(j-i)}. // stack: proof_prefix_addr, jumpdest, ctx, retdest // stack: (empty) global write_table_if_jumpdest: @@ -197,7 +206,7 @@ global write_table_if_jumpdest: SWAP1 // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in positions 1-2, 8-11, 16-19, and 24-27 + // - (0xFF|X⁷)³² for bytes in positions 1-3, 8-11, 16-19, and 24-27 // - (has_prefix => is_0_at_6 |X⁷)³² on the rest // Compute also that ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. @@ -294,7 +303,7 @@ global jumpdest_analysis: // and the next prover input should contain a proof for address'. PROVER_INPUT(jumpdest_table::next_address) DUP1 %jumpi(check_proof) - // If proof == 0 there are no more jump destinations to check + // If address == 0 there are no more jump destinations to check POP // This is just a hook used for avoiding verification of the jumpdest // table in another context. It is useful during proof generation, @@ -303,7 +312,7 @@ global jumpdest_analysis_end: %pop2 JUMP check_proof: - // stack: proof, ctx, code_len, retdest + // stack: address, ctx, code_len, retdest DUP3 DUP2 %assert_le %decrement // stack: proof, ctx, code_len, retdest diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index 566614bd9b..855eb4bb6e 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -8,15 +8,15 @@ jumpi %endmacro +// Jump to `jumpdest` if the top of the stack is != c %macro jump_neq_const(c, jumpdest) PUSH $c SUB %jumpi($jumpdest) %endmacro +// Jump to `jumpdest` if the top of the stack is < c %macro jumpi_lt_const(c, jumpdest) - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x < c) == !(x >= c). %ge_const($c) %jumpi($jumpdest) %endmacro From f9c3ad6646c1047c3a44d2eb89d48f001c4e6ae6 Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 11 Jan 2024 13:44:20 +0100 Subject: [PATCH 096/175] Update empty_txn_list --- evm/tests/empty_txn_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index ff4e7637cc..2130e51bab 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -77,7 +77,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { // Initialize the preprocessed circuits for the zkEVM. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 10..11, 12..13, 14..15, 9..11, 12..13, 17..18], // Minimal ranges to prove an empty list + &[16..17, 9..11, 12..13, 14..15, 9..11, 12..13, 17..18], // Minimal ranges to prove an empty list &config, ); From ac9f704f97caebefeb934960653015acb261759c Mon Sep 17 00:00:00 2001 From: 4l0n50 Date: Thu, 11 Jan 2024 14:26:28 +0100 Subject: [PATCH 097/175] Fix comment --- evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index fef192cef4..9dee5d2b88 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -229,7 +229,7 @@ global write_table_if_jumpdest: SWAP1 // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in positions 1-7 and 16-23 + // - (0xFF|X⁷)³² for bytes in 1, 4-5, 8-9, 12-13, 16-17, 20-21, 24-25, 28-29 // - (has_prefix => is_0_at_7 |X⁷)³² on the rest // Compute also that ~has_prefix = ~has_prefix OR is_0_at_7 for all bytes. From cda30847a9c33f23aae1eb659646f6e5023a942e Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 11 Jan 2024 15:43:51 +0100 Subject: [PATCH 098/175] Apply review --- plonky2/src/fri/mod.rs | 4 +++- plonky2/src/gates/arithmetic_base.rs | 2 +- plonky2/src/gates/arithmetic_extension.rs | 2 +- plonky2/src/gates/gate.rs | 4 ++-- plonky2/src/gates/mod.rs | 2 +- plonky2/src/gates/multiplication_extension.rs | 4 ++-- plonky2/src/iop/target.rs | 4 ++-- plonky2/src/plonk/circuit_builder.rs | 6 +++--- plonky2/src/plonk/circuit_data.rs | 2 ++ plonky2/src/plonk/config.rs | 4 ++-- 10 files changed, 19 insertions(+), 15 deletions(-) diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index ce104ada0d..207a2ea82c 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -1,4 +1,6 @@ -//! Fast Reed-Solomon IOP (FRI) protocol and its circuit version +//! Fast Reed-Solomon IOP (FRI) protocol. +//! +//! It provides both a native implementation and an in-circuit version //! of the FRI verifier for recursive proof composition. use alloc::vec::Vec; diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index e06c58aff3..dfdd87e8c0 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -21,7 +21,7 @@ use crate::plonk::vars::{ use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config -/// supports enough routed wires, it can support several such operations in one gate. +/// has enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct ArithmeticGate { /// Number of arithmetic operations performed by an arithmetic gate. diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 37482a5d92..a19c6b4a4b 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -17,7 +17,7 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// A gate which can perform a weighted multiply-add, i.e. `result = c0.x.y + c1.z`. If the config -/// supports enough routed wires, it can support several such operations in one gate. +/// has enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct ArithmeticExtensionGate { /// Number of arithmetic operations performed by an arithmetic gate. diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index baf26f8cd4..3087b74cc3 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -41,7 +41,7 @@ use crate::util::serialization::{Buffer, IoResult}; /// instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. /// Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. /// -/// However, such gate is fairly limited for more complex computations. Hence, when a computation may +/// However, such a gate is fairly limited for more complex computations. Hence, when a computation may /// require too many of these "vanilla" gates, or when a computation arises often within the same circuit, /// one may want to construct a tailored custom gate. These custom gates can use more selectors and are /// not necessarily limited to 2 inputs + 1 output = 3 wires. @@ -63,7 +63,7 @@ pub trait Gate, const D: usize>: 'static + Send + S where Self: Sized; - /// Defines the constraints that enforce the statement represented by this gate. + /// Defines and evaluates the constraints that enforce the statement represented by this gate. /// Constraints must be defined in the extension of this custom gate base field. fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec; diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index 446f1aeda2..b490e12e37 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -14,7 +14,7 @@ //! instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. //! Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. //! -//! However, such gate is fairly limited for more complex computations. Hence, when a computation may +//! However, such a gate is fairly limited for more complex computations. Hence, when a computation may //! require too many of these "vanilla" gates, or when a computation arises often within the same circuit, //! one may want to construct a tailored custom gate. These custom gates can use more selectors and are //! not necessarily limited to 2 inputs + 1 output = 3 wires. diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 1d85817ade..3f9fd8fe53 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -16,8 +16,8 @@ use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::util::serialization::{Buffer, IoResult, Read, Write}; -/// A gate which can perform a weighted multiplication, i.e. `result = c0.x.y`. If the config -/// supports enough routed wires, it can support several such operations in one gate. +/// A gate which can perform a weighted multiplication, i.e. `result = c0.x.y` on [`ExtensionTarget`]. +/// If the config has enough routed wires, it can support several such operations in one gate. #[derive(Debug, Clone)] pub struct MulExtensionGate { /// Number of multiplications performed by the gate. diff --git a/plonky2/src/iop/target.rs b/plonky2/src/iop/target.rs index b1fe9bf9b6..705941e023 100644 --- a/plonky2/src/iop/target.rs +++ b/plonky2/src/iop/target.rs @@ -17,8 +17,8 @@ use crate::plonk::circuit_data::CircuitConfig; /// the [PartialWitness](crate::iop::witness::PartialWitness) interface. /// /// There are different "variants" of the `Target` type, namely [`ExtensionTarget`], -/// [ExtensionAlgebraTarget](crate::iop::ext_target::ExtensionAlgebraTarget), but the `Target` -/// type is the default one for most circuits verifying some simple statement. +/// [ExtensionAlgebraTarget](crate::iop::ext_target::ExtensionAlgebraTarget). +/// The `Target` type is the default one for most circuits verifying some simple statement. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] pub enum Target { /// A target that has a fixed location in the witness (seen as a `degree x num_wires` grid). diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 227af85bba..2e0904e434 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -486,7 +486,7 @@ impl, const D: usize> CircuitBuilder { } /// Adds a gate type to the set of gates to be used in this circuit. This can be useful - /// in conditional recursion to uniformize the gates set of the different circuits. + /// in conditional recursion to uniformize the set of gates of the different circuits. pub fn add_gate_to_gate_set(&mut self, gate: GateRef) { self.gates.insert(gate); } @@ -575,12 +575,12 @@ impl, const D: usize> CircuitBuilder { self.constant(F::NEG_ONE) } - /// Returns a rootable boolean target set to false. + /// Returns a routable boolean target set to false. pub fn _false(&mut self) -> BoolTarget { BoolTarget::new_unsafe(self.zero()) } - /// Returns a rootable boolean target set to true. + /// Returns a routable boolean target set to true. pub fn _true(&mut self) -> BoolTarget { BoolTarget::new_unsafe(self.one()) } diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index 4a2efe060c..d9847f4be8 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -66,6 +66,8 @@ pub struct CircuitConfig { /// This allows copy constraints, i.e. enforcing that two distant values in a circuit are equal. /// Non-routed wires are called advice wires. pub num_routed_wires: usize, + /// The number of constants that can be used per gate. If a gate requires more constants than the config + /// allows, the [`CircuitBuilder`] will complain when trying to add this gate to its set of gates. pub num_constants: usize, /// Whether to use a dedicated gate for base field arithmetic, rather than using a single gate /// for both base field and extension field arithmetic. diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index c4e73356b3..1ed40c40ce 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -3,8 +3,8 @@ //! This module defines a [`Hasher`] trait as well as its recursive //! counterpart [`AlgebraicHasher`] for in-circuit hashing. It also //! provides concrete configurations, one fully recursive leveraging -//! Poseidon hash function both internally and natively, and one mixing -//! Poseidon internally and truncated Keccak externally. +//! the Poseidon hash function both internally and natively, and one +//! mixing Poseidon internally and truncated Keccak externally. use alloc::vec; use alloc::vec::Vec; From b05e84ddf8cc5213dbf2939be511ccef268072b8 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:03:05 +0100 Subject: [PATCH 099/175] Interpreter GenerationInputs (#1455) * Add initialization with GenerationInputs to the interpreter * Add new_with_generation_inputs_and_kernel * Apply comments --- evm/src/cpu/kernel/interpreter.rs | 151 +++++++++++++++++++++++++- evm/src/cpu/kernel/tests/add11.rs | 171 +++++++++++++----------------- evm/src/generation/state.rs | 2 +- 3 files changed, 224 insertions(+), 100 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index b99ccaebc5..227df021d1 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -5,7 +5,8 @@ use std::collections::HashMap; use std::ops::Range; use anyhow::bail; -use ethereum_types::{U256, U512}; +use eth_trie_utils::partial_trie::PartialTrie; +use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; @@ -16,11 +17,13 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; use crate::cpu::stack::MAX_USER_STACK_SIZE; use crate::extension_tower::BN_BASE; +use crate::generation::mpt::load_all_mpts; use crate::generation::prover_input::ProverInputFn; -use crate::generation::state::GenerationState; +use crate::generation::rlp::all_rlp_prover_inputs_reversed; +use crate::generation::state::{all_withdrawals_prover_inputs_reversed, GenerationState}; use crate::generation::GenerationInputs; use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::util::u256_to_usize; +use crate::util::{h2u, u256_to_usize}; use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; @@ -150,6 +153,18 @@ impl<'a> Interpreter<'a> { result } + /// Returns an instance of `Interpreter` given `GenerationInputs`, and assuming we are + /// initializing with the `KERNEL` code. + pub(crate) fn new_with_generation_inputs_and_kernel( + initial_offset: usize, + initial_stack: Vec, + inputs: GenerationInputs, + ) -> Self { + let mut result = Self::new_with_kernel(initial_offset, initial_stack); + result.initialize_interpreter_state_with_kernel(inputs); + result + } + pub(crate) fn new( code: &'a [u8], initial_offset: usize, @@ -181,6 +196,136 @@ impl<'a> Interpreter<'a> { result } + /// Initializes the interpreter state given `GenerationInputs`, using the KERNEL code. + pub(crate) fn initialize_interpreter_state_with_kernel(&mut self, inputs: GenerationInputs) { + self.initialize_interpreter_state(inputs, KERNEL.code_hash, KERNEL.code.len()); + } + + /// Initializes the interpreter state given `GenerationInputs`. + pub(crate) fn initialize_interpreter_state( + &mut self, + inputs: GenerationInputs, + kernel_hash: H256, + kernel_code_len: usize, + ) { + let tries = &inputs.tries; + + // Set state's inputs. + self.generation_state.inputs = inputs.clone(); + + // Initialize the MPT's pointers. + let (trie_root_ptrs, trie_data) = + load_all_mpts(tries).expect("Invalid MPT data for preinitialization"); + let trie_roots_after = &inputs.trie_roots_after; + self.generation_state.trie_root_ptrs = trie_root_ptrs; + + // Initialize the `TrieData` segment. + for (i, data) in trie_data.iter().enumerate() { + let trie_addr = MemoryAddress::new(0, Segment::TrieData, i); + self.generation_state.memory.set(trie_addr, data.into()); + } + + // Update the RLP and withdrawal prover inputs. + let rlp_prover_inputs = + all_rlp_prover_inputs_reversed(inputs.clone().signed_txn.as_ref().unwrap_or(&vec![])); + let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); + self.generation_state.rlp_prover_inputs = rlp_prover_inputs; + self.generation_state.withdrawal_prover_inputs = withdrawal_prover_inputs; + + // Set `GlobalMetadata` values. + let metadata = &inputs.block_metadata; + let global_metadata_to_set = [ + ( + GlobalMetadata::BlockBeneficiary, + U256::from_big_endian(&metadata.block_beneficiary.0), + ), + (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), + (GlobalMetadata::BlockNumber, metadata.block_number), + (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), + ( + GlobalMetadata::BlockRandom, + metadata.block_random.into_uint(), + ), + (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), + (GlobalMetadata::BlockChainId, metadata.block_chain_id), + (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), + ( + GlobalMetadata::BlockCurrentHash, + h2u(inputs.block_hashes.cur_hash), + ), + (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), + (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), + (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), + (GlobalMetadata::TxnNumberBefore, inputs.txn_number_before), + ( + GlobalMetadata::TxnNumberAfter, + inputs.txn_number_before + if inputs.signed_txn.is_some() { 1 } else { 0 }, + ), + ( + GlobalMetadata::StateTrieRootDigestBefore, + h2u(tries.state_trie.hash()), + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + h2u(tries.transactions_trie.hash()), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + h2u(tries.receipts_trie.hash()), + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + h2u(trie_roots_after.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + h2u(trie_roots_after.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + h2u(trie_roots_after.receipts_root), + ), + (GlobalMetadata::KernelHash, h2u(kernel_hash)), + (GlobalMetadata::KernelLen, kernel_code_len.into()), + ]; + + self.set_global_metadata_multi_fields(&global_metadata_to_set); + + // Set final block bloom values. + let final_block_bloom_fields = (0..8) + .map(|i| { + ( + MemoryAddress::new_u256s( + U256::zero(), + (Segment::GlobalBlockBloom.unscale()).into(), + i.into(), + ) + .unwrap(), + metadata.block_bloom[i], + ) + }) + .collect::>(); + + self.set_memory_multi_addresses(&final_block_bloom_fields); + + // Set previous block hash. + let block_hashes_fields = (0..256) + .map(|i| { + ( + MemoryAddress::new_u256s( + U256::zero(), + (Segment::BlockHashes.unscale()).into(), + i.into(), + ) + .unwrap(), + h2u(inputs.block_hashes.prev_hashes[i]), + ) + }) + .collect::>(); + + self.set_memory_multi_addresses(&block_hashes_fields); + } + fn checkpoint(&self) -> InterpreterCheckpoint { let registers = InterpreterRegistersState { kernel_mode: self.is_kernel(), diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 1e71d60dbe..89cdf82d66 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use ethereum_types::{Address, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; @@ -17,56 +17,9 @@ use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::proof::TrieRoots; +use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; use crate::util::h2u; - -// Stolen from `tests/mpt/insert.rs` -// Prepare the interpreter by loading the initial MPTs and -// by setting all `GlobalMetadata` and necessary code into memory. -fn prepare_interpreter( - interpreter: &mut Interpreter, - trie_inputs: TrieInputs, - transaction: &[u8], - contract_code: HashMap>, -) { - initialize_mpts(interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Set necessary `GlobalMetadata`. - let global_metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(trie_inputs.state_trie.hash()), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(trie_inputs.transactions_trie.hash()), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(trie_inputs.receipts_trie.hash()), - ), - (GlobalMetadata::TxnNumberAfter, 1.into()), - (GlobalMetadata::BlockGasUsedAfter, 0xa868u64.into()), - (GlobalMetadata::BlockGasLimit, 1_000_000.into()), - (GlobalMetadata::BlockBaseFee, 10.into()), - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian( - &Address::from(hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba")).0, - ), - ), - ]; - - interpreter.set_global_metadata_multi_fields(&global_metadata_to_set); - - // Set contract code and transaction. - interpreter.generation_state.inputs.contract_code = contract_code; - - interpreter.generation_state.inputs.signed_txn = Some(transaction.to_vec()); - let rlp_prover_inputs = all_rlp_prover_inputs_reversed(transaction); - interpreter.generation_state.rlp_prover_inputs = rlp_prover_inputs; -} +use crate::GenerationInputs; #[test] fn test_add11_yml() { @@ -120,10 +73,8 @@ fn test_add11_yml() { let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let gas_used = 0xa868u64.into(); - prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code); let expected_state_trie_after = { let beneficiary_account_after = AccountRlp { nonce: 1.into(), @@ -158,7 +109,7 @@ fn test_add11_yml() { }; let receipt_0 = LegacyReceiptRlp { status: true, - cum_gas_used: 0xa868u64.into(), + cum_gas_used: gas_used, bloom: vec![0; 256].into(), logs: vec![], }; @@ -179,24 +130,41 @@ fn test_add11_yml() { receipts_root: receipts_trie.hash(), }; - // Set trie roots after the transaction was executed. - let metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - ]; - interpreter.set_global_metadata_multi_fields(&metadata_to_set); - - let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: gas_used, + block_bloom: [0.into(); 8], + }; + + let tries_inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code: contract_code.clone(), + block_metadata, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let initial_stack = vec![]; + let mut interpreter = + Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); + + let route_txn_label = KERNEL.global_labels["main"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); @@ -259,10 +227,6 @@ fn test_add11_yml_with_exception() { let txn_gas_limit = 400_000; let gas_price = 10; - let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); - - prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code); // Here, since the transaction fails, it consumes its gas limit, and does nothing else. let expected_state_trie_after = { let beneficiary_account_after = beneficiary_account_before; @@ -308,26 +272,41 @@ fn test_add11_yml_with_exception() { receipts_root: receipts_trie.hash(), }; - // Set trie roots after the transaction was executed. - let metadata_to_set = [ - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - // The gas used in this case is the transaction gas limit - (GlobalMetadata::BlockGasUsedAfter, txn_gas_limit.into()), - ]; - interpreter.set_global_metadata_multi_fields(&metadata_to_set); - - let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: txn_gas_limit.into(), + block_bloom: [0.into(); 8], + }; + + let tries_inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code: contract_code.clone(), + block_metadata, + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: txn_gas_limit.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let initial_stack = vec![]; + let mut interpreter = + Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); + + let route_txn_label = KERNEL.global_labels["main"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index fec2e11ca8..c2822d1cc0 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -169,7 +169,7 @@ impl GenerationState { /// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. /// Returns the reversed array. -fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { +pub(crate) fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { let mut withdrawal_prover_inputs = withdrawals .iter() .flat_map(|w| [U256::from((w.0).0.as_slice()), w.1]) From f80ebe77f38ecbf94ce3c357fcc5332344561b6d Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Thu, 11 Jan 2024 11:36:47 -0500 Subject: [PATCH 100/175] Remove full memory channel (#1450) * Remove a full memory channel * Remove unnecessary uses * Revert PDF change * Apply comments * Apply more comments * Move disabling functions to cpu_stark.rs * Apply comments --- evm/spec/zkevm.pdf | Bin 315675 -> 296911 bytes evm/src/all_stark.rs | 33 ++++-- evm/src/cpu/contextops.rs | 145 ++++---------------------- evm/src/cpu/cpu_stark.rs | 127 ++++++++++++++++++++++- evm/src/cpu/dup_swap.rs | 22 ---- evm/src/cpu/kernel/tests/add11.rs | 2 +- evm/src/cpu/membus.rs | 7 +- evm/src/cpu/memio.rs | 2 +- evm/src/cpu/shift.rs | 4 +- evm/src/cpu/syscalls_exceptions.rs | 161 +++++++++++++---------------- evm/src/witness/operation.rs | 138 ++++++++++++------------- 11 files changed, 317 insertions(+), 324 deletions(-) diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 97217d5d8069f79d32afa655ebcfafe1cea12357..3b10fba30b89f1ad27d84f3a860fbb31286cb1e0 100644 GIT binary patch delta 111745 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 E2c3ol@&Et; delta 129755 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 E2d*NNYybcN diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index a2996eb47b..cf03a60088 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -135,6 +135,11 @@ fn ctl_byte_packing() -> CrossTableLookup { cpu_stark::ctl_data_byte_packing_push(), Some(cpu_stark::ctl_filter_byte_packing_push()), ); + let cpu_jumptable_read_looking = TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_jumptable_read(), + Some(cpu_stark::ctl_filter_syscall_exceptions()), + ); let byte_packing_looked = TableWithColumns::new( Table::BytePacking, byte_packing_stark::ctl_looked_data(), @@ -145,6 +150,7 @@ fn ctl_byte_packing() -> CrossTableLookup { cpu_packing_looking, cpu_unpacking_looking, cpu_push_packing_looking, + cpu_jumptable_read_looking, ], byte_packing_looked, ) @@ -238,6 +244,16 @@ fn ctl_memory() -> CrossTableLookup { cpu_stark::ctl_data_partial_memory::(), Some(cpu_stark::ctl_filter_partial_memory()), ); + let cpu_set_context_write = TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_memory_old_sp_write_set_context::(), + Some(cpu_stark::ctl_filter_set_context()), + ); + let cpu_set_context_read = TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_memory_new_sp_read_set_context::(), + Some(cpu_stark::ctl_filter_set_context()), + ); let keccak_sponge_reads = (0..KECCAK_RATE_BYTES).map(|i| { TableWithColumns::new( Table::KeccakSponge, @@ -252,12 +268,17 @@ fn ctl_memory() -> CrossTableLookup { Some(byte_packing_stark::ctl_looking_memory_filter(i)), ) }); - let all_lookers = iter::once(cpu_memory_code_read) - .chain(cpu_memory_gp_ops) - .chain(iter::once(cpu_push_write_ops)) - .chain(keccak_sponge_reads) - .chain(byte_packing_ops) - .collect(); + let all_lookers = vec![ + cpu_memory_code_read, + cpu_push_write_ops, + cpu_set_context_write, + cpu_set_context_read, + ] + .into_iter() + .chain(cpu_memory_gp_ops) + .chain(keccak_sponge_reads) + .chain(byte_packing_ops) + .collect(); let memory_looked = TableWithColumns::new( Table::Memory, memory_stark::ctl_data(), diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 77debc759c..7f46d1418f 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -7,11 +7,12 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use super::columns::ops::OpsColumnsView; +use super::cpu_stark::{disable_unused_channels, disable_unused_channels_circuit}; use super::membus::NUM_GP_CHANNELS; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; // If true, the instruction will keep the current context for the next row. @@ -95,12 +96,7 @@ fn eval_packed_get( yield_constr.constraint(filter * (nv.stack_len - (lv.stack_len + P::ONES))); // Unused channels. - for i in 1..NUM_GP_CHANNELS { - if i != 3 { - let channel = lv.mem_channels[i]; - yield_constr.constraint(filter * channel.used); - } - } + disable_unused_channels(lv, filter, vec![1], yield_constr); yield_constr.constraint(filter * nv.mem_channels[0].used); } @@ -137,13 +133,7 @@ fn eval_ext_circuit_get, const D: usize>( } // Unused channels. - for i in 1..NUM_GP_CHANNELS { - if i != 3 { - let channel = lv.mem_channels[i]; - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - } + disable_unused_channels_circuit(builder, lv, filter, vec![1], yield_constr); { let constr = builder.mul_extension(filter, nv.mem_channels[0].used); yield_constr.constraint(builder, constr); @@ -158,12 +148,6 @@ fn eval_packed_set( ) { let filter = lv.op.context_op * lv.opcode_bits[0]; let stack_top = lv.mem_channels[0].value; - let write_old_sp_channel = lv.mem_channels[1]; - let read_new_sp_channel = lv.mem_channels[2]; - // We need to unscale the context metadata segment and related field. - let ctx_metadata_segment = P::Scalar::from_canonical_usize(Segment::ContextMetadata.unscale()); - let stack_size_field = P::Scalar::from_canonical_usize(ContextMetadata::StackSize.unscale()); - let local_sp_dec = lv.stack_len - P::ONES; // The next row's context is read from stack_top. yield_constr.constraint(filter * (stack_top[2] - nv.context)); @@ -171,27 +155,9 @@ fn eval_packed_set( yield_constr.constraint(filter * limb); } - // The old SP is decremented (since the new context was popped) and written to memory. - yield_constr.constraint(filter * (write_old_sp_channel.value[0] - local_sp_dec)); - for &limb in &write_old_sp_channel.value[1..] { - yield_constr.constraint(filter * limb); - } - yield_constr.constraint(filter * (write_old_sp_channel.used - P::ONES)); - yield_constr.constraint(filter * write_old_sp_channel.is_read); - yield_constr.constraint(filter * (write_old_sp_channel.addr_context - lv.context)); - yield_constr.constraint(filter * (write_old_sp_channel.addr_segment - ctx_metadata_segment)); - yield_constr.constraint(filter * (write_old_sp_channel.addr_virtual - stack_size_field)); - + // The old SP is decremented (since the new context was popped) and stored in memory. // The new SP is loaded from memory. - yield_constr.constraint(filter * (read_new_sp_channel.value[0] - nv.stack_len)); - for &limb in &read_new_sp_channel.value[1..] { - yield_constr.constraint(filter * limb); - } - yield_constr.constraint(filter * (read_new_sp_channel.used - P::ONES)); - yield_constr.constraint(filter * (read_new_sp_channel.is_read - P::ONES)); - yield_constr.constraint(filter * (read_new_sp_channel.addr_context - nv.context)); - yield_constr.constraint(filter * (read_new_sp_channel.addr_segment - ctx_metadata_segment)); - yield_constr.constraint(filter * (read_new_sp_channel.addr_virtual - stack_size_field)); + // This is all done with CTLs: nothing is constrained here. // Constrain stack_inv_aux_2. let new_top_channel = nv.mem_channels[0]; @@ -200,17 +166,19 @@ fn eval_packed_set( * (lv.general.stack().stack_inv_aux * lv.opcode_bits[0] - lv.general.stack().stack_inv_aux_2), ); - // The new top is loaded in memory channel 3, if the stack isn't empty (see eval_packed). + // The new top is loaded in memory channel 2, if the stack isn't empty (see eval_packed). for (&limb_new_top, &limb_read_top) in new_top_channel .value .iter() - .zip(lv.mem_channels[3].value.iter()) + .zip(lv.mem_channels[2].value.iter()) { yield_constr.constraint( lv.op.context_op * lv.general.stack().stack_inv_aux_2 * (limb_new_top - limb_read_top), ); } + // Unused channels. + disable_unused_channels(lv, filter, vec![1], yield_constr); yield_constr.constraint(filter * new_top_channel.used); } @@ -224,17 +192,6 @@ fn eval_ext_circuit_set, const D: usize>( ) { let filter = builder.mul_extension(lv.op.context_op, lv.opcode_bits[0]); let stack_top = lv.mem_channels[0].value; - let write_old_sp_channel = lv.mem_channels[1]; - let read_new_sp_channel = lv.mem_channels[2]; - // We need to unscale the context metadata segment and related field. - let ctx_metadata_segment = builder.constant_extension(F::Extension::from_canonical_usize( - Segment::ContextMetadata.unscale(), - )); - let stack_size_field = builder.constant_extension(F::Extension::from_canonical_usize( - ContextMetadata::StackSize.unscale(), - )); - let one = builder.one_extension(); - let local_sp_dec = builder.sub_extension(lv.stack_len, one); // The next row's context is read from stack_top. { @@ -247,73 +204,9 @@ fn eval_ext_circuit_set, const D: usize>( yield_constr.constraint(builder, constr); } - // The old SP is decremented (since the new context was popped) and written to memory. - { - let diff = builder.sub_extension(write_old_sp_channel.value[0], local_sp_dec); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - for &limb in &write_old_sp_channel.value[1..] { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, write_old_sp_channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(filter, write_old_sp_channel.is_read); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(write_old_sp_channel.addr_context, lv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(write_old_sp_channel.addr_segment, ctx_metadata_segment); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(write_old_sp_channel.addr_virtual, stack_size_field); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - + // The old SP is decremented (since the new context was popped) and stored in memory. // The new SP is loaded from memory. - { - let diff = builder.sub_extension(read_new_sp_channel.value[0], nv.stack_len); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - for &limb in &read_new_sp_channel.value[1..] { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, read_new_sp_channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, read_new_sp_channel.is_read, filter); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(read_new_sp_channel.addr_context, nv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(read_new_sp_channel.addr_segment, ctx_metadata_segment); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(read_new_sp_channel.addr_virtual, stack_size_field); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } + // This is all done with CTLs: nothing is constrained here. // Constrain stack_inv_aux_2. let new_top_channel = nv.mem_channels[0]; @@ -326,11 +219,11 @@ fn eval_ext_circuit_set, const D: usize>( let constr = builder.mul_extension(lv.op.context_op, diff); yield_constr.constraint(builder, constr); } - // The new top is loaded in memory channel 3, if the stack isn't empty (see eval_packed). + // The new top is loaded in memory channel 2, if the stack isn't empty (see eval_packed). for (&limb_new_top, &limb_read_top) in new_top_channel .value .iter() - .zip(lv.mem_channels[3].value.iter()) + .zip(lv.mem_channels[2].value.iter()) { let diff = builder.sub_extension(limb_new_top, limb_read_top); let prod = builder.mul_extension(lv.general.stack().stack_inv_aux_2, diff); @@ -338,6 +231,8 @@ fn eval_ext_circuit_set, const D: usize>( yield_constr.constraint(builder, constr); } + // Unused channels. + disable_unused_channels_circuit(builder, lv, filter, vec![1], yield_constr); { let constr = builder.mul_extension(filter, new_top_channel.used); yield_constr.constraint(builder, constr); @@ -355,10 +250,10 @@ pub(crate) fn eval_packed( eval_packed_set(lv, nv, yield_constr); // Stack constraints. - // Both operations use memory channel 3. The operations are similar enough that + // Both operations use memory channel 2. The operations are similar enough that // we can constrain both at the same time. let filter = lv.op.context_op; - let channel = lv.mem_channels[3]; + let channel = lv.mem_channels[2]; // For get_context, we check if lv.stack_len is 0. For set_context, we check if nv.stack_len is 0. // However, for get_context, we can deduce lv.stack_len from nv.stack_len since the operation only pushes. let stack_len = nv.stack_len - (P::ONES - lv.opcode_bits[0]); @@ -396,10 +291,10 @@ pub(crate) fn eval_ext_circuit, const D: usize>( eval_ext_circuit_set(builder, lv, nv, yield_constr); // Stack constraints. - // Both operations use memory channel 3. The operations are similar enough that + // Both operations use memory channel 2. The operations are similar enough that // we can constrain both at the same time. let filter = lv.op.context_op; - let channel = lv.mem_channels[3]; + let channel = lv.mem_channels[2]; // For get_context, we check if lv.stack_len is 0. For set_context, we check if nv.stack_len is 0. // However, for get_context, we can deduce lv.stack_len from nv.stack_len since the operation only pushes. let diff = builder.add_const_extension(lv.opcode_bits[0], -F::ONE); diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 478dc9c2a6..1212e7a741 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -11,6 +11,7 @@ use plonky2::iop::ext_target::ExtensionTarget; use super::columns::CpuColumnsView; use super::halt; +use super::kernel::constants::context_metadata::ContextMetadata; use super::membus::NUM_GP_CHANNELS; use crate::all_stark::Table; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; @@ -21,7 +22,7 @@ use crate::cpu::{ }; use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; @@ -189,6 +190,40 @@ pub(crate) fn ctl_filter_byte_unpacking() -> Filter { ) } +/// Creates the vector of `Columns` corresponding to three consecutive (byte) reads in memory. +/// It's used by syscalls and exceptions to read an address in a jumptable. +pub(crate) fn ctl_data_jumptable_read() -> Vec> { + let is_read = Column::constant(F::ONE); + let mut res = vec![is_read]; + + // When reading the jumptable, the address to start reading from is in + // GP channel 1; the result is in GP channel 1's values. + let channel_map = COL_MAP.mem_channels[1]; + res.extend(Column::singles([ + channel_map.addr_context, + channel_map.addr_segment, + channel_map.addr_virtual, + ])); + let val = Column::singles(channel_map.value); + + // len is always 3. + let len = Column::constant(F::from_canonical_usize(3)); + res.push(len); + + let num_channels = F::from_canonical_usize(NUM_CHANNELS); + let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); + res.push(timestamp); + + res.extend(val); + + res +} + +/// CTL filter for syscalls and exceptions. +pub(crate) fn ctl_filter_syscall_exceptions() -> Filter { + Filter::new_simple(Column::sum([COL_MAP.op.syscall, COL_MAP.op.exception])) +} + /// Creates the vector of `Columns` corresponding to the contents of the CPU registers when performing a `PUSH`. /// `PUSH` internal reads are done by calling `BytePackingStark`. pub(crate) fn ctl_data_byte_packing_push() -> Vec> { @@ -305,6 +340,53 @@ pub(crate) fn ctl_data_partial_memory() -> Vec> { cols } +/// Old stack pointer write for SET_CONTEXT. +pub(crate) fn ctl_data_memory_old_sp_write_set_context() -> Vec> { + let mut cols = vec![ + Column::constant(F::ZERO), // is_read + Column::single(COL_MAP.context), // addr_context + Column::constant(F::from_canonical_usize(Segment::ContextMetadata.unscale())), // addr_segment + Column::constant(F::from_canonical_usize( + ContextMetadata::StackSize.unscale(), + )), // addr_virtual + ]; + + // Low limb is current stack length minus one. + cols.push(Column::linear_combination_with_constant( + [(COL_MAP.stack_len, F::ONE)], + -F::ONE, + )); + + // High limbs of the value are all zero. + cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); + + cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + 1)); + + cols +} + +/// New stack pointer read for SET_CONTEXT. +pub(crate) fn ctl_data_memory_new_sp_read_set_context() -> Vec> { + let mut cols = vec![ + Column::constant(F::ONE), // is_read + Column::single(COL_MAP.mem_channels[0].value[2]), // addr_context (in the top of the stack) + Column::constant(F::from_canonical_usize(Segment::ContextMetadata.unscale())), // addr_segment + Column::constant(F::from_canonical_u64( + ContextMetadata::StackSize as u64 - Segment::ContextMetadata as u64, + )), // addr_virtual + ]; + + // Low limb is new stack length. + cols.push(Column::single_next_row(COL_MAP.stack_len)); + + // High limbs of the value are all zero. + cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); + + cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + 2)); + + cols +} + /// CTL filter for code read and write operations. pub(crate) fn ctl_filter_code_memory() -> Filter { Filter::new_simple(Column::sum(COL_MAP.op.iter())) @@ -319,6 +401,49 @@ pub(crate) fn ctl_filter_partial_memory() -> Filter { Filter::new_simple(Column::single(COL_MAP.partial_channel.used)) } +/// CTL filter for the `SET_CONTEXT` operation. +/// SET_CONTEXT is differentiated from GET_CONTEXT by its zeroth bit set to 1 +pub(crate) fn ctl_filter_set_context() -> Filter { + Filter::new( + vec![( + Column::single(COL_MAP.op.context_op), + Column::single(COL_MAP.opcode_bits[0]), + )], + vec![], + ) +} + +/// Disable the specified memory channels. +/// Since channel 0 contains the top of the stack and is handled specially, +/// channels to disable are 1, 2 or both. All cases can be expressed as a vec. +pub(crate) fn disable_unused_channels( + lv: &CpuColumnsView

, + filter: P, + channels: Vec, + yield_constr: &mut ConstraintConsumer

, +) { + for i in channels { + yield_constr.constraint(filter * lv.mem_channels[i].used); + } +} + +/// Circuit version of `disable_unused_channels`. +/// Disable the specified memory channels. +/// Since channel 0 contains the top of the stack and is handled specially, +/// channels to disable are 1, 2 or both. All cases can be expressed as a vec. +pub(crate) fn disable_unused_channels_circuit, const D: usize>( + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + lv: &CpuColumnsView>, + filter: ExtensionTarget, + channels: Vec, + yield_constr: &mut RecursiveConstraintConsumer, +) { + for i in channels { + let constr = builder.mul_extension(filter, lv.mem_channels[i].used); + yield_constr.constraint(builder, constr); + } +} + /// Structure representing the CPU Stark. #[derive(Copy, Clone, Default)] pub(crate) struct CpuStark { diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 44763fdc91..7b9d80f37c 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -138,11 +138,6 @@ fn eval_packed_dup( // Disable next top. yield_constr.constraint(filter * nv.mem_channels[0].used); - - // Constrain unused channels. - for i in 3..NUM_GP_CHANNELS { - yield_constr.constraint(filter * lv.mem_channels[i].used); - } } /// Circuit version of `eval_packed_dup`. @@ -205,12 +200,6 @@ fn eval_ext_circuit_dup, const D: usize>( let constr = builder.mul_extension(filter, nv.mem_channels[0].used); yield_constr.constraint(builder, constr); } - - // Constrain unused channels. - for i in 3..NUM_GP_CHANNELS { - let constr = builder.mul_extension(filter, lv.mem_channels[i].used); - yield_constr.constraint(builder, constr); - } } /// Evaluates constraints for SWAP. @@ -245,11 +234,6 @@ fn eval_packed_swap( // Disable next top. yield_constr.constraint(filter * nv.mem_channels[0].used); - - // Constrain unused channels. - for i in 3..NUM_GP_CHANNELS { - yield_constr.constraint(filter * lv.mem_channels[i].used); - } } /// Circuit version of `eval_packed_swap`. @@ -314,12 +298,6 @@ fn eval_ext_circuit_swap, const D: usize>( let constr = builder.mul_extension(filter, nv.mem_channels[0].used); yield_constr.constraint(builder, constr); } - - // Constrain unused channels. - for i in 3..NUM_GP_CHANNELS { - let constr = builder.mul_extension(filter, lv.mem_channels[i].used); - yield_constr.constraint(builder, constr); - } } /// Evaluates the constraints for the DUP and SWAP opcodes. diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 89cdf82d66..c2725488a6 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -16,7 +16,7 @@ use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; use crate::util::h2u; use crate::GenerationInputs; diff --git a/evm/src/cpu/membus.rs b/evm/src/cpu/membus.rs index e86d1ce554..fb6c530113 100644 --- a/evm/src/cpu/membus.rs +++ b/evm/src/cpu/membus.rs @@ -7,7 +7,7 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::cpu::columns::CpuColumnsView; /// General-purpose memory channels; they can read and write to all contexts/segments/addresses. -pub(crate) const NUM_GP_CHANNELS: usize = 4; +pub(crate) const NUM_GP_CHANNELS: usize = 3; /// Indices for code and general purpose memory channels. pub mod channel_indices { @@ -29,8 +29,11 @@ pub mod channel_indices { /// - the address is `program_counter`, /// - the value must fit in one byte (in the least-significant position) and its eight bits are /// found in `opcode_bits`. +/// +/// There is also a partial channel, which shares its values with another general purpose channel. +/// /// These limitations save us numerous columns in the CPU table. -pub(crate) const NUM_CHANNELS: usize = channel_indices::GP.end; +pub(crate) const NUM_CHANNELS: usize = channel_indices::GP.end + 1; /// Evaluates constraints regarding the membus. pub(crate) fn eval_packed( diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 7cb4c38855..924f030f5f 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -9,7 +9,7 @@ use super::cpu_stark::get_addr; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::stack; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::segments::Segment; const fn get_addr_load(lv: &CpuColumnsView) -> (T, T, T) { get_addr(lv, 0) diff --git a/evm/src/cpu/shift.rs b/evm/src/cpu/shift.rs index 29baa5ea63..9e751421ff 100644 --- a/evm/src/cpu/shift.rs +++ b/evm/src/cpu/shift.rs @@ -48,7 +48,7 @@ pub(crate) fn eval_packed( yield_constr.constraint(is_shift * (two_exp.addr_virtual - displacement.value[0])); // Other channels must be unused - for chan in &lv.mem_channels[3..NUM_GP_CHANNELS - 1] { + for chan in &lv.mem_channels[3..NUM_GP_CHANNELS] { yield_constr.constraint(is_shift * chan.used); // channel is not used } @@ -116,7 +116,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( yield_constr.constraint(builder, t); // Other channels must be unused - for chan in &lv.mem_channels[3..NUM_GP_CHANNELS - 1] { + for chan in &lv.mem_channels[3..NUM_GP_CHANNELS] { let t = builder.mul_extension(is_shift, chan.used); yield_constr.constraint(builder, t); } diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index 337a0a86ec..debae9dcd5 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -17,7 +17,6 @@ use crate::memory::segments::Segment; // Copy the constant but make it `usize`. const BYTES_PER_OFFSET: usize = crate::cpu::kernel::assembler::BYTES_PER_OFFSET as usize; -const_assert!(BYTES_PER_OFFSET < NUM_GP_CHANNELS); // Reserve one channel for stack push /// Evaluates constraints for syscalls and exceptions. pub(crate) fn eval_packed( @@ -71,41 +70,38 @@ pub(crate) fn eval_packed( let exc_handler_addr_start = exc_jumptable_start + exc_code * P::Scalar::from_canonical_usize(BYTES_PER_OFFSET); - for (i, channel) in lv.mem_channels[1..BYTES_PER_OFFSET + 1].iter().enumerate() { - // Set `used` and `is_read`. - yield_constr.constraint(total_filter * (channel.used - P::ONES)); - yield_constr.constraint(total_filter * (channel.is_read - P::ONES)); + let jumpdest_channel = lv.mem_channels[1]; - // Set kernel context and code segment - yield_constr.constraint(total_filter * channel.addr_context); - yield_constr.constraint(total_filter * (channel.addr_segment - code_segment)); + // Set `used` and `is_read`. + // The channel is not used: the reads will be done with the byte packing CTL. + yield_constr.constraint(total_filter * (jumpdest_channel.used)); + yield_constr.constraint(total_filter * (jumpdest_channel.is_read - P::ONES)); - // Set address, using a separate channel for each of the `BYTES_PER_OFFSET` limbs. - let limb_address_syscall = opcode_handler_addr_start + P::Scalar::from_canonical_usize(i); - let limb_address_exception = exc_handler_addr_start + P::Scalar::from_canonical_usize(i); + // Set kernel context and code segment + yield_constr.constraint(total_filter * jumpdest_channel.addr_context); + yield_constr.constraint(total_filter * (jumpdest_channel.addr_segment - code_segment)); - yield_constr.constraint(filter_syscall * (channel.addr_virtual - limb_address_syscall)); - yield_constr.constraint(filter_exception * (channel.addr_virtual - limb_address_exception)); + // Set address. + yield_constr + .constraint(filter_syscall * (jumpdest_channel.addr_virtual - opcode_handler_addr_start)); + yield_constr + .constraint(filter_exception * (jumpdest_channel.addr_virtual - exc_handler_addr_start)); + + // Set higher limbs to zero. + for &limb in &jumpdest_channel.value[1..] { + yield_constr.constraint(total_filter * limb); } // Disable unused channels - for channel in &lv.mem_channels[BYTES_PER_OFFSET + 1..NUM_GP_CHANNELS] { + for channel in &lv.mem_channels[2..NUM_GP_CHANNELS] { yield_constr.constraint(total_filter * channel.used); } // Set program counter to the handler address - // The addresses are big-endian in memory - let target = lv.mem_channels[1..BYTES_PER_OFFSET + 1] - .iter() - .map(|channel| channel.value[0]) - .fold(P::ZEROS, |cumul, limb| { - cumul * P::Scalar::from_canonical_u64(256) + limb - }); - yield_constr.constraint_transition(total_filter * (nv.program_counter - target)); + yield_constr + .constraint_transition(total_filter * (nv.program_counter - jumpdest_channel.value[0])); // Set kernel mode yield_constr.constraint_transition(total_filter * (nv.is_kernel_mode - P::ONES)); - // Maintain current context - yield_constr.constraint_transition(total_filter * (nv.context - lv.context)); // Reset gas counter to zero. yield_constr.constraint_transition(total_filter * nv.gas); @@ -197,61 +193,58 @@ pub(crate) fn eval_ext_circuit, const D: usize>( exc_jumptable_start, ); - for (i, channel) in lv.mem_channels[1..BYTES_PER_OFFSET + 1].iter().enumerate() { - // Set `used` and `is_read`. - { - let constr = builder.mul_sub_extension(total_filter, channel.used, total_filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(total_filter, channel.is_read, total_filter); - yield_constr.constraint(builder, constr); - } - - // Set kernel context and code segment - { - let constr = builder.mul_extension(total_filter, channel.addr_context); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -code_segment, - total_filter, - channel.addr_segment, - total_filter, - ); - yield_constr.constraint(builder, constr); - } - - // Set address, using a separate channel for each of the `BYTES_PER_OFFSET` limbs. - { - let diff_syscall = - builder.sub_extension(channel.addr_virtual, opcode_handler_addr_start); - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(i), - filter_syscall, - diff_syscall, - filter_syscall, - ); - yield_constr.constraint(builder, constr); - - let diff_exception = - builder.sub_extension(channel.addr_virtual, exc_handler_addr_start); - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(i), - filter_exception, - diff_exception, - filter_exception, - ); - yield_constr.constraint(builder, constr); - } + let jumpdest_channel = lv.mem_channels[1]; + + // Set `used` and `is_read`. + // The channel is not used: the reads will be done with the byte packing CTL. + { + let constr = builder.mul_extension(total_filter, jumpdest_channel.used); + yield_constr.constraint(builder, constr); + } + { + let constr = + builder.mul_sub_extension(total_filter, jumpdest_channel.is_read, total_filter); + yield_constr.constraint(builder, constr); + } + + // Set kernel context and code segment + { + let constr = builder.mul_extension(total_filter, jumpdest_channel.addr_context); + yield_constr.constraint(builder, constr); + } + { + let constr = builder.arithmetic_extension( + F::ONE, + -code_segment, + total_filter, + jumpdest_channel.addr_segment, + total_filter, + ); + yield_constr.constraint(builder, constr); + } + + // Set address. + { + let diff_syscall = + builder.sub_extension(jumpdest_channel.addr_virtual, opcode_handler_addr_start); + let constr = builder.mul_extension((filter_syscall), diff_syscall); + yield_constr.constraint(builder, constr); + } + { + let diff_exception = + builder.sub_extension(jumpdest_channel.addr_virtual, exc_handler_addr_start); + let constr = builder.mul_extension(filter_exception, diff_exception); + yield_constr.constraint(builder, constr); } - // Disable unused channels (the last channel is used to push to the stack) - for channel in &lv.mem_channels[BYTES_PER_OFFSET + 1..NUM_GP_CHANNELS] { + // Set higher limbs to zero. + for &limb in &jumpdest_channel.value[1..] { + let constr = builder.mul_extension(total_filter, limb); + yield_constr.constraint(builder, constr); + } + + // Disable unused channels + for channel in &lv.mem_channels[2..NUM_GP_CHANNELS] { let constr = builder.mul_extension(total_filter, channel.used); yield_constr.constraint(builder, constr); } @@ -259,13 +252,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( // Set program counter to the handler address // The addresses are big-endian in memory { - let target = lv.mem_channels[1..BYTES_PER_OFFSET + 1] - .iter() - .map(|channel| channel.value[0]) - .fold(builder.zero_extension(), |cumul, limb| { - builder.mul_const_add_extension(F::from_canonical_u64(256), cumul, limb) - }); - let diff = builder.sub_extension(nv.program_counter, target); + let diff = builder.sub_extension(nv.program_counter, jumpdest_channel.value[0]); let constr = builder.mul_extension(total_filter, diff); yield_constr.constraint_transition(builder, constr); } @@ -274,12 +261,6 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let constr = builder.mul_sub_extension(total_filter, nv.is_kernel_mode, total_filter); yield_constr.constraint_transition(builder, constr); } - // Maintain current context - { - let diff = builder.sub_extension(nv.context, lv.context); - let constr = builder.mul_extension(total_filter, diff); - yield_constr.constraint_transition(builder, constr); - } // Reset gas counter to zero. { let constr = builder.mul_extension(total_filter, nv.gas); diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index c9dea2307b..e87c1465e1 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -4,8 +4,8 @@ use keccak_hash::keccak; use plonky2::field::types::Field; use super::util::{ - byte_packing_log, byte_unpacking_log, mem_write_partial_log_and_fill, push_no_write, - push_with_write, + byte_packing_log, byte_unpacking_log, mem_read_with_log, mem_write_log, + mem_write_partial_log_and_fill, push_no_write, push_with_write, }; use crate::arithmetic::BinaryOperator; use crate::cpu::columns::CpuColumnsView; @@ -349,7 +349,7 @@ pub(crate) fn generate_get_context( Segment::Stack, state.registers.stack_len - 1, ); - let res = mem_write_gp_log_and_fill(3, address, state, &mut row, state.registers.stack_top); + let res = mem_write_gp_log_and_fill(2, address, state, &mut row, state.registers.stack_top); Some(res) }; push_no_write( @@ -380,7 +380,10 @@ pub(crate) fn generate_set_context( let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); - let log_write_old_sp = mem_write_gp_log_and_fill(1, old_sp_addr, state, &mut row, sp_to_save); + // This channel will hold in limb 0 and 1 the one-limb value of two separate memory operations: + // the old stack pointer write and the new stack pointer read. + // Channels only matter for time stamps: the write must happen before the read. + let log_write_old_sp = mem_write_log(GeneralPurpose(1), old_sp_addr, state, sp_to_save); let (new_sp, log_read_new_sp) = if old_ctx == new_ctx { let op = MemoryOp::new( MemoryChannel::GeneralPurpose(2), @@ -389,23 +392,9 @@ pub(crate) fn generate_set_context( MemoryOpKind::Read, sp_to_save, ); - - let channel = &mut row.mem_channels[2]; - assert_eq!(channel.used, F::ZERO); - channel.used = F::ONE; - channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_usize(new_ctx); - channel.addr_segment = F::from_canonical_usize(Segment::ContextMetadata.unscale()); - channel.addr_virtual = F::from_canonical_usize(new_sp_addr.virt); - let val_limbs: [u64; 4] = sp_to_save.0; - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - (sp_to_save, op) } else { - mem_read_gp_with_log_and_fill(2, new_sp_addr, state, &mut row) + mem_read_with_log(GeneralPurpose(2), new_sp_addr, state) }; // If the new stack isn't empty, read stack_top from memory. @@ -425,7 +414,7 @@ pub(crate) fn generate_set_context( let new_top_addr = MemoryAddress::new(new_ctx, Segment::Stack, new_sp - 1); let (new_top, log_read_new_top) = - mem_read_gp_with_log_and_fill(3, new_top_addr, state, &mut row); + mem_read_gp_with_log_and_fill(2, new_top_addr, state, &mut row); state.registers.stack_top = new_top; state.traces.push_memory(log_read_new_top); } else { @@ -705,27 +694,30 @@ pub(crate) fn generate_syscall( let handler_addr_addr = handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize); assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); - let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill( - 1, - MemoryAddress::new(0, Segment::Code, handler_addr_addr), - state, - &mut row, - ); - let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill( - 2, - MemoryAddress::new(0, Segment::Code, handler_addr_addr + 1), - state, - &mut row, - ); - let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill( - 3, - MemoryAddress::new(0, Segment::Code, handler_addr_addr + 2), - state, - &mut row, - ); + let base_address = MemoryAddress::new(0, Segment::Code, handler_addr_addr); + let bytes = (0..BYTES_PER_OFFSET as usize) + .map(|i| { + let address = MemoryAddress { + virt: base_address.virt + i, + ..base_address + }; + let val = state.memory.get(address); + val.low_u32() as u8 + }) + .collect_vec(); + + let packed_int = U256::from_big_endian(&bytes); + + let jumptable_channel = &mut row.mem_channels[1]; + jumptable_channel.is_read = F::ONE; + jumptable_channel.addr_context = F::ZERO; + jumptable_channel.addr_segment = F::from_canonical_usize(Segment::Code as usize); + jumptable_channel.addr_virtual = F::from_canonical_usize(handler_addr_addr); + jumptable_channel.value[0] = F::from_canonical_usize(u256_to_usize(packed_int)?); + + byte_packing_log(state, base_address, bytes); - let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; - let new_program_counter = u256_to_usize(handler_addr)?; + let new_program_counter = u256_to_usize(packed_int)?; let gas = U256::from(state.registers.gas_used); @@ -734,14 +726,15 @@ pub(crate) fn generate_syscall( + (gas << 192); // `ArithmeticStark` range checks `mem_channels[0]`, which contains - // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and - // next_row's `mem_channels[0]` which contains the next top of the stack. + // the top of the stack, `mem_channels[1]`, which contains the new PC, + // `mem_channels[2]`, which is empty, and next_row's `mem_channels[0]`, + // which contains the next top of the stack. // Our goal here is to range-check the gas, contained in syscall_info, // stored in the next stack top. let range_check_op = arithmetic::Operation::range_check( state.registers.stack_top, - handler_addr0, - handler_addr1, + packed_int, + U256::from(0), U256::from(opcode), syscall_info, ); @@ -757,9 +750,6 @@ pub(crate) fn generate_syscall( log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter)); state.traces.push_arithmetic(range_check_op); - state.traces.push_memory(log_in0); - state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); state.traces.push_cpu(row); Ok(()) @@ -950,27 +940,29 @@ pub(crate) fn generate_exception( let handler_addr_addr = handler_jumptable_addr + (exc_code as usize) * (BYTES_PER_OFFSET as usize); assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); - let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill( - 1, - MemoryAddress::new(0, Segment::Code, handler_addr_addr), - state, - &mut row, - ); - let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill( - 2, - MemoryAddress::new(0, Segment::Code, handler_addr_addr + 1), - state, - &mut row, - ); - let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill( - 3, - MemoryAddress::new(0, Segment::Code, handler_addr_addr + 2), - state, - &mut row, - ); + let base_address = MemoryAddress::new(0, Segment::Code, handler_addr_addr); + let bytes = (0..BYTES_PER_OFFSET as usize) + .map(|i| { + let address = MemoryAddress { + virt: base_address.virt + i, + ..base_address + }; + let val = state.memory.get(address); + val.low_u32() as u8 + }) + .collect_vec(); + + let packed_int = U256::from_big_endian(&bytes); + + let jumptable_channel = &mut row.mem_channels[1]; + jumptable_channel.is_read = F::ONE; + jumptable_channel.addr_context = F::ZERO; + jumptable_channel.addr_segment = F::from_canonical_usize(Segment::Code as usize); + jumptable_channel.addr_virtual = F::from_canonical_usize(handler_addr_addr); + jumptable_channel.value[0] = F::from_canonical_usize(u256_to_usize(packed_int)?); - let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; - let new_program_counter = u256_to_usize(handler_addr)?; + byte_packing_log(state, base_address, bytes); + let new_program_counter = u256_to_usize(packed_int)?; let gas = U256::from(state.registers.gas_used); @@ -982,14 +974,15 @@ pub(crate) fn generate_exception( let opcode = state.memory.get(address); // `ArithmeticStark` range checks `mem_channels[0]`, which contains - // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and - // next_row's `mem_channels[0]` which contains the next top of the stack. + // the top of the stack, `mem_channels[1]`, which contains the new PC, + // `mem_channels[2]`, which is empty, and next_row's `mem_channels[0]`, + // which contains the next top of the stack. // Our goal here is to range-check the gas, contained in syscall_info, // stored in the next stack top. let range_check_op = arithmetic::Operation::range_check( state.registers.stack_top, - handler_addr0, - handler_addr1, + packed_int, + U256::from(0), opcode, exc_info, ); @@ -1004,9 +997,6 @@ pub(crate) fn generate_exception( log::debug!("Exception to {}", KERNEL.offset_name(new_program_counter)); state.traces.push_arithmetic(range_check_op); - state.traces.push_memory(log_in0); - state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); state.traces.push_cpu(row); Ok(()) From ab05d181bcc837946b9d6dcf9518406c6e949671 Mon Sep 17 00:00:00 2001 From: BGluth Date: Thu, 11 Jan 2024 11:10:59 -0700 Subject: [PATCH 101/175] Bumped `eth_trie_utils` - Contains important fixes. --- evm/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index e328aa0c97..a6a996397f 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" anyhow = "1.0.40" bytes = "1.4.0" env_logger = "0.10.0" -eth_trie_utils = { git = "https://github.com/0xPolygonZero/eth_trie_utils.git", rev = "e9ec4ec2aa2ae976b7c699ef40c1ffc716d87ed5" } +eth_trie_utils = { git = "https://github.com/0xPolygonZero/eth_trie_utils.git", rev = "7fc3c3f54b3cec9c6fc5ffc5230910bd1cb77f76" } ethereum-types = "0.14.0" hex = { version = "0.4.3", optional = true } hex-literal = "0.4.1" From 7f5fae844c3bc1f79ca3f0e4b0196876c5a04e70 Mon Sep 17 00:00:00 2001 From: Paul Gebheim Date: Thu, 11 Jan 2024 12:40:10 -0800 Subject: [PATCH 102/175] Add files via upload --- evm/LICENSE-APACHE | 176 +++++++++++++++++++++++++++++++++++++++++++++ evm/LICENSE-MIT | 19 +++++ 2 files changed, 195 insertions(+) create mode 100644 evm/LICENSE-APACHE create mode 100644 evm/LICENSE-MIT diff --git a/evm/LICENSE-APACHE b/evm/LICENSE-APACHE new file mode 100644 index 0000000000..1b5ec8b78e --- /dev/null +++ b/evm/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/evm/LICENSE-MIT b/evm/LICENSE-MIT new file mode 100644 index 0000000000..72dc60d84b --- /dev/null +++ b/evm/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From de2709d82ef71bccb8aeaa2ff23a85d9a8ad75b7 Mon Sep 17 00:00:00 2001 From: Paul Gebheim Date: Thu, 11 Jan 2024 12:42:54 -0800 Subject: [PATCH 103/175] Update README.md Add License / Contributing to readme --- evm/README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/evm/README.md b/evm/README.md index 5293f8ba8a..a5c201550b 100644 --- a/evm/README.md +++ b/evm/README.md @@ -20,5 +20,17 @@ Audits for the ZK-EVM will begin on November 27th, 2023. See the [Audit RC1 Mile The current specification is located in the [/spec](/spec) directory, with the most currently up-to-date PDF [available here](https://github.com/0xPolygonZero/plonky2/blob/main/evm/spec/zkevm.pdf). Further documentation will be made over the coming months. ---- -Copyright (C) 2023 PT Services DMCC +## License +Copyright (c) 2023 PT Services DMCC + +Licensed under either of: +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +The SPDX license identifier for this project is `MIT OR Apache-2.0`. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. From ccd4ff872b7682dcd51799027d7c92101293e8fc Mon Sep 17 00:00:00 2001 From: Paul Gebheim Date: Thu, 11 Jan 2024 13:10:18 -0800 Subject: [PATCH 104/175] Update Cargo.toml Add license field to cargo manifest --- evm/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index a6a996397f..e3ca8c1af3 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -2,6 +2,7 @@ name = "plonky2_evm" description = "Implementation of STARKs for the Ethereum Virtual Machine" version = "0.1.1" +license = "MIT or Apache-2.0" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" repository = "https://github.com/0xPolygonZero/plonky2" From 219365d61b4a361487a6857e2d7f1d255ab00420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Fri, 12 Jan 2024 13:02:32 +0100 Subject: [PATCH 105/175] Packed rlp prover inputs (#1460) * Pack rlp prover inputs * Fix endianness bug * Remove debug info and fix clippy --- evm/src/cpu/kernel/asm/rlp/read_to_memory.asm | 21 ++++++++----------- evm/src/generation/rlp.rs | 10 +++++++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm index 7593537194..8070fd0beb 100644 --- a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm +++ b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm @@ -2,7 +2,7 @@ // segment of memory. // Pre stack: retdest -// Post stack: (empty) +// Post stack: txn_rlp_len global read_rlp_to_memory: // stack: retdest @@ -13,21 +13,18 @@ global read_rlp_to_memory: PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 // stack: addr, final_addr, retdest - read_rlp_to_memory_loop: // stack: addr, final_addr, retdest DUP2 DUP2 - EQ - // stack: addr == final_addr, addr, final_addr, retdest + LT + ISZERO + // stack: addr >= final_addr, addr, final_addr, retdest %jumpi(read_rlp_to_memory_finish) - // stack: addr, len, retdest - DUP1 - PROVER_INPUT(rlp) - // stack: byte, addr, addr, final_addr, retdest - MSTORE_GENERAL // stack: addr, final_addr, retdest - %increment + PROVER_INPUT(rlp) + SWAP1 + MSTORE_32BYTES_32 // stack: addr', final_addr, retdest %jump(read_rlp_to_memory_loop) @@ -35,7 +32,7 @@ read_rlp_to_memory_finish: // stack: addr, final_addr, retdest // we recover the offset here PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - DUP2 SUB + DUP3 SUB // stack: pos, addr, final_addr, retdest %stack(pos, addr, final_addr, retdest) -> (retdest, pos) - JUMP + JUMP \ No newline at end of file diff --git a/evm/src/generation/rlp.rs b/evm/src/generation/rlp.rs index 6a6b4b13c1..ffc302fd54 100644 --- a/evm/src/generation/rlp.rs +++ b/evm/src/generation/rlp.rs @@ -9,8 +9,14 @@ pub(crate) fn all_rlp_prover_inputs_reversed(signed_txn: &[u8]) -> Vec { fn all_rlp_prover_inputs(signed_txn: &[u8]) -> Vec { let mut prover_inputs = vec![]; prover_inputs.push(signed_txn.len().into()); - for &byte in signed_txn { - prover_inputs.push(byte.into()); + let mut chunks = signed_txn.chunks_exact(32); + for bytes in chunks.by_ref() { + prover_inputs.push(U256::from_big_endian(bytes)); + } + let mut last_chunk = chunks.remainder().to_vec(); + if !last_chunk.is_empty() { + last_chunk.extend_from_slice(&vec![0u8; 32 - last_chunk.len()]); + prover_inputs.push(U256::from_big_endian(&last_chunk)); } prover_inputs } From c2a73ad89ffb02490f7374fc80a6eff4e442f38e Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:09:19 +0100 Subject: [PATCH 106/175] Fix clippy (#1464) --- starky/src/prover.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 866bb6357a..56154d9105 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -29,6 +29,7 @@ use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; +#[allow(clippy::useless_asref)] pub fn prove( stark: S, config: &StarkConfig, From 70483050252e403409bc676ab2572629f4d09661 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:35:41 +0100 Subject: [PATCH 107/175] Fix fill_gaps (#1465) --- evm/src/memory/memory_stark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 1283965b18..13ea1166f0 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -199,7 +199,7 @@ impl, const D: usize> MemoryStark { while next.address.virt > max_rc { let mut dummy_address = next.address; dummy_address.virt -= max_rc; - let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0, next.value); + let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0, U256::zero()); memory_ops.push(dummy_read); next = dummy_read; } From bb48cabdb19c0de801255b2a5418afc459f691f5 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:07:18 +0100 Subject: [PATCH 108/175] Add math rendering with Katex (#1459) --- .cargo/katex-header.html | 30 ++++++++++++++++++++++++ evm/.cargo/katex-header.html | 1 + evm/Cargo.toml | 4 ++++ field/.cargo/katex-header.html | 1 + field/Cargo.toml | 4 ++++ maybe_rayon/.cargo/katex-header.html | 1 + maybe_rayon/Cargo.toml | 4 ++++ plonky2/.cargo/katex-header.html | 1 + plonky2/Cargo.toml | 4 ++++ plonky2/src/gates/coset_interpolation.rs | 25 +++++++++++--------- plonky2/src/gates/gate.rs | 13 +++++----- plonky2/src/gates/mod.rs | 11 +++++---- starky/.cargo/katex-header.html | 1 + starky/Cargo.toml | 4 ++++ util/.cargo/katex-header.html | 1 + util/Cargo.toml | 4 ++++ 16 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 .cargo/katex-header.html create mode 100644 evm/.cargo/katex-header.html create mode 100644 field/.cargo/katex-header.html create mode 100644 maybe_rayon/.cargo/katex-header.html create mode 100644 plonky2/.cargo/katex-header.html create mode 100644 starky/.cargo/katex-header.html create mode 100644 util/.cargo/katex-header.html diff --git a/.cargo/katex-header.html b/.cargo/katex-header.html new file mode 100644 index 0000000000..5db5bc0b19 --- /dev/null +++ b/.cargo/katex-header.html @@ -0,0 +1,30 @@ + + + + \ No newline at end of file diff --git a/evm/.cargo/katex-header.html b/evm/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/evm/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/evm/Cargo.toml b/evm/Cargo.toml index a6a996397f..8701a279d0 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -59,3 +59,7 @@ required-features = ["asmtools"] [[bench]] name = "stack_manipulation" harness = false + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/field/.cargo/katex-header.html b/field/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/field/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/field/Cargo.toml b/field/Cargo.toml index ed5ef27bc2..72408c4946 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -15,3 +15,7 @@ rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/maybe_rayon/.cargo/katex-header.html b/maybe_rayon/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/maybe_rayon/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/maybe_rayon/Cargo.toml b/maybe_rayon/Cargo.toml index 89499e7423..e436563215 100644 --- a/maybe_rayon/Cargo.toml +++ b/maybe_rayon/Cargo.toml @@ -10,3 +10,7 @@ parallel = ["rayon"] [dependencies] rayon = { version = "1.5.3", optional = true } + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/plonky2/.cargo/katex-header.html b/plonky2/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/plonky2/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index ad586679de..d2727853b8 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -78,3 +78,7 @@ harness = false [[bench]] name = "reverse_index_bits" harness = false + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index 331f18205f..ab69f698be 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -29,23 +29,26 @@ use crate::util::serialization::{Buffer, IoResult, Read, Write}; /// - the values that the interpolated polynomial takes on the coset /// - the evaluation point /// -/// The evaluation strategy is based on the observation that if P(X) is the interpolant of some -/// values over a coset and P'(X) is the interpolant of those values over the subgroup, then -/// P(X) = P'(X `shift`^{-1}). Interpolating P'(X) is preferable because when subgroup is fixed +/// The evaluation strategy is based on the observation that if $P(X)$ is the interpolant of some +/// values over a coset and $P'(X)$ is the interpolant of those values over the subgroup, then +/// $P(X) = P'(X \cdot \mathrm{shift}^{-1})$. Interpolating $P'(X)$ is preferable because when subgroup is fixed /// then so are the Barycentric weights and both can be hardcoded into the constraint polynomials. /// /// A full interpolation of N values corresponds to the evaluation of a degree-N polynomial. This /// gate can however be configured with a bounded degree of at least 2 by introducing more -/// non-routed wires. Let x[] be the domain points, v[] be the values, w[] be the Barycentric -/// weights and z be the evaluation point. Define the sequences +/// non-routed wires. Let $x[]$ be the domain points, $v[]$ be the values, $w[]$ be the Barycentric +/// weights and $z$ be the evaluation point. Define the sequences /// -/// p[0] = 1 -/// p[i] = p[i - 1] * (z - x[i - 1]) -/// e[0] = 0, -/// e[i] = e[i - 1] * (z - x[i - 1]) + w[i - 1] * v[i - 1] * p[i - 1] +/// $p[0] = 1,$ /// -/// Then e[N] is the final interpolated value. The non-routed wires hold every (d - 1)'th -/// intermediate value of p and e, starting at p[d] and e[d], where d is the gate degree. +/// $p[i] = p[i - 1] \cdot (z - x[i - 1]),$ +/// +/// $e[0] = 0,$ +/// +/// $e[i] = e[i - 1] ] \cdot (z - x[i - 1]) + w[i - 1] \cdot v[i - 1] \cdot p[i - 1]$ +/// +/// Then $e[N]$ is the final interpolated value. The non-routed wires hold every $(d - 1)$'th +/// intermediate value of $p$ and $e$, starting at $p[d]$ and $e[d]$, where $d$ is the gate degree. #[derive(Clone, Debug, Default)] pub struct CosetInterpolationGate, const D: usize> { pub subgroup_bits: usize, diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 3087b74cc3..6dbed3fb02 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -30,16 +30,17 @@ use crate::util::serialization::{Buffer, IoResult}; /// Vanilla Plonk arithmetization only supports basic fan-in 2 / fan-out 1 arithmetic gates, /// each of the form /// -/// $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$ +/// $$ a.b \cdot q_M + a \cdot q_L + b \cdot q_R + c \cdot q_O + q_C = 0 $$ /// /// where: -/// - q_M, q_L, q_R and q_O are boolean selectors, -/// - a, b and c are values used as inputs and output respectively, -/// - q_C is a constant (possibly 0). +/// - $q_M$, $q_L$, $q_R$ and $q_O$ are boolean selectors, +/// - $a$, $b$ and $c$ are values used as inputs and output respectively, +/// - $q_C$ is a constant (possibly 0). /// /// This allows expressing simple operations like multiplication, addition, etc. For -/// instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. -/// Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. +/// instance, to define a multiplication, one can set $q_M=1$, $q_L=q_R=0$, $q_O = -1$ and $q_C = 0$. +/// +/// Hence, the gate equation simplifies to $a.b - c = 0$, or equivalently to $a.b = c$. /// /// However, such a gate is fairly limited for more complex computations. Hence, when a computation may /// require too many of these "vanilla" gates, or when a computation arises often within the same circuit, diff --git a/plonky2/src/gates/mod.rs b/plonky2/src/gates/mod.rs index b490e12e37..e349cf7568 100644 --- a/plonky2/src/gates/mod.rs +++ b/plonky2/src/gates/mod.rs @@ -6,13 +6,14 @@ //! $$ a.b.q_M + a.q_L + b.q_R + c.q_O + q_C = 0 $$ //! //! where: -//! - q_M, q_L, q_R and q_O are boolean selectors, -//! - a, b and c are values used as inputs and output respectively, -//! - q_C is a constant (possibly 0). +//! - $q_M$, $q_L$, $q_R$ and $q_O$ are boolean selectors, +//! - $a$, $b$ and $c$ are values used as inputs and output respectively, +//! - $q_C$ is a constant (possibly 0). //! //! This allows expressing simple operations like multiplication, addition, etc. For -//! instance, to define a multiplication, one can set q_M=1, q_L=q_R=0, q_O = -1 and q_C = 0. -//! Hence, the gate equation simplifies to a.b - c = 0, or a.b = c. +//! instance, to define a multiplication, one can set $q_M=1$, $q_L=q_R=0$, $q_O = -1$ and $q_C = 0$. +//! +//! Hence, the gate equation simplifies to $a.b - c = 0$, or equivalently to $a.b = c$. //! //! However, such a gate is fairly limited for more complex computations. Hence, when a computation may //! require too many of these "vanilla" gates, or when a computation arises often within the same circuit, diff --git a/starky/.cargo/katex-header.html b/starky/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/starky/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 62a67ee78e..a3f9b37c5d 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -25,3 +25,7 @@ plonky2 = { path = "../plonky2", default-features = false } [dev-dependencies] env_logger = { version = "0.9.0", default-features = false } + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/util/.cargo/katex-header.html b/util/.cargo/katex-header.html new file mode 100644 index 0000000000..20723b5d27 --- /dev/null +++ b/util/.cargo/katex-header.html @@ -0,0 +1 @@ +../../.cargo/katex-header.html \ No newline at end of file diff --git a/util/Cargo.toml b/util/Cargo.toml index 1ece1e574f..758391c3b9 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } + +# Display math equations properly in documentation +[package.metadata.docs.rs] +rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] From c4319dcee6a809d505b6a2dd77e5fa6307c2ec55 Mon Sep 17 00:00:00 2001 From: Ratan Kaliani Date: Fri, 12 Jan 2024 10:25:31 -0800 Subject: [PATCH 109/175] fix: make add_generators public (#1463) --- plonky2/src/plonk/circuit_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 2e0904e434..4c2a536905 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -546,7 +546,7 @@ impl, const D: usize> CircuitBuilder { self.connect(x, one); } - fn add_generators(&mut self, generators: Vec>) { + pub fn add_generators(&mut self, generators: Vec>) { self.generators.extend(generators); } From 5c1ec524d3de53ed1adf1c3f35252364090d9ad3 Mon Sep 17 00:00:00 2001 From: Ursulafe <152976968+Ursulafe@users.noreply.github.com> Date: Sat, 13 Jan 2024 18:31:07 +0100 Subject: [PATCH 110/175] proofreading (#1466) --- evm/spec/framework.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/spec/framework.tex b/evm/spec/framework.tex index 8fc6c2f892..c20e46db67 100644 --- a/evm/spec/framework.tex +++ b/evm/spec/framework.tex @@ -86,14 +86,14 @@ \subsubsection{What to range-check?} \item Syscalls, exceptions and prover inputs are range-checked in ``ArithmeticStark''. \item The inputs and outputs of binary and ternary arithmetic operations are range-checked in ``ArithmeticStark''. \item The inputs' bits of logic operations are checked to be either 1 or 0 in ``LogicStark''. Since ``LogicStark'' only deals with bitwise operations, this is enough to have range-checked outputs as well. - \item The inputs of Keccak operations are range-checked in ``KeccakStark''. The output digest is written as bytes in ``KeccakStark''. Those bytes are used to reconstruct the associated 32-bit limbs checked against the limbs in ``CpuStark''. This implictly ensures that the output is range-checked. + \item The inputs of Keccak operations are range-checked in ``KeccakStark''. The output digest is written as bytes in ``KeccakStark''. Those bytes are used to reconstruct the associated 32-bit limbs checked against the limbs in ``CpuStark''. This implicitly ensures that the output is range-checked. \end{enumerate} Note that some operations do not require a range-check: \begin{enumerate} \item ``MSTORE\_GENERAL'' read the value to write from the stack. Thus, the written value was already range-checked by a previous push. \item ``EQ'' reads two -- already range-checked -- elements on the stack, and checks they are equal. The output is either 0 or 1, and does therefore not need to be checked. \item ``NOT'' reads one -- already range-checked -- element. The result is constrained to be equal to $\texttt{0xFFFFFFFF} - \texttt{input}$, which implicitly enforces the range check. - \item ``PC'': the program counter cannot be greater than $2^{32}$ in user mode. Indeed, the user code cannot be longer than $2^{32}$, and jumps are constrainted to be JUMPDESTs. Moreover, in kernel mode, every jump is towards a location within the kernel, and the kernel code is smaller than $2^{32}$. These two points implicitly enforce $PC$'s range check. + \item ``PC'': the program counter cannot be greater than $2^{32}$ in user mode. Indeed, the user code cannot be longer than $2^{32}$, and jumps are constrained to be JUMPDESTs. Moreover, in kernel mode, every jump is towards a location within the kernel, and the kernel code is smaller than $2^{32}$. These two points implicitly enforce $PC$'s range check. \item ``GET\_CONTEXT'', ``DUP'' and ``SWAP'' all read and push values that were already written in memory. The pushed values were therefore already range-checked. \end{enumerate} Range-checks are performed on the range $[0, 2^{16} - 1]$, to limit the trace length. @@ -120,7 +120,7 @@ \subsubsection{Lookup Argument} d_i = \frac{1}{\alpha + t_i} \end{gather*} -The $h$ helper columns can be batched together to save columns. We can batch at most $\texttt{contraint\_degree} - 1$ helper functions together. In our case, we batch them 2 by 2. At row $i$, we now have: +The $h$ helper columns can be batched together to save columns. We can batch at most $\texttt{constraint\_degree} - 1$ helper functions together. In our case, we batch them 2 by 2. At row $i$, we now have: \begin{align*} h_i^k = \frac{1}{\alpha + s_i^{2k}} + \frac{1}{\alpha + s_i^{2k+1}} \forall 1 \leq k \leq c/2 \\ \end{align*} From 30b47998262642be54da5acf03dfca31af4d93f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Mon, 15 Jan 2024 15:54:25 +0100 Subject: [PATCH 111/175] Fix simulation for jumpdest analysis (#1467) --- evm/src/generation/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index e14de9b98a..c8f2dac10d 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -365,7 +365,9 @@ fn simulate_cpu_between_labels_and_get_user_jumps( loop { // skip jumpdest table validations in simulations - if state.registers.program_counter == KERNEL.global_labels["jumpdest_analysis"] { + if state.registers.is_kernel + && state.registers.program_counter == KERNEL.global_labels["jumpdest_analysis"] + { state.registers.program_counter = KERNEL.global_labels["jumpdest_analysis_end"] } let pc = state.registers.program_counter; From 990eb34d96a0f1544f90073102297af78ce18f37 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:00:55 -0500 Subject: [PATCH 112/175] Remove some CPU cycles (#1469) * Amortize mload_packing * Reduce stack overhead * Amortize mstore_unpacking * Speed-up stack operation in hash.asm * Misc * Small tweaks * Misc small optims * Fix comments * Fix main access to withdrawals * Fix stack description * minor: rename label * Comments --------- Co-authored-by: Linda Guiga --- evm/src/cpu/kernel/asm/account_code.asm | 12 ++--- evm/src/cpu/kernel/asm/balance.asm | 6 +-- .../cpu/kernel/asm/core/create_addresses.asm | 15 +++--- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 4 +- .../kernel/asm/core/precompiles/blake2_f.asm | 2 +- .../kernel/asm/core/precompiles/bn_add.asm | 20 +++---- .../kernel/asm/core/precompiles/bn_mul.asm | 17 +++--- .../cpu/kernel/asm/core/precompiles/ecrec.asm | 15 +++--- .../kernel/asm/core/precompiles/expmod.asm | 12 ++--- .../kernel/asm/core/precompiles/rip160.asm | 5 +- .../kernel/asm/core/precompiles/sha256.asm | 5 +- .../kernel/asm/core/precompiles/snarkv.asm | 29 +++++----- evm/src/cpu/kernel/asm/main.asm | 27 ++++++---- evm/src/cpu/kernel/asm/memory/core.asm | 14 ++--- evm/src/cpu/kernel/asm/memory/packing.asm | 19 +------ evm/src/cpu/kernel/asm/memory/syscalls.asm | 6 +-- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 41 +++++++------- evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 10 ++-- .../asm/mpt/insert/insert_trie_specific.asm | 2 +- evm/src/cpu/kernel/asm/mpt/util.asm | 8 +-- evm/src/cpu/kernel/asm/rlp/encode.asm | 24 +++------ .../kernel/asm/rlp/increment_bounded_rlp.asm | 14 ++--- evm/src/cpu/kernel/asm/util/keccak.asm | 10 ++-- evm/src/cpu/kernel/tests/packing.rs | 54 ------------------- evm/src/cpu/kernel/tests/rlp/encode.rs | 12 ++--- 25 files changed, 153 insertions(+), 230 deletions(-) diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 6aedbd2e39..2654bedc7b 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -2,13 +2,13 @@ global sys_extcodehash: // stack: kexit_info, address SWAP1 %u256_to_addr // stack: address, kexit_info - DUP1 %insert_accessed_addresses - // stack: cold_access, address, kexit_info + SWAP1 + DUP2 %insert_accessed_addresses + // stack: cold_access, kexit_info, address PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS MUL PUSH @GAS_WARMACCESS ADD - %stack (gas, address, kexit_info) -> (gas, kexit_info, address) %charge_gas // stack: kexit_info, address @@ -57,13 +57,13 @@ global sys_extcodesize: // stack: kexit_info, address SWAP1 %u256_to_addr // stack: address, kexit_info - DUP1 %insert_accessed_addresses - // stack: cold_access, address, kexit_info + SWAP1 + DUP2 %insert_accessed_addresses + // stack: cold_access, kexit_info, address PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS MUL PUSH @GAS_WARMACCESS ADD - %stack (gas, address, kexit_info) -> (gas, kexit_info, address) %charge_gas // stack: kexit_info, address diff --git a/evm/src/cpu/kernel/asm/balance.asm b/evm/src/cpu/kernel/asm/balance.asm index f175d027c9..d39f660630 100644 --- a/evm/src/cpu/kernel/asm/balance.asm +++ b/evm/src/cpu/kernel/asm/balance.asm @@ -2,13 +2,13 @@ global sys_balance: // stack: kexit_info, address SWAP1 %u256_to_addr // stack: address, kexit_info - DUP1 %insert_accessed_addresses - // stack: cold_access, address, kexit_info + SWAP1 + DUP2 %insert_accessed_addresses + // stack: cold_access, kexit_info, address PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS MUL PUSH @GAS_WARMACCESS ADD - %stack (gas, address, kexit_info) -> (gas, kexit_info, address) %charge_gas // stack: kexit_info, address diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 1cd50f661a..77b6f6044e 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -38,20 +38,17 @@ global get_create_address: global get_create2_address: // stack: sender, code_hash, salt, retdest PUSH 0xff PUSH 0 %mstore_kernel_general - %stack (sender, code_hash, salt, retdest) -> (@SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + %stack (sender, code_hash, salt, retdest) -> (@SEGMENT_KERNEL_GENERAL, 1, sender, salt, code_hash, retdest) ADD - %jump(mstore_unpacking) -get_create2_address_contd: + MSTORE_32BYTES_20 POP - %stack (salt, code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + %stack (salt, code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 21, salt, code_hash, retdest) ADD - %jump(mstore_unpacking) -get_create2_address_contd2: + MSTORE_32BYTES_32 POP - %stack (code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + %stack (code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 53, code_hash, retdest) ADD - %jump(mstore_unpacking) -get_create2_address_finish: + MSTORE_32BYTES_32 POP %stack (retdest) -> (@SEGMENT_KERNEL_GENERAL, 85, retdest) // offset == context == 0 // addr, len, retdest diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 9dee5d2b88..efcd66420f 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -42,7 +42,7 @@ continue: proof_ok: // stack: i, ctx, final_pos, retdest // We already know final_pos is a jumpdest - %stack (i, ctx, final_pos) -> (ctx, @SEGMENT_JUMPDEST_BITS, i) + %stack (i, ctx, final_pos) -> (ctx, @SEGMENT_JUMPDEST_BITS, final_pos) %build_address PUSH 1 MSTORE_GENERAL @@ -145,7 +145,7 @@ global write_table_if_jumpdest: (proof_prefix_addr, ctx) -> (ctx, proof_prefix_addr, 32, proof_prefix_addr, ctx) ADD // combine context and offset to make an address (SEGMENT_CODE == 0) - %mload_packing + MLOAD_32BYTES // packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest DUP1 %shl_const(1) DUP2 %shl_const(2) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm index 500548eff5..91d4b3960f 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm @@ -105,7 +105,7 @@ global precompile_blake2_f: GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info %build_address_no_offset - %mload_packing + MLOAD_32BYTES // stack: rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info DUP1 diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm index dcd641a327..9554044eff 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm @@ -14,32 +14,32 @@ global precompile_bn_add: %charge_gas_const(@BN_ADD_GAS) - // Load x0, y0, x1, y1 from the call data using `mload_packing`. + // Load x0, y0, x1, y1 from the call data using `MLOAD_32BYTES`. PUSH bn_add_return // stack: bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, bn_add_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, y1, bn_add_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: x1, y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, x1, y1, bn_add_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: y0, x1, y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, y0, x1, y1, bn_add_return, kexit_info %build_address_no_offset - %mload_packing + MLOAD_32BYTES // stack: x0, y0, x1, y1, bn_add_return, kexit_info %jump(bn_add) bn_add_return: @@ -53,11 +53,11 @@ bn_add_return: // Store the result (x, y) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, x, 32, bn_add_contd6, parent_ctx, y) + %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, x, parent_ctx, y) %build_address_no_offset - %jump(mstore_unpacking) -bn_add_contd6: + MSTORE_32BYTES_32 POP - %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y, 32, pop_and_return_success) + %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y) %build_address - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm index df2e27e99f..5872e17f26 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm @@ -14,26 +14,26 @@ global precompile_bn_mul: %charge_gas_const(@BN_MUL_GAS) - // Load x, y, n from the call data using `mload_packing`. + // Load x, y, n from the call data using `MLOAD_32BYTES`. PUSH bn_mul_return // stack: bn_mul_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, bn_mul_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: n, bn_mul_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, n, bn_mul_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: y, n, bn_mul_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, y, n, bn_mul_return, kexit_info %build_address_no_offset - %mload_packing + MLOAD_32BYTES // stack: x, y, n, bn_mul_return, kexit_info %jump(bn_mul) bn_mul_return: @@ -47,11 +47,12 @@ bn_mul_return: // Store the result (Px, Py) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, Px, 32, bn_mul_contd6, parent_ctx, Py) + %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, Px, parent_ctx, Py) %build_address_no_offset - %jump(mstore_unpacking) + MSTORE_32BYTES_32 bn_mul_contd6: POP - %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py, 32, pop_and_return_success) + %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py) %build_address - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm index baa661962d..6c141aabc5 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm @@ -14,32 +14,32 @@ global precompile_ecrec: %charge_gas_const(@ECREC_GAS) - // Load hash, v, r, s from the call data using `mload_packing`. + // Load hash, v, r, s from the call data using `MLOAD_32BYTES`. PUSH ecrec_return // stack: ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, ecrec_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, s, ecrec_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: r, s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, r, s, ecrec_return, kexit_info %build_address - %mload_packing + MLOAD_32BYTES // stack: v, r, s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, v, r, s, ecrec_return, kexit_info %build_address_no_offset - %mload_packing + MLOAD_32BYTES // stack: hash, v, r, s, ecrec_return, kexit_info %jump(ecrecover) ecrec_return: @@ -49,9 +49,10 @@ ecrec_return: // Store the result address to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address) %build_address_no_offset - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) // On bad input, return empty return data but still return success. ecrec_bad_input: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm index 52bb220dab..6bff54ea4e 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm @@ -18,7 +18,7 @@ mload_bytes_as_limbs: // stack: min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs DUP2 // stack: addr, min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs - %mload_packing + MLOAD_32BYTES // stack: new_limb, addr, num_bytes, retdest, total_num_limbs, len, ..limbs %stack (new, addr, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) // stack: num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs @@ -113,7 +113,7 @@ calculate_l_E_prime: PUSH @SEGMENT_CALLDATA GET_CONTEXT %build_address - %mload_packing + MLOAD_32BYTES // stack: i[96 + l_B..128 + l_B], l_E, l_B, retdest %log2_floor // stack: log2(i[96 + l_B..128 + l_B]), l_E, l_B, retdest @@ -144,7 +144,7 @@ case_le_32: PUSH @SEGMENT_CALLDATA GET_CONTEXT %build_address - %mload_packing + MLOAD_32BYTES // stack: E, retdest %log2_floor // stack: log2(E), retdest @@ -172,21 +172,21 @@ global precompile_expmod: GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, kexit_info %build_address_no_offset - %mload_packing + MLOAD_32BYTES // stack: l_B, kexit_info // Load l_E from i[32..64]. %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT %build_address - %mload_packing + MLOAD_32BYTES // stack: l_E, l_B, kexit_info // Load l_M from i[64..96]. %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT %build_address - %mload_packing + MLOAD_32BYTES // stack: l_M, l_E, l_B, kexit_info DUP3 ISZERO DUP2 ISZERO MUL // AND diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm index 0231baf0eb..e57504961b 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm @@ -44,6 +44,7 @@ rip160_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash) %build_address_no_offset - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm index b537cd4c7b..3c926f0bbd 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm @@ -44,6 +44,7 @@ sha256_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash) %build_address_no_offset - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm index 2d990d09d9..23ad9eb17d 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm @@ -30,47 +30,47 @@ loading_loop: DUP1 %mul_const(192) // stack: px, i, k, kexit_info GET_CONTEXT - %stack (ctx, px) -> (ctx, @SEGMENT_CALLDATA, px, 32, loading_loop_contd, px) + %stack (ctx, px) -> (ctx, @SEGMENT_CALLDATA, px, 32, px) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd: // stack: x, px, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT - %stack (ctx, py) -> (ctx, @SEGMENT_CALLDATA, py, 32, loading_loop_contd2, py) + %stack (ctx, py) -> (ctx, @SEGMENT_CALLDATA, py, 32, py) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd2: // stack: y, py, x, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT - %stack (ctx, px_im) -> (ctx, @SEGMENT_CALLDATA, px_im, 32, loading_loop_contd3, px_im) + %stack (ctx, px_im) -> (ctx, @SEGMENT_CALLDATA, px_im, 32, px_im) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd3: // stack: x_im, px_im, y, x, i, k, kexit_info SWAP1 %add_const(32) // stack: px_re, x_im, y, x, i, k, kexit_info GET_CONTEXT - %stack (ctx, px_re) -> (ctx, @SEGMENT_CALLDATA, px_re, 32, loading_loop_contd4, px_re) + %stack (ctx, px_re) -> (ctx, @SEGMENT_CALLDATA, px_re, 32, px_re) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd4: // stack: x_re, px_re, x_im, y, x, i, k, kexit_info SWAP1 %add_const(32) // stack: py_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT - %stack (ctx, py_im) -> (ctx, @SEGMENT_CALLDATA, py_im, 32, loading_loop_contd5, py_im) + %stack (ctx, py_im) -> (ctx, @SEGMENT_CALLDATA, py_im, 32, py_im) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd5: // stack: y_im, py_im, x_re, x_im, y, x, i, k, kexit_info SWAP1 %add_const(32) // stack: py_re, y_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT - %stack (ctx, py_re) -> (ctx, @SEGMENT_CALLDATA, py_re, 32, loading_loop_contd6) + %stack (ctx, py_re) -> (ctx, @SEGMENT_CALLDATA, py_re, 32) %build_address - %jump(mload_packing) + MLOAD_32BYTES loading_loop_contd6: // stack: y_re, y_im, x_re, x_im, y, x, i, k, kexit_info SWAP1 // the EVM serializes the imaginary part first @@ -124,6 +124,7 @@ got_result: // Store the result bool (repr. by a U256) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address) %build_address_no_offset - %jump(mstore_unpacking) + MSTORE_32BYTES_32 + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 270478126e..d78152f4be 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -47,14 +47,15 @@ global start_txn: // is handled outside of the kernel. %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) // stack: txn_nb - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE) - // stack: init_used_gas, txn_nb - DUP2 %scalar_to_rlp - // stack: txn_counter, init_gas_used, txn_nb + DUP1 %scalar_to_rlp + // stack: txn_counter, txn_nb DUP1 %num_bytes %mul_const(2) - // stack: num_nibbles, txn_counter, init_gas_used, txn_nb - SWAP2 - // stack: init_gas_used, txn_counter, num_nibbles, txn_nb + // stack: num_nibbles, txn_counter, txn_nb + %increment_bounded_rlp + // stack: txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE) + + // stack: init_gas_used, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb // If the prover has no txn for us to process, halt. PROVER_INPUT(no_txn) @@ -62,9 +63,9 @@ global start_txn: // Call route_txn. When we return, we will process the txn receipt. PUSH txn_after - // stack: retdest, prev_gas_used, txn_counter, num_nibbles, txn_nb - DUP4 DUP4 %increment_bounded_rlp - %stack (next_txn_counter, next_num_nibbles, retdest, prev_gas_used, txn_counter, num_nibbles) -> (txn_counter, num_nibbles, retdest, prev_gas_used, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles) + // stack: retdest, prev_gas_used, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb + DUP4 DUP4 + %jump(route_txn) global txn_after: @@ -72,10 +73,14 @@ global txn_after: %process_receipt // stack: new_cum_gas, txn_counter, num_nibbles, txn_nb SWAP3 %increment SWAP3 + %jump(execute_withdrawals_post_stack_op) global execute_withdrawals: - // stack: cum_gas, txn_counter, num_nibbles, txn_nb + // stack: cum_gas, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb + %stack (cum_gas, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles) -> (cum_gas, txn_counter, num_nibbles) +execute_withdrawals_post_stack_op: %withdrawals + global hash_final_tries: // stack: cum_gas, txn_counter, num_nibbles, txn_nb // Check that we end up with the correct `cum_gas`, `txn_nb` and bloom filter. diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index eef7ee1a49..74b49f2780 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -1,9 +1,8 @@ // Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mload_u32 // stack: addr - %stack (addr) -> (addr, 4, %%after) - %jump(mload_packing) -%%after: + %stack (addr) -> (addr, 4) + MLOAD_32BYTES %endmacro // Load a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3). @@ -51,17 +50,14 @@ // Load a big-endian u256. %macro mload_u256 // stack: addr - %stack (addr) -> (addr, 32, %%after) - %jump(mload_packing) -%%after: + %stack (addr) -> (addr, 32) + MLOAD_32BYTES %endmacro // Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mstore_u32 // stack: addr, value - %stack (addr, value) -> (addr, value, 4, %%after) - %jump(mstore_unpacking) -%%after: + MSTORE_32BYTES_4 // stack: offset POP %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/packing.asm b/evm/src/cpu/kernel/asm/memory/packing.asm index 9b2d6cdd3b..a1bf5a09ad 100644 --- a/evm/src/cpu/kernel/asm/memory/packing.asm +++ b/evm/src/cpu/kernel/asm/memory/packing.asm @@ -1,22 +1,5 @@ // Methods for encoding integers as bytes in memory, as well as the reverse, -// decoding bytes as integers. All big-endian. - -// Given a pointer to some bytes in memory, pack them into a word. Assumes 0 < len <= 32. -// Pre stack: addr, len, retdest -// Post stack: packed_value -global mload_packing: - // stack: addr, len, retdest - MLOAD_32BYTES - // stack: packed_value, retdest - SWAP1 - // stack: retdest, packed_value - JUMP - -%macro mload_packing - %stack (addr, len) -> (addr, len, %%after) - %jump(mload_packing) -%%after: -%endmacro +// decoding bytes as integers. All big-endian unless specified. global mload_packing_u64_LE: // stack: addr, retdest diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index a0af8b07db..d20f2d0e6c 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -60,11 +60,11 @@ global sys_calldataload: %mload_context_metadata(@CTX_METADATA_CALLDATA_SIZE) %stack (calldata_size, kexit_info, i) -> (calldata_size, i, kexit_info, i) LT %jumpi(calldataload_large_offset) - %stack (kexit_info, i) -> (@SEGMENT_CALLDATA, i, 32, sys_calldataload_after_mload_packing, kexit_info) + %stack (kexit_info, i) -> (@SEGMENT_CALLDATA, i, 32, kexit_info) GET_CONTEXT %build_address - // stack: addr, 32, sys_calldataload_after_mload_packing, kexit_info - %jump(mload_packing) + // stack: addr, 32, kexit_info + MLOAD_32BYTES sys_calldataload_after_mload_packing: // stack: value, kexit_info SWAP1 diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 15fdd6a967..9acde9ce78 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -63,15 +63,16 @@ global encode_or_hash_node: // Load the hash and return (hash, 32). // stack: node_type, node_ptr, encode_value, cur_len, retdest POP - // Update the length of the `TrieData` segment: there are only two - // elements in a hash node. - SWAP2 %add_const(2) SWAP2 + // stack: node_ptr, encode_value, cur_len, retdest %increment // Skip over node type prefix // stack: hash_ptr, encode_value, cur_len, retdest %mload_trie_data // stack: hash, encode_value, cur_len, retdest - %stack (hash, encode_value, cur_len, retdest) -> (retdest, hash, 32, cur_len) + // Update the length of the `TrieData` segment: there are only two + // elements in a hash node. + SWAP2 %add_const(2) + %stack (cur_len, encode_value, hash, retdest) -> (retdest, hash, 32, cur_len) JUMP encode_or_hash_concrete_node: %stack (node_type, node_ptr, encode_value, cur_len) -> (node_type, node_ptr, encode_value, cur_len, maybe_hash_node) @@ -89,8 +90,8 @@ maybe_hash_node: pack_small_rlp: // stack: result_ptr, result_len, cur_len, retdest %stack (result_ptr, result_len, cur_len) - -> (result_ptr, result_len, after_packed_small_rlp, result_len, cur_len) - %jump(mload_packing) + -> (result_ptr, result_len, result_len, cur_len) + MLOAD_32BYTES after_packed_small_rlp: %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) JUMP @@ -182,22 +183,21 @@ encode_node_branch_prepend_prefix: // stack: node_payload_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest %add_const($i) %mload_trie_data // stack: child_i_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %stack - (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest) -> - (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) %jump(encode_or_hash_node) %%after_encode: - // stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + // stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) + SWAP1 + PUSH 32 DUP2 SUB + %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest DUP4 // rlp_pos PUSH 160 MSTORE_GENERAL SWAP3 %increment SWAP3 // rlp_pos += 1 %%unpack: - %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) + %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, rlp_start, node_payload_ptr, encode_value, cur_len, retdest) %jump(mstore_unpacking) @@ -231,7 +231,8 @@ encode_node_extension_after_encode_child: encode_node_extension_after_hex_prefix: // stack: rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - DUP4 %sub_const(32) %jumpi(encode_node_extension_unpack) + PUSH 32 DUP5 SUB + %jumpi(encode_node_extension_unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. DUP1 // rlp_pos PUSH 160 @@ -250,11 +251,6 @@ encode_node_extension_after_unpacking: global encode_node_leaf: // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - // `TrieData` holds the node type, the number of nibbles, the nibbles, - // the pointer to the value and the value. - // First, we add 4 for the node type, the number of nibbles, the nibbles - // and the pointer to the value. - SWAP3 %add_const(4) SWAP3 POP // stack: node_payload_ptr, encode_value, cur_len, retdest %alloc_rlp_block @@ -280,7 +276,12 @@ encode_node_leaf_after_hex_prefix: JUMP encode_node_leaf_after_encode_value: // stack: rlp_end_pos, cur_len, rlp_start, retdest - %stack(rlp_end_pos, cur_len, rlp_start, retdest) -> (rlp_end_pos, rlp_start, cur_len, retdest) + // `TrieData` holds the node type, the number of nibbles, the nibbles, + // the pointer to the value and the value. + // We add 4 for the node type, the number of nibbles, the nibbles + // and the pointer to the value. + SWAP1 %add_const(4) + %stack(cur_len, rlp_end_pos, rlp_start, retdest) -> (rlp_end_pos, rlp_start, cur_len, retdest) %prepend_rlp_list_prefix %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index cee87deb0a..532966a0ce 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -28,8 +28,8 @@ first_byte: // get the first nibble, if num_nibbles is odd, or zero otherwise SWAP2 // stack: packed_nibbles, num_nibbles, rlp_addr, terminated, retdest - DUP2 DUP1 - %mod_const(2) + DUP2 + PUSH 2 DUP2 MOD // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_addr, terminated, retdest SWAP1 SUB %mul_const(4) @@ -61,12 +61,12 @@ remaining_bytes: SWAP2 PUSH @U256_MAX // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_addr, ret_dest - SWAP1 SWAP2 DUP1 - %mod_const(2) + SWAP1 SWAP2 + PUSH 2 DUP2 MOD // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP1 SUB DUP1 // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest - %div_const(2) + %div2 // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP2 SWAP1 // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm index d21e917b28..71f78ec5bd 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm @@ -81,7 +81,7 @@ global scalar_to_rlp: DUP2 DUP2 SUB // len of the key // stack: len, addr', init_addr, retdest DUP3 - %mload_packing + MLOAD_32BYTES // stack: packed_key, addr', init_addr, retdest SWAP2 %pop2 // stack: key, retdest diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index db13d8890d..9829494c2f 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -158,9 +158,9 @@ DUP3 DUP6 MUL ISZERO %jumpi(%%return) // first_nib_2 = (key_2 >> (bits_2 - 4)) & 0xF - DUP6 DUP6 %sub_const(4) SHR %and_const(0xF) + DUP6 PUSH 4 DUP7 SUB SHR %and_const(0xF) // first_nib_1 = (key_1 >> (bits_1 - 4)) & 0xF - DUP5 DUP5 %sub_const(4) SHR %and_const(0xF) + DUP5 PUSH 4 DUP6 SUB SHR %and_const(0xF) // stack: first_nib_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 // if first_nib_1 != first_nib_2: break @@ -204,8 +204,8 @@ %pop2 %%return: // stack: len_common, key_common, bits_1, key_1, bits_2, key_2 - SWAP2 %div_const(4) SWAP2 // bits_1 -> len_1 (in nibbles) - SWAP4 %div_const(4) SWAP4 // bits_2 -> len_2 (in nibbles) + SWAP2 %shr_const(2) SWAP2 // bits_1 -> len_1 (in nibbles) + SWAP4 %shr_const(2) SWAP4 // bits_2 -> len_2 (in nibbles) // stack: len_common, key_common, len_1, key_1, len_2, key_2 %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index dcd2451582..721932df50 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -1,28 +1,20 @@ -// RLP-encode a fixed-length 160 bit (20 byte) string. Assumes string < 2^160. +// Convenience macro to RLP-encode a fixed-length 160 bit (20 byte) string +// and return where we left off. Assumes string < 2^160. // Pre stack: rlp_addr, string, retdest // Post stack: rlp_addr -global encode_rlp_160: - PUSH 20 - %jump(encode_rlp_fixed) - -// Convenience macro to call encode_rlp_160 and return where we left off. %macro encode_rlp_160 - %stack (rlp_addr, string) -> (rlp_addr, string, %%after) - %jump(encode_rlp_160) + %stack (rlp_addr, string) -> (20, rlp_addr, string, %%after) + %jump(encode_rlp_fixed) %%after: %endmacro -// RLP-encode a fixed-length 256 bit (32 byte) string. +// Convenience macro to RLP-encode a fixed-length 256 bit (32 byte) string +// and return where we left off. // Pre stack: rlp_addr, string, retdest // Post stack: rlp_addr -global encode_rlp_256: - PUSH 32 - %jump(encode_rlp_fixed) - -// Convenience macro to call encode_rlp_256 and return where we left off. %macro encode_rlp_256 - %stack (rlp_addr, string) -> (rlp_addr, string, %%after) - %jump(encode_rlp_256) + %stack (rlp_addr, string) -> (32, rlp_addr, string, %%after) + %jump(encode_rlp_fixed) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm b/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm index 2e76c20f8f..6958cff9f8 100644 --- a/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm +++ b/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm @@ -2,8 +2,8 @@ // its number of nibbles when required. Shouldn't be // called with rlp_index > 0x82 ff ff global increment_bounded_rlp: - // stack: rlp_index, num_nibbles, retdest - DUP1 + // stack: num_nibbles, rlp_index, retdest + DUP2 %eq_const(0x80) %jumpi(case_0x80) DUP1 @@ -14,19 +14,19 @@ global increment_bounded_rlp: %jumpi(case_0x81ff) // If rlp_index != 0x80 and rlp_index != 0x7f and rlp_index != 0x81ff // we only need to add one and keep the number of nibbles - %increment - %stack (rlp_index, num_nibbles, retdest) -> (retdest, rlp_index, num_nibbles) + DUP2 %increment DUP2 + %stack (next_num_nibbles, next_rlp_index, num_nibbles, rlp_index, retdest) -> (retdest, rlp_index, num_nibbles, next_rlp_index, next_num_nibbles) JUMP case_0x80: - %stack (rlp_index, num_nibbles, retdest) -> (retdest, 0x01, 2) + %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x80, 2, 0x01, 2) JUMP case_0x7f: - %stack (rlp_index, num_nibbles, retdest) -> (retdest, 0x8180, 4) + %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x7f, 2, 0x8180, 4) JUMP case_0x81ff: - %stack (rlp_index, num_nibbles, retdest) -> (retdest, 0x820100, 6) + %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x81ff, 4, 0x820100, 6) JUMP diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm index 8385ee59af..80c6d841fe 100644 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ b/evm/src/cpu/kernel/asm/util/keccak.asm @@ -55,12 +55,10 @@ sys_keccak256_empty: // Since KECCAK_GENERAL takes its input from memory, we will first write // a's bytes to @SEGMENT_KERNEL_GENERAL[0..32], then b's bytes to // @SEGMENT_KERNEL_GENERAL[32..64]. - %stack (a) -> (@SEGMENT_KERNEL_GENERAL, a, 32, %%after_mstore_a) - %jump(mstore_unpacking) -%%after_mstore_a: - %stack (addr, b) -> (addr, b, 32, %%after_mstore_b) - %jump(mstore_unpacking) -%%after_mstore_b: + %stack (a) -> (@SEGMENT_KERNEL_GENERAL, a) + MSTORE_32BYTES_32 + // stack: addr, b + MSTORE_32BYTES_32 %stack (addr) -> (addr, 64, 64) // reset the address offset SUB KECCAK_GENERAL diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs index 5517001f0d..0eb09cf7a6 100644 --- a/evm/src/cpu/kernel/tests/packing.rs +++ b/evm/src/cpu/kernel/tests/packing.rs @@ -5,60 +5,6 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::memory::segments::Segment; -#[test] -fn test_mload_packing_1_byte() -> Result<()> { - let mload_packing = KERNEL.global_labels["mload_packing"]; - - let retdest = 0xDEADBEEFu32.into(); - let len = 1.into(); - let addr = (Segment::RlpRaw as u64 + 2).into(); - let initial_stack = vec![retdest, len, addr]; - - let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); - interpreter.set_rlp_memory(vec![0, 0, 0xAB]); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![0xAB.into()]); - - Ok(()) -} - -#[test] -fn test_mload_packing_3_bytes() -> Result<()> { - let mload_packing = KERNEL.global_labels["mload_packing"]; - - let retdest = 0xDEADBEEFu32.into(); - let len = 3.into(); - let addr = (Segment::RlpRaw as u64 + 2).into(); - let initial_stack = vec![retdest, len, addr]; - - let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); - interpreter.set_rlp_memory(vec![0, 0, 0xAB, 0xCD, 0xEF]); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![0xABCDEF.into()]); - - Ok(()) -} - -#[test] -fn test_mload_packing_32_bytes() -> Result<()> { - let mload_packing = KERNEL.global_labels["mload_packing"]; - - let retdest = 0xDEADBEEFu32.into(); - let len = 32.into(); - let addr = (Segment::RlpRaw as u64).into(); - let initial_stack = vec![retdest, len, addr]; - - let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); - interpreter.set_rlp_memory(vec![0xFF; 32]); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![U256::MAX]); - - Ok(()) -} - #[test] fn test_mstore_unpacking() -> Result<()> { let mstore_unpacking = KERNEL.global_labels["mstore_unpacking"]; diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs index 505c99df88..d28a763fe8 100644 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -45,13 +45,13 @@ fn test_encode_rlp_scalar_medium() -> Result<()> { #[test] fn test_encode_rlp_160() -> Result<()> { - let encode_rlp_160 = KERNEL.global_labels["encode_rlp_160"]; + let encode_rlp_fixed = KERNEL.global_labels["encode_rlp_fixed"]; let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); let pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, string, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack); + let initial_stack = vec![retdest, string, pos, U256::from(20)]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(1 + 20)]; // pos' @@ -65,13 +65,13 @@ fn test_encode_rlp_160() -> Result<()> { #[test] fn test_encode_rlp_256() -> Result<()> { - let encode_rlp_256 = KERNEL.global_labels["encode_rlp_256"]; + let encode_rlp_fixed = KERNEL.global_labels["encode_rlp_fixed"]; let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); let pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, string, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack); + let initial_stack = vec![retdest, string, pos, U256::from(32)]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(1 + 32)]; // pos' From 10fc96608cab4b33a83069b72331a99bd40aabf7 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:56:35 -0500 Subject: [PATCH 113/175] Remove some more CPU cycles (#1472) * Speed-up some mload/mstore calls * Misc * Improve logs loop * Speed-up bloom * Speed-up access_lists loops * Fix * Speed up selfdestruct loop * Speed-up touched_addresses loop * Speed-up receipt loop * Skip rep loop * Fix * Misc * Review --- evm/src/cpu/kernel/asm/bloom_filter.asm | 32 ++++---- evm/src/cpu/kernel/asm/core/access_lists.asm | 74 +++++++++++-------- .../cpu/kernel/asm/core/create_addresses.asm | 18 ++--- .../cpu/kernel/asm/core/create_receipt.asm | 30 +++----- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 45 ++++++----- evm/src/cpu/kernel/asm/core/log.asm | 19 +++-- .../cpu/kernel/asm/core/selfdestruct_list.asm | 27 ++++--- evm/src/cpu/kernel/asm/core/terminate.asm | 6 +- .../cpu/kernel/asm/core/touched_addresses.asm | 34 ++++++--- evm/src/cpu/kernel/asm/journal/journal.asm | 7 +- evm/src/cpu/kernel/asm/memory/core.asm | 21 +++++- .../kernel/asm/mpt/delete/delete_branch.asm | 15 ++-- evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 1 - .../asm/mpt/insert/insert_extension.asm | 18 ++++- .../cpu/kernel/asm/mpt/insert/insert_leaf.asm | 19 ++++- evm/src/cpu/kernel/asm/rlp/encode.asm | 3 +- evm/src/cpu/kernel/asm/util/basic_macros.asm | 8 ++ evm/src/cpu/kernel/asm/util/keccak.asm | 5 +- 18 files changed, 239 insertions(+), 143 deletions(-) diff --git a/evm/src/cpu/kernel/asm/bloom_filter.asm b/evm/src/cpu/kernel/asm/bloom_filter.asm index 8fa84a80f5..35a4ebd763 100644 --- a/evm/src/cpu/kernel/asm/bloom_filter.asm +++ b/evm/src/cpu/kernel/asm/bloom_filter.asm @@ -55,20 +55,21 @@ logs_bloom_loop: // Add address to bloom filter. %increment // stack: addr_ptr, i, logs_len, retdest + PUSH @SEGMENT_LOGS_DATA %build_kernel_address DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) - // stack: addr, addr_ptr, i, logs_len, retdest + MLOAD_GENERAL + // stack: addr, full_addr_ptr, i, logs_len, retdest PUSH 0 - // stack: is_topic, addr, addr_ptr, i, logs_len, retdest + // stack: is_topic, addr, full_addr_ptr, i, logs_len, retdest %add_to_bloom - // stack: addr_ptr, i, logs_len, retdest + // stack: full_addr_ptr, i, logs_len, retdest %increment - // stack: num_topics_ptr, i, logs_len, retdest + // stack: full_num_topics_ptr, i, logs_len, retdest DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) - // stack: num_topics, num_topics_ptr, i, logs_len, retdest + MLOAD_GENERAL + // stack: num_topics, full_num_topics_ptr, i, logs_len, retdest SWAP1 %increment - // stack: topics_ptr, num_topics, i, logs_len, retdest + // stack: full_topics_ptr, num_topics, i, logs_len, retdest PUSH 0 logs_bloom_topic_loop: @@ -78,7 +79,7 @@ logs_bloom_topic_loop: %jumpi(logs_bloom_topic_end) DUP2 DUP2 ADD // stack: curr_topic_ptr, j, topics_ptr, num_topics, i, logs_len, retdest - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL // stack: topic, j, topics_ptr, num_topics, i, logs_len, retdest PUSH 1 // stack: is_topic, topic, j, topics_ptr, num_topics, i, logs_len, retdest @@ -142,19 +143,20 @@ logs_bloom_end: // Also updates the block bloom filter. %macro bloom_write_bit // stack: byte_index, byte_bit_index + PUSH @SEGMENT_TXN_BLOOM + %build_kernel_address PUSH 1 DUP3 - // stack: byte_bit_index, 1, byte_index, byte_bit_index + // stack: byte_bit_index, 1, byte_addr, byte_bit_index PUSH 7 SUB SHL // Updates the current txn bloom filter. SWAP2 POP DUP1 - %mload_kernel(@SEGMENT_TXN_BLOOM) - // stack: old_bloom_byte, byte_index, one_shifted_by_index + MLOAD_GENERAL + // stack: old_bloom_byte, byte_addr, one_shifted_by_index DUP3 OR - // stack: new_bloom_byte, byte_index, one_shifted_by_index - SWAP1 - %mstore_kernel(@SEGMENT_TXN_BLOOM) + // stack: new_bloom_byte, byte_addr, one_shifted_by_index + MSTORE_GENERAL // stack: one_shifted_by_index POP // stack: empty diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm index 5019b6840e..30afe27c41 100644 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -25,12 +25,15 @@ global insert_accessed_addresses: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // stack: len, addr, retdest - PUSH 0 + PUSH @SEGMENT_ACCESSED_ADDRESSES ADD + PUSH @SEGMENT_ACCESSED_ADDRESSES insert_accessed_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_ACCESSED_ADDRESSES %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) EQ %jumpi(insert_address) // stack: i, len, addr, retdest - DUP1 %mload_kernel(@SEGMENT_ACCESSED_ADDRESSES) + DUP1 + MLOAD_GENERAL // stack: loaded_addr, i, len, addr, retdest DUP4 // stack: addr, loaded_addr, i, len, addr, retdest @@ -42,9 +45,10 @@ insert_accessed_addresses_loop: insert_address: %stack (i, len, addr, retdest) -> (i, addr, len, retdest) DUP2 %journal_add_account_loaded // Add a journal entry for the loaded account. - %mstore_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Store new address at the end of the array. + %swap_mstore // Store new address at the end of the array. // stack: len, retdest %increment + %sub_const(@SEGMENT_ACCESSED_ADDRESSES) // unscale `len` %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Store new length. PUSH 1 // Return 1 to indicate that the address was inserted. SWAP1 JUMP @@ -59,12 +63,14 @@ global remove_accessed_addresses: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // stack: len, addr, retdest - PUSH 0 + PUSH @SEGMENT_ACCESSED_ADDRESSES ADD + PUSH @SEGMENT_ACCESSED_ADDRESSES remove_accessed_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_ACCESSED_ADDRESSES %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) EQ %jumpi(panic) // stack: i, len, addr, retdest - DUP1 %mload_kernel(@SEGMENT_ACCESSED_ADDRESSES) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, addr, retdest DUP4 // stack: addr, loaded_addr, i, len, addr, retdest @@ -74,12 +80,15 @@ remove_accessed_addresses_loop: %jump(remove_accessed_addresses_loop) remove_accessed_addresses_found: %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Decrement the access list length. + SUB // len -= 1 + PUSH @SEGMENT_ACCESSED_ADDRESSES + DUP2 SUB // unscale `len` + %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Decrement the access list length. // stack: len-1, i, retdest - %mload_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Load the last address in the access list. + MLOAD_GENERAL // Load the last address in the access list. // stack: last_addr, i, retdest - SWAP1 - %mstore_kernel(@SEGMENT_ACCESSED_ADDRESSES) // Store the last address at the position of the removed address. + MSTORE_GENERAL + // Store the last address at the position of the removed address. JUMP @@ -97,14 +106,16 @@ global insert_accessed_storage_keys: // stack: addr, key, value, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // stack: len, addr, key, value, retdest - PUSH 0 + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS ADD + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS insert_accessed_storage_keys_loop: + // `i` and `len` are both scaled by SEGMENT_ACCESSED_STORAGE_KEYS %stack (i, len, addr, key, value, retdest) -> (i, len, i, len, addr, key, value, retdest) EQ %jumpi(insert_storage_key) // stack: i, len, addr, key, value, retdest - DUP1 %increment %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP1 %increment MLOAD_GENERAL // stack: loaded_key, i, len, addr, key, value, retdest - DUP2 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP2 MLOAD_GENERAL // stack: loaded_addr, loaded_key, i, len, addr, key, value, retdest DUP5 EQ // stack: loaded_addr==addr, loaded_key, i, len, addr, key, value, retdest @@ -120,20 +131,18 @@ insert_storage_key: // stack: i, len, addr, key, value, retdest DUP4 DUP4 %journal_add_storage_loaded // Add a journal entry for the loaded storage key. // stack: i, len, addr, key, value, retdest - DUP1 - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS - %build_kernel_address - %stack(dst, i, len, addr, key, value) -> (addr, dst, dst, key, dst, value, i, value) + %stack(dst, len, addr, key, value) -> (addr, dst, dst, key, dst, value, dst, @SEGMENT_ACCESSED_STORAGE_KEYS, value) MSTORE_GENERAL // Store new address at the end of the array. - // stack: dst, key, dst, value, i, value, retdest + // stack: dst, key, dst, value, dst, segment, value, retdest %increment SWAP1 MSTORE_GENERAL // Store new key after that - // stack: dst, value, i, value, retdest + // stack: dst, value, dst, segment, value, retdest %add_const(2) SWAP1 MSTORE_GENERAL // Store new value after that - // stack: i, value, retdest + // stack: dst, segment, value, retdest %add_const(3) + SUB // unscale dst %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length. %stack (value, retdest) -> (retdest, 1, value) // Return 1 to indicate that the storage key was inserted. JUMP @@ -141,7 +150,7 @@ insert_storage_key: insert_accessed_storage_keys_found: // stack: i, len, addr, key, value, retdest %add_const(2) - %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + MLOAD_GENERAL %stack (original_value, len, addr, key, value, retdest) -> (retdest, 0, original_value) // Return 0 to indicate that the storage key was already present. JUMP @@ -151,14 +160,16 @@ global remove_accessed_storage_keys: // stack: addr, key, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // stack: len, addr, key, retdest - PUSH 0 + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS ADD + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS remove_accessed_storage_keys_loop: + // `i` and `len` are both scaled by SEGMENT_ACCESSED_STORAGE_KEYS %stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest) EQ %jumpi(panic) // stack: i, len, addr, key, retdest - DUP1 %increment %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP1 %increment MLOAD_GENERAL // stack: loaded_key, i, len, addr, key, retdest - DUP2 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP2 MLOAD_GENERAL // stack: loaded_addr, loaded_key, i, len, addr, key, retdest DUP5 EQ // stack: loaded_addr==addr, loaded_key, i, len, addr, key, retdest @@ -172,18 +183,21 @@ remove_accessed_storage_keys_loop: remove_accessed_storage_keys_found: %stack (i, len, addr, key, retdest) -> (len, 3, i, retdest) - SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Decrease the access list length. + SUB + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS + DUP2 SUB // unscale + %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Decrease the access list length. // stack: len-3, i, retdest - DUP1 %add_const(2) %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP1 %add_const(2) MLOAD_GENERAL // stack: last_value, len-3, i, retdest - DUP2 %add_const(1) %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP2 %add_const(1) MLOAD_GENERAL // stack: last_key, last_value, len-3, i, retdest - DUP3 %mload_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP3 MLOAD_GENERAL // stack: last_addr, last_key, last_value, len-3, i, retdest - DUP5 %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Move the last tuple to the position of the removed tuple. + DUP5 %swap_mstore // Move the last tuple to the position of the removed tuple. // stack: last_key, last_value, len-3, i, retdest - DUP4 %add_const(1) %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP4 %add_const(1) %swap_mstore // stack: last_value, len-3, i, retdest - DUP3 %add_const(2) %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) + DUP3 %add_const(2) %swap_mstore // stack: len-3, i, retdest %pop2 JUMP diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 77b6f6044e..8c2de08bd2 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -37,17 +37,17 @@ global get_create_address: // Post stack: address global get_create2_address: // stack: sender, code_hash, salt, retdest - PUSH 0xff PUSH 0 %mstore_kernel_general - %stack (sender, code_hash, salt, retdest) -> (@SEGMENT_KERNEL_GENERAL, 1, sender, salt, code_hash, retdest) - ADD + PUSH @SEGMENT_KERNEL_GENERAL + DUP1 + PUSH 0xff + MSTORE_GENERAL + // stack: addr, sender, code_hash, salt, retdest + %increment + %stack (addr, sender, code_hash, salt, retdest) -> (addr, sender, salt, code_hash, retdest) MSTORE_32BYTES_20 - POP - %stack (salt, code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 21, salt, code_hash, retdest) - ADD + // stack: addr, salt, code_hash, retdest MSTORE_32BYTES_32 - POP - %stack (code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 53, code_hash, retdest) - ADD + // stack: addr, code_hash, retdest MSTORE_32BYTES_32 POP %stack (retdest) -> (@SEGMENT_KERNEL_GENERAL, 85, retdest) // offset == context == 0 diff --git a/evm/src/cpu/kernel/asm/core/create_receipt.asm b/evm/src/cpu/kernel/asm/core/create_receipt.asm index 9f7cb9f89d..60e9264739 100644 --- a/evm/src/cpu/kernel/asm/core/create_receipt.asm +++ b/evm/src/cpu/kernel/asm/core/create_receipt.asm @@ -116,22 +116,23 @@ process_receipt_logs_loop: %mload_kernel(@SEGMENT_LOGS) // stack: log_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write payload_len. + PUSH @SEGMENT_LOGS_DATA %build_kernel_address DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL %append_to_trie_data // stack: log_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write address. %increment // stack: addr_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL %append_to_trie_data // stack: addr_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest //Write num_topics. %increment // stack: num_topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL // stack: num_topics, num_topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 %append_to_trie_data @@ -151,7 +152,7 @@ process_receipt_topics_loop: DUP3 DUP2 ADD // stack: cur_topic_ptr, j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL %append_to_trie_data // stack: j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest %increment @@ -164,7 +165,7 @@ process_receipt_topics_end: // stack: data_len_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write data_len DUP1 - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL // stack: data_len, data_len_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 %append_to_trie_data @@ -184,7 +185,7 @@ process_receipt_data_loop: DUP3 DUP2 ADD // stack: cur_data_ptr, j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %mload_kernel(@SEGMENT_LOGS_DATA) + MLOAD_GENERAL %append_to_trie_data // stack: j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest %increment @@ -205,19 +206,10 @@ process_receipt_after_write: DUP5 %mpt_insert_receipt_trie // stack: new_cum_gas, txn_nb, num_nibbles, retdest - // Now, we set the Bloom filter back to 0. We proceed by chunks of 32 bytes. - PUSH @SEGMENT_TXN_BLOOM // ctx == offset == 0 - %rep 8 - // stack: addr, new_cum_gas, txn_nb, num_nibbles, retdest - PUSH 0 // we will fill the memory segment with zeroes - DUP2 - // stack: addr, 0, addr, new_cum_gas, txn_nb, num_nibbles, retdest - MSTORE_32BYTES_32 - // stack: new_addr, addr, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP1 POP - %endrep - POP - // stack: new_cum_gas, txn_nb, num_nibbles, retdest + + // We don't need to reset the bloom filter segment as we only process a single transaction. + // TODO: Revert in case we add back support for multi-txn proofs. + %stack (new_cum_gas, txn_nb, num_nibbles, retdest) -> (retdest, new_cum_gas) JUMP diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index efcd66420f..6ed4df814f 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -3,52 +3,57 @@ // Pre stack: init_pos, ctx, final_pos, retdest // Post stack: (empty) global verify_path_and_write_jumpdest_table: + SWAP2 + DUP2 + ADD // final_addr + // stack: final_addr, ctx, i, retdest + SWAP2 + ADD // init_addr loop: - // stack: i, ctx, final_pos, retdest - DUP3 DUP2 EQ // i == final_pos + // stack: i, final_pos, retdest + DUP2 DUP2 EQ // i == final_pos %jumpi(proof_ok) - DUP3 DUP2 GT // i > final_pos + DUP2 DUP2 GT // i > final_pos %jumpi(proof_not_ok) - // stack: i, ctx, final_pos, retdest - %stack (i, ctx) -> (ctx, i, i, ctx) - ADD // combine context and offset to make an address (SEGMENT_CODE == 0) - MLOAD_GENERAL - // stack: opcode, i, ctx, final_pos, retdest + // stack: i, final_pos, retdest + DUP1 + MLOAD_GENERAL // SEGMENT_CODE == 0 + // stack: opcode, i, final_pos, retdest DUP1 // Slightly more efficient than `%eq_const(0x5b) ISZERO` PUSH 0x5b SUB - // stack: opcode != JUMPDEST, opcode, i, ctx, final_pos, retdest + // stack: opcode != JUMPDEST, opcode, i, final_pos, retdest %jumpi(continue) - // stack: JUMPDEST, i, ctx, code_len, retdest - %stack (JUMPDEST, i, ctx) -> (ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) - %build_address + // stack: JUMPDEST, i, code_len, retdest + %stack (JUMPDEST, i) -> (@SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i) + ADD // address to write jumpdest bit, i already contains the context PUSH 1 - // stack: 1, addr, JUMPDEST, i, ctx + // stack: 1, addr, JUMPDEST, i MSTORE_GENERAL continue: - // stack: opcode, i, ctx, final_pos, retdest + // stack: opcode, i, final_pos, retdest %add_const(code_bytes_to_skip) %mload_kernel_code - // stack: bytes_to_skip, i, ctx, final_pos, retdest + // stack: bytes_to_skip, i, final_pos, retdest ADD - // stack: i, ctx, final_pos, retdest + // stack: i, final_pos, retdest %jump(loop) proof_ok: - // stack: i, ctx, final_pos, retdest + // stack: i, final_pos, retdest // We already know final_pos is a jumpdest - %stack (i, ctx, final_pos) -> (ctx, @SEGMENT_JUMPDEST_BITS, final_pos) - %build_address + %stack (i, final_pos) -> (@SEGMENT_JUMPDEST_BITS, final_pos) + ADD // final_pos already contains the context PUSH 1 MSTORE_GENERAL JUMP proof_not_ok: - %pop3 + %pop2 JUMP // Determines how many bytes away is the next opcode, based on the opcode we read. diff --git a/evm/src/cpu/kernel/asm/core/log.asm b/evm/src/cpu/kernel/asm/core/log.asm index 0689d49211..f23d5e174c 100644 --- a/evm/src/cpu/kernel/asm/core/log.asm +++ b/evm/src/cpu/kernel/asm/core/log.asm @@ -206,22 +206,27 @@ log_after_topics: // stack: next_log_ptr, data_ptr, data_offset, retdest SWAP1 // stack: data_ptr, next_log_ptr, data_offset, retdest + SWAP2 + PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT %build_address + SWAP2 + // stack: data_ptr, next_log_ptr, data_addr, retdest + store_log_data_loop: - // stack: cur_data_ptr, next_log_ptr, cur_data_offset, retdest + // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest DUP2 DUP2 EQ - // stack: cur_data_ptr == next_log_ptr, cur_data_ptr, next_log_ptr, cur_data_offset, retdest + // stack: cur_data_ptr == next_log_ptr, cur_data_ptr, next_log_ptr, cur_data_addr, retdest %jumpi(store_log_data_loop_end) - // stack: cur_data_ptr, next_log_ptr, cur_data_offset, retdest + // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest DUP3 - %mload_current(@SEGMENT_MAIN_MEMORY) - // stack: cur_data, cur_data_ptr, next_log_ptr, cur_data_offset, retdest + MLOAD_GENERAL + // stack: cur_data, cur_data_ptr, next_log_ptr, cur_data_addr, retdest // Store current data byte. DUP2 %mstore_kernel(@SEGMENT_LOGS_DATA) - // stack: cur_data_ptr, next_log_ptr, cur_data_offset, retdest + // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest SWAP2 %increment SWAP2 - // stack: cur_data_ptr, next_log_ptr, next_data_offset, retdest + // stack: cur_data_ptr, next_log_ptr, next_data_addr, retdest %increment %jump(store_log_data_loop) diff --git a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm b/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm index 5084541aa2..258f794054 100644 --- a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm +++ b/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm @@ -5,8 +5,9 @@ %macro insert_selfdestruct_list // stack: addr %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) - %stack (len, addr) -> (len, addr, len) - %mstore_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Store new address at the end of the array. + DUP1 PUSH @SEGMENT_SELFDESTRUCT_LIST %build_kernel_address + %stack (write_addr, len, addr) -> (addr, write_addr, len) + MSTORE_GENERAL // Store new address at the end of the array. // stack: len %increment %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Store new length. @@ -18,12 +19,14 @@ global remove_selfdestruct_list: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // stack: len, addr, retdest - PUSH 0 + PUSH @SEGMENT_SELFDESTRUCT_LIST ADD + PUSH @SEGMENT_SELFDESTRUCT_LIST remove_selfdestruct_list_loop: + // `i` and `len` are both scaled by SEGMENT_SELFDESTRUCT_LIST %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) EQ %jumpi(panic) // stack: i, len, addr, retdest - DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, addr, retdest DUP4 // stack: addr, loaded_addr, i, len, addr, retdest @@ -33,24 +36,28 @@ remove_selfdestruct_list_loop: %jump(remove_selfdestruct_list_loop) remove_selfdestruct_list_found: %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Decrement the list length. + SUB + PUSH @SEGMENT_SELFDESTRUCT_LIST + DUP2 SUB // unscale + %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Decrement the list length. // stack: len-1, i, retdest - %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Load the last address in the list. + MLOAD_GENERAL // Load the last address in the list. // stack: last_addr, i, retdest - SWAP1 - %mstore_kernel(@SEGMENT_SELFDESTRUCT_LIST) // Store the last address at the position of the removed address. + MSTORE_GENERAL // Store the last address at the position of the removed address. JUMP global delete_all_selfdestructed_addresses: // stack: retdest %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // stack: len, retdest - PUSH 0 + PUSH @SEGMENT_SELFDESTRUCT_LIST ADD + PUSH @SEGMENT_SELFDESTRUCT_LIST delete_all_selfdestructed_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_SELFDESTRUCT_LIST // stack: i, len, retdest DUP2 DUP2 EQ %jumpi(delete_all_selfdestructed_addresses_done) // stack: i, len, retdest - DUP1 %mload_kernel(@SEGMENT_SELFDESTRUCT_LIST) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, retdest DUP1 %is_non_existent ISZERO %jumpi(bingo) // stack: loaded_addr, i, len, retdest diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 9b52591933..8572f34f28 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -203,9 +203,11 @@ global terminate_common: // Similarly, we write the parent PC to SEGMENT_KERNEL_GENERAL[2] so that // we can later read it after switching to the parent context. - %mload_context_metadata(@CTX_METADATA_PARENT_PC) PUSH 2 - %mstore_kernel(@SEGMENT_KERNEL_GENERAL) + PUSH @SEGMENT_KERNEL_GENERAL + %build_kernel_address + %mload_context_metadata(@CTX_METADATA_PARENT_PC) + MSTORE_GENERAL // stack: (empty) // Go back to the parent context. diff --git a/evm/src/cpu/kernel/asm/core/touched_addresses.asm b/evm/src/cpu/kernel/asm/core/touched_addresses.asm index f2c0394a66..044b103ff4 100644 --- a/evm/src/cpu/kernel/asm/core/touched_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/touched_addresses.asm @@ -15,12 +15,14 @@ global insert_touched_addresses: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // stack: len, addr, retdest - PUSH 0 + PUSH @SEGMENT_TOUCHED_ADDRESSES ADD + PUSH @SEGMENT_TOUCHED_ADDRESSES insert_touched_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) EQ %jumpi(insert_address) // stack: i, len, addr, retdest - DUP1 %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, addr, retdest DUP4 // stack: addr, loaded_addr, i, len, addr, retdest @@ -30,10 +32,11 @@ insert_touched_addresses_loop: %jump(insert_touched_addresses_loop) insert_address: - %stack (i, len, addr, retdest) -> (i, addr, len, retdest) + %stack (i, len, addr, retdest) -> (i, addr, len, @SEGMENT_TOUCHED_ADDRESSES, retdest) DUP2 %journal_add_account_touched // Add a journal entry for the touched account. - %mstore_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Store new address at the end of the array. - // stack: len, retdest + %swap_mstore // Store new address at the end of the array. + // stack: len, segment, retdest + SUB // unscale %increment %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Store new length. JUMP @@ -49,12 +52,14 @@ global remove_touched_addresses: // stack: addr, retdest %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // stack: len, addr, retdest - PUSH 0 + PUSH @SEGMENT_TOUCHED_ADDRESSES ADD + PUSH @SEGMENT_TOUCHED_ADDRESSES remove_touched_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) EQ %jumpi(panic) // stack: i, len, addr, retdest - DUP1 %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, addr, retdest DUP4 // stack: addr, loaded_addr, i, len, addr, retdest @@ -64,12 +69,15 @@ remove_touched_addresses_loop: %jump(remove_touched_addresses_loop) remove_touched_addresses_found: %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB DUP1 %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Decrement the list length. + SUB + PUSH @SEGMENT_TOUCHED_ADDRESSES DUP2 + SUB // unscale + %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Decrement the list length. // stack: len-1, i, retdest - %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Load the last address in the list. + MLOAD_GENERAL // Load the last address in the list. // stack: last_addr, i, retdest SWAP1 - %mstore_kernel(@SEGMENT_TOUCHED_ADDRESSES) // Store the last address at the position of the removed address. + MLOAD_GENERAL // Store the last address at the position of the removed address. JUMP @@ -77,12 +85,14 @@ global delete_all_touched_addresses: // stack: retdest %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // stack: len, retdest - PUSH 0 + PUSH @SEGMENT_TOUCHED_ADDRESSES ADD + PUSH @SEGMENT_TOUCHED_ADDRESSES delete_all_touched_addresses_loop: + // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES // stack: i, len, retdest DUP2 DUP2 EQ %jumpi(delete_all_touched_addresses_done) // stack: i, len, retdest - DUP1 %mload_kernel(@SEGMENT_TOUCHED_ADDRESSES) + DUP1 MLOAD_GENERAL // stack: loaded_addr, i, len, retdest DUP1 %is_empty %jumpi(bingo) // stack: loaded_addr, i, len, retdest diff --git a/evm/src/cpu/kernel/asm/journal/journal.asm b/evm/src/cpu/kernel/asm/journal/journal.asm index 2715bc6b98..9ba4350878 100644 --- a/evm/src/cpu/kernel/asm/journal/journal.asm +++ b/evm/src/cpu/kernel/asm/journal/journal.asm @@ -182,9 +182,12 @@ // stack: (empty) %current_checkpoint // stack: current_checkpoint + DUP1 + PUSH @SEGMENT_JOURNAL_CHECKPOINTS + %build_kernel_address %journal_size - // stack: journal_size, current_checkpoint - DUP2 %mstore_kernel(@SEGMENT_JOURNAL_CHECKPOINTS) + // stack: journal_size, addr, current_checkpoint + MSTORE_GENERAL // stack: current_checkpoint %mload_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) // stack: i, current_checkpoint diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index 74b49f2780..da8a05fb18 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -225,6 +225,15 @@ // stack: value %endmacro +// Load a single value from the given segment of kernel (context 0) memory. +%macro mload_kernel_no_offset(segment) + // stack: empty + PUSH $segment + // stack: addr + MLOAD_GENERAL + // stack: value +%endmacro + // Store a single value from the given segment of kernel (context 0) memory. %macro mstore_kernel(segment) // stack: offset, value @@ -237,6 +246,16 @@ // stack: (empty) %endmacro +// Store a single value from the given segment of kernel (context 0) memory. +%macro mstore_kernel_no_offset(segment) + // stack: value + PUSH $segment + // stack: addr, value + SWAP1 + MSTORE_GENERAL + // stack: (empty) +%endmacro + // Store a single value from the given segment of kernel (context 0) memory. %macro mstore_kernel(segment, offset) // stack: value @@ -393,7 +412,7 @@ %macro mstore_kernel_code // stack: offset, value // ctx == SEGMENT_CODE == 0 - MLOAD_GENERAL + MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm b/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm index 775e4e11ed..64187ac83a 100644 --- a/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm +++ b/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm @@ -43,7 +43,10 @@ update_branch: // If it's one, transform the branch node into an leaf/extension node and return it. maybe_normalize_branch: // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - PUSH 0 %mstore_kernel_general(0) PUSH 0 %mstore_kernel_general(1) + PUSH 0 + PUSH @SEGMENT_KERNEL_GENERAL + MSTORE_32BYTES_2 + POP // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest PUSH 0 // Loop from i=0..16 excluding `first_nibble` and store the number of non-empty children in @@ -61,16 +64,18 @@ loop_eq_first_nibble: %increment %jump(loop) loop_non_empty: // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mload_kernel_general(0) %increment %mstore_kernel_general(0) - DUP1 %mstore_kernel_general(1) + %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) %increment %mstore_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) + PUSH 1 PUSH @SEGMENT_KERNEL_GENERAL %build_kernel_address + DUP2 + MSTORE_GENERAL %increment %jump(loop) loop_end: // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest POP // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest // If there's more than one non-empty child, simply update the branch node. - %mload_kernel_general(0) %gt_const(1) %jumpi(update_branch) - %mload_kernel_general(0) ISZERO %jumpi(panic) // This should never happen. + %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) %gt_const(1) %jumpi(update_branch) + %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) ISZERO %jumpi(panic) // This should never happen. // Otherwise, transform the branch node into a leaf/extension node. // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest %mload_kernel_general(1) diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index 532966a0ce..0ca2458f0c 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -111,7 +111,6 @@ rlp_header_large: DUP2 // rlp_addr PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8 MSTORE_GENERAL - // stack: rlp_addr, value, hp_len, i, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest DUP2 %increment diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm index 3ead805b1d..21a4b7558b 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm @@ -74,9 +74,21 @@ node_key_continues: // Pseudocode: new_node = [MPT_TYPE_BRANCH] + [0] * 17 %get_trie_data_size // pointer to the branch node we're about to create PUSH @MPT_NODE_BRANCH %append_to_trie_data - %rep 17 - PUSH 0 %append_to_trie_data - %endrep + + PUSH 0 + // Increment trie data size by 17 + %get_trie_data_size + // stack: trie_data_size, 0 + DUP1 + %add_const(17) + %set_trie_data_size + + // stack: trie_data_size, 0 + + // Write 17 consecutive 0s at once + PUSH @SEGMENT_TRIE_DATA %build_kernel_address + MSTORE_32BYTES_17 + POP process_node_child: // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm index 72a014ceec..806fc0ddbd 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm @@ -69,9 +69,22 @@ global mpt_insert_leaf: // For now, we allocate the branch node, initially with no children or value. %get_trie_data_size // pointer to the branch node we're about to create PUSH @MPT_NODE_BRANCH %append_to_trie_data - %rep 17 - PUSH 0 %append_to_trie_data - %endrep + + PUSH 0 + // Increment trie data size by 17 + %get_trie_data_size + // stack: trie_data_size, 0 + DUP1 + %add_const(17) + %set_trie_data_size + + // stack: trie_data_size, 0 + + // Write 17 consecutive 0s at once + PUSH @SEGMENT_TRIE_DATA %build_kernel_address + MSTORE_32BYTES_17 + POP + // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest // Now, we branch based on whether each key continues beyond the common diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 721932df50..9f6813ab18 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -195,7 +195,8 @@ prepend_rlp_list_prefix_big: PUSH 1 DUP6 SUB // start_rlp_addr - 1 SUB // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP2 %add_const(0xf7) DUP2 %swap_mstore // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len + DUP1 + DUP3 %add_const(0xf7) MSTORE_GENERAL // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %increment // start_len_rlp_addr = prefix_start_rlp_addr + 1 %stack (start_len_rlp_addr, prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest) diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index 76def0c7cc..8753e1cafb 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -446,6 +446,14 @@ // stack: addr (ctx == 0) %endmacro +%macro build_address_with_ctx(seg, off) + // stack: ctx + PUSH $seg + PUSH $off + %build_address + // stack: addr +%endmacro + %macro build_address_with_ctx_no_offset(seg) // stack: ctx PUSH $seg diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm index 80c6d841fe..dceb7b195b 100644 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ b/evm/src/cpu/kernel/asm/util/keccak.asm @@ -38,11 +38,10 @@ sys_keccak256_empty: %macro keccak256_word(num_bytes) // Since KECCAK_GENERAL takes its input from memory, we will first write // input_word's bytes to @SEGMENT_KERNEL_GENERAL[0..$num_bytes]. - %stack (word) -> (@SEGMENT_KERNEL_GENERAL, word, $num_bytes, %%after_mstore) + %stack (word) -> (@SEGMENT_KERNEL_GENERAL, word, $num_bytes, %%after_mstore, $num_bytes, $num_bytes) %jump(mstore_unpacking) %%after_mstore: - // stack: addr - %stack(addr) -> (addr, $num_bytes, $num_bytes) + // stack: addr, $num_bytes, $num_bytes SUB KECCAK_GENERAL %endmacro From 39a2d62d6d025631380da88aa78c2f8b929852a2 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 17 Jan 2024 08:28:53 -0500 Subject: [PATCH 114/175] Fix touched_addresses removal (#1473) --- evm/src/cpu/kernel/asm/core/touched_addresses.asm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/touched_addresses.asm b/evm/src/cpu/kernel/asm/core/touched_addresses.asm index 044b103ff4..d9c70f47ac 100644 --- a/evm/src/cpu/kernel/asm/core/touched_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/touched_addresses.asm @@ -76,8 +76,7 @@ remove_touched_addresses_found: // stack: len-1, i, retdest MLOAD_GENERAL // Load the last address in the list. // stack: last_addr, i, retdest - SWAP1 - MLOAD_GENERAL // Store the last address at the position of the removed address. + MSTORE_GENERAL // Store the last address at the position of the removed address. JUMP From 265d46a96ecfec49a32973f66f8aa811586c5d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:46:11 +0100 Subject: [PATCH 115/175] chore: update ci workflow (#1475) * chore: bump `actions/checkout` and `actions/cache` * chore: use `dtolnay/rust-toolchain` and `Swatinem/rust-cache` instead of outdated github actions * chore: use `cargo test` * chore: remove `actions-rs/cargo` * fix: typo * chore: enable to cancel in-progress jobs * chore: add job timeouts --- .../continuous-integration-workflow.yml | 123 +++++------------- 1 file changed, 36 insertions(+), 87 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 7c0e479458..7c18a405a9 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -10,39 +10,33 @@ on: branches: - "**" +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + jobs: test: name: Test Suite runs-on: ubuntu-latest + timeout-minutes: 30 if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install nightly toolchain - id: rustc-toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true + uses: dtolnay/rust-toolchain@nightly - - name: rust-cache - uses: actions/cache@v3 + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: rustc-test-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} + cache-on-failure: true - name: Check in plonky2 subdirectory - uses: actions-rs/cargo@v1 - with: - command: check - args: --manifest-path plonky2/Cargo.toml + run: cargo check --manifest-path plonky2/Cargo.toml env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -50,10 +44,7 @@ jobs: RUST_BACKTRACE: 1 - name: Check in starky subdirectory - uses: actions-rs/cargo@v1 - with: - command: check - args: --manifest-path starky/Cargo.toml + run: cargo check --manifest-path starky/Cargo.toml env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -61,10 +52,7 @@ jobs: RUST_BACKTRACE: 1 - name: Check in evm subdirectory - uses: actions-rs/cargo@v1 - with: - command: check - args: --manifest-path evm/Cargo.toml + run: cargo check --manifest-path evm/Cargo.toml env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -72,10 +60,7 @@ jobs: RUST_BACKTRACE: 1 - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test - args: --workspace + run: cargo test --workspace env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -85,37 +70,24 @@ jobs: wasm32: name: wasm32 compatibility runs-on: ubuntu-latest + timeout-minutes: 30 if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Install nightly wasm32 toolchain - id: rustc-toolchain - uses: actions-rs/toolchain@v1 + - name: Install nightly toolchain + uses: dtolnay/rust-toolchain@nightly with: - profile: minimal - toolchain: nightly - target: wasm32-unknown-unknown - default: true - override: true - - - name: rust-cache - uses: actions/cache@v3 + targets: wasm32-unknown-unknown + + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: rustc-wasm32-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} + cache-on-failure: true - name: Check in plonky2 subdirectory - uses: actions-rs/cargo@v1 - with: - command: check - args: --manifest-path plonky2/Cargo.toml --target wasm32-unknown-unknown --no-default-features + run: cargo check --manifest-path plonky2/Cargo.toml --target wasm32-unknown-unknown --no-default-features env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -123,10 +95,7 @@ jobs: RUST_BACKTRACE: 1 - name: Check in starky subdirectory - uses: actions-rs/cargo@v1 - with: - command: check - args: --manifest-path starky/Cargo.toml --target wasm32-unknown-unknown --no-default-features + run: cargo check --manifest-path starky/Cargo.toml --target wasm32-unknown-unknown --no-default-features env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 RUST_LOG: 1 @@ -136,44 +105,24 @@ jobs: lints: name: Formatting and Clippy runs-on: ubuntu-latest + timeout-minutes: 10 if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install nightly toolchain - id: rustc-toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@nightly with: - profile: minimal - toolchain: nightly - override: true components: rustfmt, clippy - - name: rust-cache - uses: actions/cache@v3 + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: rustc-lints-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} + cache-on-failure: true - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - env: - CARGO_INCREMENTAL: 1 + run: cargo fmt --all --check - name: Run cargo clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-features --all-targets -- -D warnings -A incomplete-features - env: - # Seems necessary until https://github.com/rust-lang/rust/pull/115819 is merged. - CARGO_INCREMENTAL: 0 + run: cargo clippy --all-features --all-targets -- -D warnings -A incomplete-features From b8a16b39c7f84491dfae9b9dc3b7ded98bb2839b Mon Sep 17 00:00:00 2001 From: Thabokani <149070269+Thabokani@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:53:06 +0800 Subject: [PATCH 116/175] Fix typos (#1479) --- evm/spec/mpts.tex | 2 +- plonky2/src/gates/gate.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/spec/mpts.tex b/evm/spec/mpts.tex index 91da59ce5c..3f6733a535 100644 --- a/evm/spec/mpts.tex +++ b/evm/spec/mpts.tex @@ -59,7 +59,7 @@ \subsection{Prover input format} \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest. \end{enumerate} Nodes are thus given in depth-first order, enabling natural recursive methods for encoding and decoding this format. -The payload of state and receipt tries is given in the natural sequential way. The transaction an receipt payloads contain variable size data, thus the input is slightly different. The prover input for for the transactions is the transaction RLP encoding preceeded by its length. For the receipts is in the natural sequential way, except that topics and data are preceeded by their lengths, respectively. +The payload of state and receipt tries is given in the natural sequential way. The transaction an receipt payloads contain variable size data, thus the input is slightly different. The prover input for for the transactions is the transaction RLP encoding preceded by its length. For the receipts is in the natural sequential way, except that topics and data are preceded by their lengths, respectively. \subsection{Encoding and Hashing} diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 6dbed3fb02..cc8f7513c4 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -219,7 +219,7 @@ pub trait Gate, const D: usize>: 'static + Send + S /// The number of wires used by this gate. /// /// While vanilla Plonk can only evaluate one addition/multiplication at a time, a wider - /// configuration may be able to accomodate several identical gates at once. This is + /// configuration may be able to accommodate several identical gates at once. This is /// particularly helpful for tiny custom gates that are being used extensively in circuits. /// /// For instance, the [crate::gates::multiplication_extension::MulExtensionGate] takes `3*D` From 8e1969db2fb671ccdc7452696d288c57ee54ff68 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:30:51 +0100 Subject: [PATCH 117/175] Fix interpreter jumps (#1471) * Fix interpreter jumps * Apply comments --- evm/src/cpu/kernel/interpreter.rs | 70 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 0a60ccff68..48eedb8417 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -57,7 +57,6 @@ impl MemoryState { } pub(crate) struct Interpreter<'a> { - jumpdests: Vec, pub(crate) generation_state: GenerationState, prover_inputs_map: &'a HashMap, pub(crate) halt_offsets: Vec, @@ -173,7 +172,6 @@ impl<'a> Interpreter<'a> { prover_inputs: &'a HashMap, ) -> Self { let mut result = Self { - jumpdests: find_jumpdests(code), generation_state: GenerationState::new(GenerationInputs::default(), code) .expect("Default inputs are known-good"), prover_inputs_map: prover_inputs, @@ -1020,8 +1018,6 @@ impl<'a> Interpreter<'a> { let addr = self.pop()?; let (context, segment, offset) = unpack_address!(addr); - // Not strictly needed but here to avoid surprises with MSIZE. - assert_ne!(segment, Segment::MainMemory, "Call KECCAK256 instead."); let size = self.pop()?.as_usize(); let bytes = (offset..offset + size) .map(|i| { @@ -1091,13 +1087,37 @@ impl<'a> Interpreter<'a> { self.push(syscall_info) } + fn set_jumpdest_bit(&mut self, x: U256) -> U256 { + if self.generation_state.memory.contexts[self.context()].segments + [Segment::JumpdestBits.unscale()] + .content + .len() + > x.low_u32() as usize + { + self.generation_state.memory.get(MemoryAddress { + context: self.context(), + segment: Segment::JumpdestBits.unscale(), + virt: x.low_u32() as usize, + }) + } else { + 0.into() + } + } fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; + + let jumpdest_bit = self.set_jumpdest_bit(x); + // Check that the destination is valid. let x: u32 = x .try_into() .map_err(|_| ProgramError::InvalidJumpDestination)?; - self.jump_to(x as usize) + + if !self.is_kernel() && jumpdest_bit != U256::one() { + return Err(ProgramError::InvalidJumpDestination); + } + + self.jump_to(x as usize, false) } fn run_jumpi(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1107,9 +1127,13 @@ impl<'a> Interpreter<'a> { let x: u32 = x .try_into() .map_err(|_| ProgramError::InvalidJumpiDestination)?; - self.jump_to(x as usize)?; + self.jump_to(x as usize, true)?; } + let jumpdest_bit = self.set_jumpdest_bit(x); + if !b.is_zero() && !self.is_kernel() && jumpdest_bit != U256::one() { + return Err(ProgramError::InvalidJumpiDestination); + } Ok(()) } @@ -1129,14 +1153,20 @@ impl<'a> Interpreter<'a> { Ok(()) } - fn jump_to(&mut self, offset: usize) -> anyhow::Result<(), ProgramError> { - // The JUMPDEST rule is not enforced in kernel mode. - if !self.is_kernel() && self.jumpdests.binary_search(&offset).is_err() { - return Err(ProgramError::InvalidJumpDestination); - } - + fn jump_to(&mut self, offset: usize, is_jumpi: bool) -> anyhow::Result<(), ProgramError> { self.generation_state.registers.program_counter = offset; + if offset == KERNEL.global_labels["observe_new_address"] { + let tip_u256 = stack_peek(&self.generation_state, 0)?; + let tip_h256 = H256::from_uint(&tip_u256); + let tip_h160 = H160::from(tip_h256); + self.generation_state.observe_address(tip_h160); + } else if offset == KERNEL.global_labels["observe_new_contract"] { + let tip_u256 = stack_peek(&self.generation_state, 0)?; + let tip_h256 = H256::from_uint(&tip_u256); + self.generation_state.observe_contract(tip_h256)?; + } + if self.halt_offsets.contains(&offset) { self.running = false; } @@ -1431,22 +1461,6 @@ const SIGN_MASK: U256 = U256([ 0x7fffffffffffffff, ]); -/// Return the (ordered) JUMPDEST offsets in the code. -fn find_jumpdests(code: &[u8]) -> Vec { - let mut offset = 0; - let mut res = Vec::new(); - while offset < code.len() { - let opcode = code[offset]; - match opcode { - 0x5b => res.push(offset), - x if (0x60..0x80).contains(&x) => offset += x as usize - 0x5f, // PUSH instruction, disregard data. - _ => (), - } - offset += 1; - } - res -} - fn get_mnemonic(opcode: u8) -> &'static str { match opcode { 0x00 => "STOP", From 319fc6a2f206b6a67508dd60b4424ce1a54bcb99 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 22 Jan 2024 08:02:38 -0500 Subject: [PATCH 118/175] Improve SHA2 precompile (#1480) * Improve SHA2 precompile * Review * Add removed global label --- .../cpu/kernel/asm/hash/sha2/compression.asm | 9 +-- evm/src/cpu/kernel/asm/hash/sha2/main.asm | 23 +++---- .../kernel/asm/hash/sha2/message_schedule.asm | 62 +++++++++++-------- .../cpu/kernel/asm/hash/sha2/write_length.asm | 9 ++- evm/src/cpu/kernel/asm/memory/core.asm | 23 +++++++ 5 files changed, 81 insertions(+), 45 deletions(-) diff --git a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm b/evm/src/cpu/kernel/asm/hash/sha2/compression.asm index 5e1ff1f30a..f25ff30229 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/compression.asm @@ -4,6 +4,9 @@ // stack: num_blocks %mul_const(320) %add_const(2) + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address %endmacro global sha2_compression: @@ -24,9 +27,7 @@ global sha2_compression: // stack: i=0, message_schedule_addr, a[0]..h[0], retdest SWAP1 // stack: message_schedule_addr, i=0, a[0]..h[0], retdest - PUSH 0 - // stack: 0, message_schedule_addr, i=0, a[0]..h[0], retdest - %mload_current_general + %mload_current_general_no_offset // stack: num_blocks, message_schedule_addr, i=0, a[0]..h[0], retdest DUP1 // stack: num_blocks, num_blocks, message_schedule_addr, i=0, a[0]..h[0], retdest @@ -53,7 +54,7 @@ compression_loop: // stack: 4*i, message_schedule_addr, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest ADD // stack: message_schedule_addr + 4*i, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %mload_current_general_u32 + %mload_u32 // stack: W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest PUSH sha2_constants_k // stack: sha2_constants_k, W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest diff --git a/evm/src/cpu/kernel/asm/hash/sha2/main.asm b/evm/src/cpu/kernel/asm/hash/sha2/main.asm index 81ec391293..039379f39a 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/main.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/main.asm @@ -1,20 +1,22 @@ global sha2: // stack: virt, num_bytes, retdest - SWAP1 - // stack: num_bytes, virt, retdest - DUP2 - // stack: virt, num_bytes, virt, retdest - %mstore_current_general - // stack: virt, retdest + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address + // stack: addr, num_bytes, retdest + DUP1 SWAP2 + // stack: num_bytes, addr, addr, retdest + MSTORE_GENERAL + // stack: addr, retdest -// Precodition: input is in memory, starting at virt of kernel general segment, of the form +// Precondition: input is in memory, starting at addr of kernel general segment, of the form // num_bytes, x[0], x[1], ..., x[num_bytes - 1] // Postcodition: output is in memory, starting at 0, of the form // num_blocks, block0[0], ..., block0[63], block1[0], ..., blocklast[63] global sha2_pad: - // stack: virt, retdest - %mload_current_general + // stack: addr, retdest + MLOAD_GENERAL // stack: num_bytes, retdest // STEP 1: append 1 // insert 128 (= 1 << 7) at x[num_bytes+1] @@ -50,8 +52,7 @@ global sha2_pad: DUP1 // stack: num_blocks, num_blocks, retdest // STEP 5: write num_blocks to x[0] - PUSH 0 - %mstore_current_general + %mstore_current_general_no_offset // stack: num_blocks, retdest %message_schedule_addr_from_num_blocks %jump(sha2_gen_all_message_schedules) diff --git a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm b/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm index c9f542ce5f..b789a7fbb8 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm @@ -3,9 +3,12 @@ // stack: num_blocks %mul_const(64) %add_const(2) + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address %endmacro -// Precodition: stack contains address of one message block, followed by output address +// Precondition: stack contains address of one message block, followed by output address // Postcondition: 256 bytes starting at given output address contain the 64 32-bit chunks // of message schedule (in four-byte increments) gen_message_schedule_from_block: @@ -16,18 +19,17 @@ gen_message_schedule_from_block: // stack: block_addr + 32, block_addr, output_addr, retdest SWAP1 // stack: block_addr, block_addr + 32, output_addr, retdest - %mload_current_general_u256 + %mload_u256 // stack: block[0], block_addr + 32, output_addr, retdest SWAP1 // stack: block_addr + 32, block[0], output_addr, retdest - %mload_current_general_u256 + %mload_u256 // stack: block[1], block[0], output_addr, retdest SWAP2 // stack: output_addr, block[0], block[1], retdest %add_const(28) PUSH 8 // stack: counter=8, output_addr + 28, block[0], block[1], retdest - %jump(gen_message_schedule_from_block_0_loop) gen_message_schedule_from_block_0_loop: // Split the first half (256 bits) of the block into the first eight (32-bit) chunks of the message sdchedule. // stack: counter, output_addr, block[0], block[1], retdest @@ -43,7 +45,7 @@ gen_message_schedule_from_block_0_loop: // stack: block[0] % (1 << 32), block[0] >> 32, output_addr, counter, block[1], retdest DUP3 // stack: output_addr, block[0] % (1 << 32), block[0] >> 32, output_addr, counter, block[1], retdest - %mstore_current_general_u32 + %mstore_u32 // stack: block[0] >> 32, output_addr, counter, block[1], retdest SWAP1 // stack: output_addr, block[0] >> 32, counter, block[1], retdest @@ -81,7 +83,7 @@ gen_message_schedule_from_block_1_loop: // stack: block[1] % (1 << 32), block[1] >> 32, output_addr, counter, block[0], retdest DUP3 // stack: output_addr, block[1] % (1 << 32), block[1] >> 32, output_addr, counter, block[0], retdest - %mstore_current_general_u32 + %mstore_u32 // stack: block[1] >> 32, output_addr, counter, block[0], retdest SWAP1 // stack: output_addr, block[1] >> 32, counter, block[0], retdest @@ -111,39 +113,43 @@ gen_message_schedule_remaining_loop: // stack: counter, output_addr, block[0], block[1], retdest SWAP1 // stack: output_addr, counter, block[0], block[1], retdest - DUP1 - // stack: output_addr, output_addr, counter, block[0], block[1], retdest - %sub_const(8) + PUSH 8 + DUP2 + // stack: output_addr, 2*4, output_addr, counter, block[0], block[1], retdest + SUB // stack: output_addr - 2*4, output_addr, counter, block[0], block[1], retdest - %mload_current_general_u32 + %mload_u32 // stack: x[output_addr - 2*4], output_addr, counter, block[0], block[1], retdest %sha2_sigma_1 // stack: sigma_1(x[output_addr - 2*4]), output_addr, counter, block[0], block[1], retdest SWAP1 // stack: output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - DUP1 - // stack: output_addr, output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %sub_const(28) + PUSH 28 + DUP2 + // stack: output_addr, 7*4, output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest + SUB // stack: output_addr - 7*4, output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_current_general_u32 + %mload_u32 // stack: x[output_addr - 7*4], output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest SWAP1 // stack: output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - DUP1 - // stack: output_addr, output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %sub_const(60) + PUSH 60 + DUP2 + // stack: output_addr, 15*4, output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest + SUB // stack: output_addr - 15*4, output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_current_general_u32 + %mload_u32 // stack: x[output_addr - 15*4], output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest %sha2_sigma_0 // stack: sigma_0(x[output_addr - 15*4]), output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest SWAP1 // stack: output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - DUP1 - // stack: output_addr, output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %sub_const(64) + PUSH 64 + DUP2 + // stack: output_addr, 16*4, output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest + SUB // stack: output_addr - 16*4, output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_current_general_u32 + %mload_u32 // stack: x[output_addr - 16*4], output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest SWAP1 // stack: output_addr, x[output_addr - 16*4], sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest @@ -155,7 +161,7 @@ gen_message_schedule_remaining_loop: // stack: sigma_1(x[output_addr - 2*4]) + x[output_addr - 16*4] + sigma_0(x[output_addr - 15*4]) + x[output_addr - 7*4], output_addr, counter, block[0], block[1], retdest DUP2 // stack: output_addr, sigma_1(x[output_addr - 2*4]) + x[output_addr - 16*4] + sigma_0(x[output_addr - 15*4]) + x[output_addr - 7*4], output_addr, counter, block[0], block[1], retdest - %mstore_current_general_u32 + %mstore_u32 // stack: output_addr, counter, block[0], block[1], retdest %add_const(4) // stack: output_addr + 4, counter, block[0], block[1], retdest @@ -178,12 +184,14 @@ global sha2_gen_all_message_schedules: // stack: output_addr, retdest DUP1 // stack: output_addr, output_addr, retdest - PUSH 0 - // stack: 0, output_addr, output_addr, retdest - %mload_current_general + %mload_current_general_no_offset // stack: num_blocks, output_addr, output_addr, retdest PUSH 1 - // stack: cur_addr = 1, counter = num_blocks, output_addr, output_addr, retdest + // stack: cur_offset = 1, counter = num_blocks, output_addr, output_addr, retdest + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address + // stack: cur_addr, counter, output_addr, output_addr, retdest gen_all_message_schedules_loop: // stack: cur_addr, counter, cur_output_addr, output_addr, retdest PUSH gen_all_message_schedules_loop_end diff --git a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm index c412a666ff..c9a0642fa2 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm @@ -1,5 +1,8 @@ %macro sha2_write_length - // stack: last_addr, length + // stack: last_addr_offset, length + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address SWAP1 // stack: length, last_addr DUP1 @@ -8,7 +11,7 @@ // stack: length % (1 << 8), length, last_addr DUP3 // stack: last_addr, length % (1 << 8), length, last_addr - %mstore_current_general + %swap_mstore %rep 7 // For i = 0 to 6 @@ -26,7 +29,7 @@ // stack: (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 DUP3 // stack: last_addr - i - 2, (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 - %mstore_current_general + %swap_mstore %endrep %pop2 diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index da8a05fb18..dad5979f22 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -124,6 +124,16 @@ // stack: value %endmacro +// Load a single value from the kernel general memory, in the current context (not the kernel's context). +%macro mload_current_general_no_offset + // stack: + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address_no_offset + MLOAD_GENERAL + // stack: value +%endmacro + // Load a big-endian u32 from kernel general memory in the current context. %macro mload_current_general_u32 // stack: offset @@ -185,6 +195,19 @@ // stack: (empty) %endmacro +// Store a single value to kernel general memory in the current context. +%macro mstore_current_general_no_offset + // stack: value + PUSH @SEGMENT_KERNEL_GENERAL + // stack: segment, value + GET_CONTEXT + // stack: context, segment, value + %build_address_no_offset + SWAP1 + MSTORE_GENERAL + // stack: (empty) +%endmacro + %macro mstore_current_general(offset) // stack: value PUSH $offset From 8cb80e097d2378b33b5e6f8eeabfd6430b7b1926 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:42:11 -0500 Subject: [PATCH 119/175] Improve `blake2f` call (#1477) * Improve on blake2 operations * Comments * Remove swap_mstore calls by changing stack macros --- .../cpu/kernel/asm/hash/blake2/addresses.asm | 18 +++-- .../cpu/kernel/asm/hash/blake2/blake2_f.asm | 70 +++++++++---------- .../kernel/asm/hash/blake2/g_functions.asm | 20 +++--- evm/src/cpu/kernel/asm/hash/blake2/hash.asm | 6 +- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm b/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm index 06b93f9ea9..5beb1dee64 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm @@ -1,12 +1,20 @@ // Address where the working version of the hash value is stored. +// It is ready to be used, i.e. already containing the current context +// and SEGMENT_KERNEL_GENERAL. %macro blake2_hash_value_addr - PUSH 0 - // stack: 0 - %mload_current_general - // stack: num_blocks + PUSH @SEGMENT_KERNEL_GENERAL + // stack: segment + GET_CONTEXT + // stack: context, segment + %build_address_no_offset + DUP1 + MLOAD_GENERAL + // stack: num_blocks, addr %block_size %add_const(2) - // stack: num_bytes+2 + // stack: num_bytes+2, addr + ADD + // stack: addr %endmacro // Address where the working version of the compression internal state is stored. diff --git a/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm b/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm index 95a4749e0f..d1a4a2ab64 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm @@ -6,9 +6,9 @@ global blake2_f: // stack: addr, rounds, h0...h7, m0...m15, t0, t1, flag, retdest %rep 8 // stack: addr, rounds, h_i, ... - %stack (addr, rounds, h_i) -> (addr, h_i, addr, rounds) - // stack: addr, h_i, addr, rounds, ... - %mstore_current_general + %stack (addr, rounds, h_i) -> (h_i, addr, addr, rounds) + // stack: h_i, addr, addr, rounds, ... + MSTORE_GENERAL %increment %endrep @@ -21,9 +21,9 @@ global blake2_f: // stack: message_addr, rounds, m0...m15, t0, t1, flag, retdest %rep 16 // stack: message_addr, rounds, m_i, ... - %stack (message_addr, rounds, m_i) -> (message_addr, m_i, message_addr, rounds) - // stack: message_addr, m_i, message_addr, rounds, ... - %mstore_current_general + %stack (message_addr, rounds, m_i) -> (m_i, message_addr, message_addr, rounds) + // stack: m_i, message_addr, message_addr, rounds, ... + MSTORE_GENERAL %increment %endrep @@ -37,7 +37,7 @@ global blake2_f: // stack: addr, ... DUP1 // stack: addr, addr, ... - %mload_current_general + MLOAD_GENERAL // stack: val, addr, ... SWAP1 // stack: addr, val, ... @@ -53,31 +53,30 @@ global blake2_f: // First eight words of the internal state: current hash value h_0, ..., h_7. %rep 8 - SWAP1 - DUP2 - %mstore_current_general + DUP1 + SWAP2 + MSTORE_GENERAL %increment %endrep // stack: start + 8, rounds, t0, t1, flag, retdest // Next four values of the internal state: first four IV values. PUSH 0 - // stack: 0, start + 8, rounds, t0, t1, flag, retdest + // stack: 0, addr, rounds, t0, t1, flag, retdest %rep 4 - // stack: i, loc, ... - DUP1 - // stack: i, i, loc, ... + // stack: i, addr, ... + DUP2 + DUP2 + // stack: i, addr, i, addr, ... %blake2_iv - // stack: IV_i, i, loc, ... - DUP3 - // stack: loc, IV_i, i, loc, ... - %mstore_current_general - // stack: i, loc, ... + // stack: IV_i, addr, i, addr, ... + MSTORE_GENERAL + // stack: i, addr, ... %increment SWAP1 %increment SWAP1 - // stack: i + 1, loc + 1,... + // stack: i + 1, addr + 1,... %endrep // stack: 4, start + 12, rounds, t0, t1, flag, retdest POP @@ -92,29 +91,28 @@ global blake2_f: // Last four values of the internal state: last four IV values, XOR'd with // the values (t0, t1, invert_if_flag, 0). %rep 4 - // stack: i, loc, val, next_val,... - DUP1 - // stack: i, i, loc, val, next_val,... + // stack: i, addr, val, next_val,... + DUP2 + DUP2 + // stack: i, addr, i, addr, val, next_val,... %blake2_iv - // stack: IV_i, i, loc, val, next_val,... - DUP4 - // stack: val, IV_i, i, loc, val, next_val,... + // stack: IV_i, addr, i, addr, val, next_val,... + DUP5 + // stack: val, IV_i, addr, i, addr, val, next_val,... XOR - // stack: val ^ IV_i, i, loc, val, next_val,... - DUP3 - // stack: loc, val ^ IV_i, i, loc, val, next_val,... - %mstore_current_general - // stack: i, loc, val, next_val,... + // stack: val ^ IV_i, addr, i, addr, val, next_val,... + MSTORE_GENERAL + // stack: i, addr, val, next_val,... %increment - // stack: i + 1, loc, val, next_val,... + // stack: i + 1, addr, val, next_val,... SWAP2 - // stack: val, loc, i + 1, next_val,... + // stack: val, addr, i + 1, next_val,... POP - // stack: loc, i + 1, next_val,... + // stack: addr, i + 1, next_val,... %increment - // stack: loc + 1, i + 1, next_val,... + // stack: addr + 1, i + 1, next_val,... SWAP1 - // stack: i + 1, loc + 1, next_val,... + // stack: i + 1, addr + 1, next_val,... %endrep // stack: 8, start + 16, rounds, retdest %pop2 diff --git a/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm b/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm index 45e54ff43f..d521da6d80 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm @@ -11,28 +11,28 @@ DUP11 // stack: start, a, b, c, d, a, b, c, d, x, y, start ADD - %mload_current_general + MLOAD_GENERAL // stack: v[a], b, c, d, a, b, c, d, x, y, start SWAP1 // stack: b, v[a], c, d, a, b, c, d, x, y, start DUP11 // stack: start, b, v[a], c, d, a, b, c, d, x, y, start ADD - %mload_current_general + MLOAD_GENERAL // stack: v[b], v[a], c, d, a, b, c, d, x, y, start SWAP2 // stack: c, v[a], v[b], d, a, b, c, d, x, y, start DUP11 // stack: start, c, v[a], v[b], d, a, b, c, d, x, y, start ADD - %mload_current_general + MLOAD_GENERAL // stack: v[c], v[a], v[b], d, a, b, c, d, x, y, start SWAP3 // stack: d, v[a], v[b], v[c], a, b, c, d, x, y, start DUP11 // stack: start, d, v[a], v[b], v[c], a, b, c, d, x, y, start ADD - %mload_current_general + MLOAD_GENERAL // stack: v[d], v[a], v[b], v[c], a, b, c, d, x, y, start %stack (vd, vs: 3) -> (vs, vd) // stack: v[a], v[b], v[c], v[d], a, b, c, d, x, y, start @@ -95,13 +95,13 @@ %stack (vb, vc, vd, va, a, b, c, d, x, y, start) -> (start, a, va, start, b, vb, start, c, vc, start, d, vd) // stack: start, a, v[a]'', start, b, v[b]'', start, c, v[c]'', start, d, v[d]'' ADD - %mstore_current_general + %swap_mstore ADD - %mstore_current_general + %swap_mstore ADD - %mstore_current_general + %swap_mstore ADD - %mstore_current_general + %swap_mstore %endmacro %macro call_blake2_g_function(a, b, c, d, x_idx, y_idx) @@ -113,7 +113,7 @@ // stack: s[y_idx], round, start %blake2_message_addr ADD - %mload_current_general + MLOAD_GENERAL // stack: m[s[y_idx]], round, start PUSH $x_idx DUP3 @@ -122,7 +122,7 @@ // stack: s[x_idx], m[s[y_idx]], round, start %blake2_message_addr ADD - %mload_current_general + MLOAD_GENERAL // stack: m[s[x_idx]], m[s[y_idx]], round, start %stack (ss: 2, r, s) -> (ss, s, r, s) // stack: m[s[x_idx]], m[s[y_idx]], start, round, start diff --git a/evm/src/cpu/kernel/asm/hash/blake2/hash.asm b/evm/src/cpu/kernel/asm/hash/blake2/hash.asm index 24ec9caba8..ab0d247633 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/hash.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/hash.asm @@ -5,13 +5,13 @@ blake2_generate_new_hash_value: // stack: addr, i, retdest DUP2 ADD - %mload_current_general + MLOAD_GENERAL // stack: h_i, i, retdest %blake2_internal_state_addr // stack: addr, h_i, i, retdest DUP3 ADD - %mload_current_general + MLOAD_GENERAL // stack: v_i, h_i, i, retdest %blake2_internal_state_addr // stack: addr, v_i, h_i, i, retdest @@ -21,7 +21,7 @@ blake2_generate_new_hash_value: // stack: i, addr, h_i, v_i, retdest ADD %add_const(8) - %mload_current_general + MLOAD_GENERAL // stack: v_(i+8), h_i, v_i, retdest XOR XOR From acc59c3563df863e235f0e85c35f860b64e52b17 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:36:14 -0500 Subject: [PATCH 120/175] Speed-up `bn254` pairing operation (#1476) * Speed-up bn254 operations * Add comment for write_fp254_12_unit macro * Refactor some macros * nit: comment * Add newline --- .../bn254/curve_arithmetic/final_exponent.asm | 23 +- .../bn254/curve_arithmetic/miller_loop.asm | 134 ++-- .../curve/bn254/curve_arithmetic/pairing.asm | 17 +- .../curve/bn254/field_arithmetic/inverse.asm | 7 +- .../asm/curve/bn254/field_arithmetic/util.asm | 721 +++++++++--------- 5 files changed, 459 insertions(+), 443 deletions(-) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm index d1f32ce65a..035cb43830 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm @@ -56,14 +56,21 @@ final_exp: %stack (val) -> (val, 0, val) // stack: val, 0, val, retdest %move_fp254_12 - // stack: 0, val, retdest {0: sqr} - %stack () -> (1, 1, 1) - // stack: 1, 1, 1, 0, val, retdest - %mstore_bn254_pairing(12) - %mstore_bn254_pairing(24) - %mstore_bn254_pairing(36) - // stack: 0, val, retdest {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (64, 62, 65) + // dest addr returned by %move_fp254_12 is already scaled + // stack: addr, val, retdest {0: sqr} + + // Write 1s at offset 12, 24 and 36 + PUSH 12 + ADD + DUP1 %add_const(12) + DUP1 %add_const(12) + // stack: addr_1, addr_2, addr_3 + %rep 3 + PUSH 1 MSTORE_GENERAL + %endrep + + // stack: val, retdest {0: sqr, 12: y0, 24: y2, 36: y4} + %stack () -> (64, 62, 65, 0) // stack: 64, 62, 65, 0, val, retdest {0: sqr, 12: y0, 24: y2, 36: y4} %jump(power_loop_4) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm index 3b4ded57e2..99cf24e71d 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm @@ -27,9 +27,9 @@ global bn254_miller: // stack: ptr, out, retdest - %stack (ptr, out) -> (out, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ptr, out) - // stack: out, unit, ptr, out, retdest - %store_fp254_12 + %stack (ptr, out) -> (out, ptr, out) + // stack: out, ptr, out, retdest + %write_fp254_12_unit // stack: ptr, out, retdest %load_fp254_6 // stack: P, Q, out, retdest @@ -39,7 +39,7 @@ global bn254_miller: miller_loop: POP // stack: times , O, P, Q, out, retdest - DUP1 + DUP1 ISZERO // stack: break?, times , O, P, Q, out, retdest %jumpi(miller_return) @@ -60,7 +60,7 @@ miller_return: miller_one: // stack: 0xnm, times, O, P, Q, out, retdest - DUP1 + DUP1 %lt_const(0x20) // stack: skip?, 0xnm, times, O, P, Q, out, retdest %jumpi(miller_zero) @@ -73,7 +73,7 @@ miller_one: miller_zero: // stack: m , times, O, P, Q, out, retdest - DUP1 + DUP1 ISZERO // stack: skip?, m , times, O, P, Q, out, retdest %jumpi(miller_loop) @@ -93,8 +93,8 @@ miller_zero: mul_tangent: // stack: retdest, 0xnm, times, O, P, Q, out - PUSH mul_tangent_2 - DUP13 + PUSH mul_tangent_2 + DUP13 PUSH mul_tangent_1 // stack: mul_tangent_1, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out %stack (mul_tangent_1, out) -> (out, out, mul_tangent_1, out) @@ -107,7 +107,7 @@ mul_tangent_1: DUP13 DUP13 // stack: Q, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - DUP11 + DUP11 DUP11 // stack: O, Q, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out %tangent @@ -141,15 +141,15 @@ mul_cord: // stack: 0xnm, times, O, P, Q, out PUSH mul_cord_1 // stack: mul_cord_1, 0xnm, times, O, P, Q, out - DUP11 - DUP11 - DUP11 + DUP11 + DUP11 + DUP11 DUP11 // stack: Q, mul_cord_1, 0xnm, times, O, P, Q, out - DUP9 + DUP9 DUP9 // stack: O, Q, mul_cord_1, 0xnm, times, O, P, Q, out - DUP13 + DUP13 DUP13 // stack: P, O, Q, mul_cord_1, 0xnm, times, O, P, Q, out %cord @@ -188,43 +188,51 @@ after_add: %macro tangent // stack: px, py, qx, qx_, qy, qy_ - %stack (px, py) -> (py, py , 9, px, py) - // stack: py, py , 9, px, py, qx, qx_, qy, qy_ + PUSH 12 + %create_bn254_pairing_address + %stack (addr12, px, py) -> (py, py, 9, addr12, addr12, px, py) + // stack: py, py, 9, addr12, addr12, px, py, qx, qx_, qy, qy_ MULFP254 - // stack: py^2 , 9, px, py, qx, qx_, qy, qy_ + // stack: py^2, 9, addr12, addr12, px, py, qx, qx_, qy, qy_ SUBFP254 - // stack: py^2 - 9, px, py, qx, qx_, qy, qy_ - %mstore_bn254_pairing(12) - // stack: px, py, qx, qx_, qy, qy_ - DUP1 + // stack: py^2 - 9, addr12, addr12, px, py, qx, qx_, qy, qy_ + MSTORE_GENERAL + // stack: addr12, px, py, qx, qx_, qy, qy_ + %add_const(2) DUP1 + SWAP2 + DUP1 MULFP254 - // stack: px^2, py, qx, qx_, qy, qy_ - PUSH 3 + // stack: px^2, addr14, addr14, py, qx, qx_, qy, qy_ + PUSH 3 MULFP254 - // stack: 3*px^2, py, qx, qx_, qy, qy_ - PUSH 0 + // stack: 3*px^2, addr14, addr14, py, qx, qx_, qy, qy_ + PUSH 0 SUBFP254 - // stack: -3*px^2, py, qx, qx_, qy, qy_ - SWAP2 - // stack: qx, py, -3px^2, qx_, qy, qy_ - DUP3 + // stack: -3*px^2, addr14, addr14, py, qx, qx_, qy, qy_ + SWAP4 + // stack: qx, addr14, addr14, py, -3px^2, qx_, qy, qy_ + DUP5 MULFP254 - // stack: (-3*px^2)qx, py, -3px^2, qx_, qy, qy_ - %mstore_bn254_pairing(14) - // stack: py, -3px^2, qx_, qy, qy_ - PUSH 2 + // stack: (-3*px^2)qx, addr14, addr14, py, -3px^2, qx_, qy, qy_ + MSTORE_GENERAL + // stack: addr14, py, -3px^2, qx_, qy, qy_ + DUP1 %add_const(6) + // stack: addr20, addr14, py, -3px^2, qx_, qy, qy_ + %stack (addr20, addr14, py) -> (2, py, addr20, addr14) MULFP254 - // stack: 2py, -3px^2, qx_, qy, qy_ - SWAP3 - // stack: qy, -3px^2, qx_, 2py, qy_ - DUP4 + // stack: 2py, addr20, addr14, -3px^2, qx_, qy, qy_ + SWAP5 + // stack: qy, addr20, addr14, -3px^2, qx_, 2py, qy_ + DUP6 MULFP254 - // stack: (2py)qy, -3px^2, qx_, 2py, qy_ - %mstore_bn254_pairing(20) - // stack: -3px^2, qx_, 2py, qy_ + // stack: (2py)qy, addr20, addr14, -3px^2, qx_, 2py, qy_ + MSTORE_GENERAL + // stack: addr14, -3px^2, qx_, 2py, qy_ + %add_const(1) SWAP2 + // stack: qx_, -3px^2, addr15, 2py, qy_ MULFP254 - // stack: (-3px^2)*qx_, 2py, qy_ - %mstore_bn254_pairing(15) + // stack: (-3px^2)*qx_, addr15, 2py, qy_ + MSTORE_GENERAL // stack: 2py, qy_ MULFP254 // stack: (2py)*qy_ @@ -240,11 +248,11 @@ after_add: %macro cord // stack: p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - DUP1 - DUP5 + DUP1 + DUP5 MULFP254 // stack: p2y*p1x, p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - DUP3 + DUP3 DUP5 MULFP254 // stack: p1y*p2x , p2y*p1x, p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ @@ -284,10 +292,34 @@ after_add: %endmacro %macro clear_line - %stack () -> (0, 0, 0, 0, 0) - %mstore_bn254_pairing(12) - %mstore_bn254_pairing(14) - %mstore_bn254_pairing(15) - %mstore_bn254_pairing(20) - %mstore_bn254_pairing(21) + PUSH 12 + %create_bn254_pairing_address + // stack: addr12 + DUP1 %add_const(2) + // stack: addr14, addr12 + DUP1 %add_const(1) + // stack: addr15, addr14, addr12 + DUP1 %add_const(5) + // stack: addr20, addr15, addr14, addr12 + DUP1 %add_const(1) + // stack: addr21, addr20, addr15, addr14, addr12 + %rep 5 + PUSH 0 MSTORE_GENERAL + %endrep +%endmacro + + +%macro write_fp254_12_unit + // Write 0x10000000000000000000000 with MSTORE_32BYTES_12, + // effectively storing 1 at the initial offset, and 11 0s afterwards. + + // stack: out + %create_bn254_pairing_address + // stack: addr + PUSH 0x10000000000000000000000 + SWAP1 + // stack: addr, 0x10000000000000000000000 + MSTORE_32BYTES_12 + POP + // stack: %endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm index c63c3b35e3..735d001aae 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm @@ -83,9 +83,9 @@ bn_pairing_invalid_input: bn254_pairing_start: // stack: 0, k, inp, out, retdest - %stack (j, k, inp, out) -> (out, 1, k, inp, out, bn254_pairing_output_validation, out) - // stack: out, 1, k, inp, out, bn254_pairing_output_validation, out, retdest - %mstore_bn254_pairing + %stack (j, k, inp, out) -> (out, k, inp, out, bn254_pairing_output_validation, out) + // stack: out, k, inp, out, bn254_pairing_output_validation, out, retdest + %mstore_bn254_pairing_value(1) // stack: k, inp, out, bn254_pairing_output_validation, out, retdest bn254_pairing_loop: @@ -125,8 +125,9 @@ bn_skip_input: bn254_pairing_output_validation: // stack: out, retdest + %create_bn254_pairing_address PUSH 1 - // stack: check, out, retdest + // stack: check, out_addr, retdest %check_output_term %check_output_term(1) %check_output_term(2) @@ -139,15 +140,15 @@ bn254_pairing_output_validation: %check_output_term(9) %check_output_term(10) %check_output_term(11) - // stack: check, out, retdest - %stack (check, out, retdest) -> (retdest, check) + // stack: check, out_addr, retdest + %stack (check, out_addr, retdest) -> (retdest, check) JUMP %macro check_output_term // stack: check, out DUP2 // stack: out0, check, out - %mload_bn254_pairing + MLOAD_GENERAL // stack: f0, check, out %eq_const(1) // stack: check0, check, out @@ -160,7 +161,7 @@ bn254_pairing_output_validation: DUP2 %add_const($j) // stack: outj, check, out - %mload_bn254_pairing + MLOAD_GENERAL // stack: fj, check, out ISZERO // stack: checkj, check, out diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm index 28e3b5d20e..7c7729057c 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm @@ -42,9 +42,12 @@ check_inv_fp254_12: // stack: unit?, retdest %assert_eq_unit_fp254_12 // stack: retdest + PUSH 60 + %create_bn254_pairing_address PUSH 0 - // stack: 0, retdest - %mstore_bn254_pairing(60) + // stack: 0, addr, retdest + MSTORE_GENERAL + // stack: retdest JUMP %macro prover_inv_fp254_12 diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm index 86b179ba9e..897404dbf2 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm @@ -20,6 +20,24 @@ // stack: %endmacro +// Build an address on the current context within SEGMENT_BN_PAIRING. +%macro create_bn254_pairing_address + // stack: offset + PUSH @SEGMENT_BN_PAIRING + GET_CONTEXT + %build_address + // stack: addr +%endmacro + +// Store a single value to bn254 pairings memory. +%macro mstore_bn254_pairing_value(value) + // stack: offset + %create_bn254_pairing_address + PUSH $value + MSTORE_GENERAL + // stack: +%endmacro + %macro mstore_bn254_pairing(offset) // stack: value PUSH $offset @@ -32,14 +50,15 @@ %macro load_fp254_2 // stack: ptr - DUP1 + %create_bn254_pairing_address + DUP1 %add_const(1) - // stack: ind1, ptr - %mload_bn254_pairing - // stack: x1, ptr + // stack: addr1, addr + MLOAD_GENERAL + // stack: x1, addr SWAP1 - // stack: ind0, x1 - %mload_bn254_pairing + // stack: addr0, x1 + MLOAD_GENERAL // stack: x0, x1 %endmacro @@ -101,14 +120,14 @@ // stack: b, a , b DUP2 // stack: a , b, a , b - PUSH 9 + PUSH 9 MULFP254 // stack: 9a , b, a , b SUBFP254 // stack: 9a - b, a , b SWAP2 // stack: b , a, 9a - b - PUSH 9 + PUSH 9 MULFP254 // stack 9b , a, 9a - b ADDFP254 @@ -145,24 +164,25 @@ %macro load_fp254_4 // stack: ptr - DUP1 + %create_bn254_pairing_address + DUP1 %add_const(2) - // stack: ind2, ptr - %mload_bn254_pairing - // stack: x2, ptr - DUP2 + // stack: addr2, addr + MLOAD_GENERAL + // stack: x2, addr + DUP2 %add_const(1) - // stack: ind1, x2, ptr - %mload_bn254_pairing - // stack: x1, x2, ptr - DUP3 + // stack: addr1, x2, addr + MLOAD_GENERAL + // stack: x1, x2, addr + DUP3 %add_const(3) - // stack: ind3, x1, x2, ptr - %mload_bn254_pairing - // stack: x3, x1, x2, ptr + // stack: addr3, x1, x2, addr + MLOAD_GENERAL + // stack: x3, x1, x2, addr SWAP3 - // stack: ind0, x1, x2, x3 - %mload_bn254_pairing + // stack: addr0, x1, x2, x3 + MLOAD_GENERAL // stack: x0, x1, x2, x3 %endmacro @@ -170,228 +190,177 @@ %macro load_fp254_6 // stack: ptr - DUP1 + %create_bn254_pairing_address + DUP1 %add_const(4) - // stack: ind4, ptr - %mload_bn254_pairing - // stack: x4, ptr - DUP2 + // stack: addr4, addr + MLOAD_GENERAL + // stack: x4, addr + DUP2 %add_const(3) - // stack: ind3, x4, ptr - %mload_bn254_pairing - // stack: x3, x4, ptr - DUP3 + // stack: addr3, x4, addr + MLOAD_GENERAL + // stack: x3, x4, addr + DUP3 %add_const(2) - // stack: ind2, x3, x4, ptr - %mload_bn254_pairing - // stack: x2, x3, x4, ptr - DUP4 + // stack: addr2, x3, x4, addr + MLOAD_GENERAL + // stack: x2, x3, x4, addr + DUP4 %add_const(1) - // stack: ind1, x2, x3, x4, ptr - %mload_bn254_pairing - // stack: x1, x2, x3, x4, ptr - DUP5 + // stack: addr1, x2, x3, x4, addr + MLOAD_GENERAL + // stack: x1, x2, x3, x4, addr + DUP5 %add_const(5) - // stack: ind5, x1, x2, x3, x4, ptr - %mload_bn254_pairing - // stack: x5, x1, x2, x3, x4, ptr + // stack: addr5, x1, x2, x3, x4, addr + MLOAD_GENERAL + // stack: x5, x1, x2, x3, x4, addr SWAP5 - // stack: ind0, x1, x2, x3, x4, x5 - %mload_bn254_pairing + // stack: addr0, x1, x2, x3, x4, x5 + MLOAD_GENERAL // stack: x0, x1, x2, x3, x4, x5 %endmacro -// cost: 6 loads + 6 pushes + 5 adds = 6*4 + 6*1 + 5*2 = 40 %macro load_fp254_6(ptr) // stack: - PUSH $ptr - %add_const(5) - // stack: ind5 - %mload_bn254_pairing - // stack: x5 - PUSH $ptr - %add_const(4) - // stack: ind4, x5 - %mload_bn254_pairing - // stack: x4, x5 - PUSH $ptr - %add_const(3) - // stack: ind3, x4, x5 - %mload_bn254_pairing - // stack: x3, x4, x5 - PUSH $ptr - %add_const(2) - // stack: ind2, x3, x4, x5 - %mload_bn254_pairing - // stack: x2, x3, x4, x5 - PUSH $ptr - %add_const(1) - // stack: ind1, x2, x3, x4, x5 - %mload_bn254_pairing - // stack: x1, x2, x3, x4, x5 PUSH $ptr - // stack: ind0, x1, x2, x3, x4, x5 - %mload_bn254_pairing - // stack: x0, x1, x2, x3, x4, x5 + %load_fp254_6 + // stack: x0, x1, x2, x3, x4, x5 %endmacro -// cost: 6 stores + 6 swaps/dups + 5 adds = 6*4 + 6*1 + 5*2 = 40 %macro store_fp254_6 // stack: ptr, x0, x1, x2, x3, x4 , x5 + %create_bn254_pairing_address SWAP5 - // stack: x4, x0, x1, x2, x3, ptr, x5 - DUP6 + // stack: x4, x0, x1, x2, x3, addr, x5 + DUP6 %add_const(4) - // stack: ind4, x4, x0, x1, x2, x3, ptr, x5 - %mstore_bn254_pairing - // stack: x0, x1, x2, x3, ptr, x5 + // stack: addr4, x4, x0, x1, x2, x3, addr, x5 + %swap_mstore + // stack: x0, x1, x2, x3, addr, x5 DUP5 - // stack: ind0, x0, x1, x2, x3, ptr, x5 - %mstore_bn254_pairing - // stack: x1, x2, x3, ptr, x5 - DUP4 + // stack: addr0, x0, x1, x2, x3, addr, x5 + %swap_mstore + // stack: x1, x2, x3, addr, x5 + DUP4 %add_const(1) - // stack: ind1, x1, x2, x3, ptr, x5 - %mstore_bn254_pairing - // stack: x2, x3, ptr, x5 - DUP3 + // stack: addr1, x1, x2, x3, addr, x5 + %swap_mstore + // stack: x2, x3, addr, x5 + DUP3 %add_const(2) - // stack: ind2, x2, x3, ptr, x5 - %mstore_bn254_pairing - // stack: x3, ptr, x5 - DUP2 + // stack: addr2, x2, x3, addr, x5 + %swap_mstore + // stack: x3, addr, x5 + DUP2 %add_const(3) - // stack: ind3, x3, ptr, x5 - %mstore_bn254_pairing - // stack: ptr, x5 + // stack: addr3, x3, addr, x5 + %swap_mstore + // stack: addr, x5 %add_const(5) - // stack: ind5, x5 - %mstore_bn254_pairing + // stack: addr5, x5 + %swap_mstore // stack: %endmacro -// cost: 6 stores + 7 swaps/dups + 5 adds + 6 doubles = 6*4 + 7*1 + 5*2 + 6*2 = 53 %macro store_fp254_6_double // stack: ptr, x0, x1, x2, x3, x4, x5 + %create_bn254_pairing_address SWAP6 - // stack: x5, x0, x1, x2, x3, x4, ptr - PUSH 2 + // stack: x5, x0, x1, x2, x3, x4, addr + PUSH 2 MULFP254 - // stack: 2*x5, x0, x1, x2, x3, x4, ptr - DUP7 + // stack: 2*x5, x0, x1, x2, x3, x4, addr + DUP7 %add_const(5) - // stack: ind5, 2*x5, x0, x1, x2, x3, x4, ptr - %mstore_bn254_pairing - // stack: x0, x1, x2, x3, x4, ptr - PUSH 2 + // stack: addr5, 2*x5, x0, x1, x2, x3, x4, addr + %swap_mstore + // stack: x0, x1, x2, x3, x4, addr + PUSH 2 MULFP254 - // stack: 2*x0, x1, x2, x3, x4, ptr + // stack: 2*x0, x1, x2, x3, x4, addr DUP6 - // stack: ind0, 2*x0, x1, x2, x3, x4, ptr - %mstore_bn254_pairing - // stack: x1, x2, x3, x4, ptr - PUSH 2 + // stack: addr0, 2*x0, x1, x2, x3, x4, addr + %swap_mstore + // stack: x1, x2, x3, x4, addr + PUSH 2 MULFP254 - // stack: 2*x1, x2, x3, x4, ptr - DUP5 + // stack: 2*x1, x2, x3, x4, addr + DUP5 %add_const(1) - // stack: ind1, 2*x1, x2, x3, x4, ptr - %mstore_bn254_pairing - // stack: x2, x3, x4, ptr - PUSH 2 + // stack: addr1, 2*x1, x2, x3, x4, addr + %swap_mstore + // stack: x2, x3, x4, addr + PUSH 2 MULFP254 - // stack: 2*x2, x3, x4, ptr - DUP4 + // stack: 2*x2, x3, x4, addr + DUP4 %add_const(2) - // stack: ind2, 2*x2, x3, x4, ptr - %mstore_bn254_pairing - // stack: x3, x4, ptr + // stack: addr2, 2*x2, x3, x4, addr + %swap_mstore + // stack: x3, x4, addr PUSH 2 MULFP254 - // stack: 2*x3, x4, ptr - DUP3 + // stack: 2*x3, x4, addr + DUP3 %add_const(3) - // stack: ind3, 2*x3, x4, ptr - %mstore_bn254_pairing - // stack: x4, ptr - PUSH 2 + // stack: addr3, 2*x3, x4, addr + %swap_mstore + // stack: x4, addr + PUSH 2 MULFP254 - // stack: 2*x4, ptr + // stack: 2*x4, addr SWAP1 - // stack: ptr, 2*x4 + // stack: addr, 2*x4 %add_const(4) - // stack: ind4, 2*x4 - %mstore_bn254_pairing + // stack: addr4, 2*x4 + %swap_mstore // stack: %endmacro -// cost: 6 stores + 6 pushes + 5 adds = 6*4 + 6*1 + 5*2 = 40 %macro store_fp254_6(ptr) - // stack: x0, x1, x2, x3, x4, x5 + // stack: x0, x1, x2, x3, x4, x5 PUSH $ptr - // stack: ind0, x0, x1, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x1, x2, x3, x4, x5 - PUSH $ptr - %add_const(1) - // stack: ind1, x1, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x2, x3, x4, x5 - PUSH $ptr - %add_const(2) - // stack: ind2, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x3, x4, x5 - PUSH $ptr - %add_const(3) - // stack: ind3, x3, x4, x5 - %mstore_bn254_pairing - // stack: x4, x5 - PUSH $ptr - %add_const(4) - // stack: ind4, x4, x5 - %mstore_bn254_pairing - // stack: x5 - PUSH $ptr - %add_const(5) - // stack: ind5, x5 - %mstore_bn254_pairing + %store_fp254_6 // stack: %endmacro -// cost: store (40) + i9 (9) = 49 %macro store_fp254_6_sh(ptr) // stack: x0, x1, x2, x3, x4, x5 - PUSH $ptr + PUSH $ptr + %create_bn254_pairing_address + // stack: addr, x0, x1, x2, x3, x4, x5 %add_const(2) - // stack: ind2, x0, x1, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x1, x2, x3, x4, x5 - PUSH $ptr - %add_const(3) - // stack: ind3, x1, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x2, x3, x4, x5 - PUSH $ptr - %add_const(4) - // stack: ind4, x2, x3, x4, x5 - %mstore_bn254_pairing - // stack: x3, x4, x5 - PUSH $ptr - %add_const(5) - // stack: ind5, x3, x4, x5 - %mstore_bn254_pairing + DUP1 + // stack: addr2, addr2, x0, x1, x2, x3, x4, x5 + SWAP2 MSTORE_GENERAL + // stack: addr2, x1, x2, x3, x4, x5 + %add_const(1) + DUP1 + // stack: addr3, addr3, x1, x2, x3, x4, x5 + SWAP2 MSTORE_GENERAL + // stack: addr3, x2, x3, x4, x5 + %add_const(1) + DUP1 + // stack: addr4, addr4, x2, x3, x4, x5 + SWAP2 MSTORE_GENERAL + // stack: addr4, x3, x4, x5 + %add_const(1) + // stack: addr5, x3, x4, x5 + %swap_mstore // stack: x4, x5 %i9 // stack: y5, y4 PUSH $ptr + %create_bn254_pairing_address + DUP1 %add_const(1) - // stack: ind1, y5, y4 - %mstore_bn254_pairing - // stack: y4 - PUSH $ptr - // stack: ind0, y4 - %mstore_bn254_pairing + // stack: addr1, addr, y5, y4 + SWAP3 + MSTORE_GENERAL + // stack: y5, addr1 + MSTORE_GENERAL // stack: %endmacro @@ -575,10 +544,10 @@ MULFP254 SWAP3 // stack: c , f0, f1, c * f2, c * f3, c *f 4, c * f5 - SWAP2 - DUP3 + SWAP2 + DUP3 MULFP254 - SWAP2 + SWAP2 // stack: c , f0, c * f1, c * f2, c * f3, c * f4, c * f5 MULFP254 // stack: c * f0, c * f1, c * f2, c * f3, c * f4, c * f5 @@ -864,264 +833,268 @@ %macro load_fp254_12 // stack: ptr - DUP1 + %create_bn254_pairing_address + DUP1 %add_const(10) - // stack: ind10, ptr - %mload_bn254_pairing - // stack: x10, ptr - DUP2 + // stack: addr10, addr + MLOAD_GENERAL + // stack: x10, addr + DUP2 %add_const(9) - // stack: ind09, x10, ptr - %mload_bn254_pairing - // stack: x09, x10, ptr - DUP3 + // stack: addr09, x10, addr + MLOAD_GENERAL + // stack: x09, x10, addr + DUP3 %add_const(8) - // stack: ind08, x09, x10, ptr - %mload_bn254_pairing - // stack: x08, x09, x10, ptr - DUP4 + // stack: addr08, x09, x10, addr + MLOAD_GENERAL + // stack: x08, x09, x10, addr + DUP4 %add_const(7) - // stack: ind07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x07, x08, x09, x10, ptr - DUP5 + // stack: addr07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x07, x08, x09, x10, addr + DUP5 %add_const(6) - // stack: ind06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x06, x07, x08, x09, x10, ptr - DUP6 + // stack: addr06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x06, x07, x08, x09, x10, addr + DUP6 %add_const(5) - // stack: ind05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x05, x06, x07, x08, x09, x10, ptr - DUP7 + // stack: addr05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x05, x06, x07, x08, x09, x10, addr + DUP7 %add_const(4) - // stack: ind04, x05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x04, x05, x06, x07, x08, x09, x10, ptr - DUP8 + // stack: addr04, x05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x04, x05, x06, x07, x08, x09, x10, addr + DUP8 %add_const(3) - // stack: ind03, x04, x05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x03, x04, x05, x06, x07, x08, x09, x10, ptr - DUP9 + // stack: addr03, x04, x05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x03, x04, x05, x06, x07, x08, x09, x10, addr + DUP9 %add_const(2) - // stack: ind02, x03, x04, x05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x02, x03, x04, x05, x06, x07, x08, x09, x10, ptr - DUP10 + // stack: addr02, x03, x04, x05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x02, x03, x04, x05, x06, x07, x08, x09, x10, addr + DUP10 %add_const(1) - // stack: ind01, x02, x03, x04, x05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, ptr - DUP11 + // stack: addr01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr + DUP11 %add_const(11) - // stack: ind11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, ptr - %mload_bn254_pairing - // stack: x11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, ptr + // stack: addr11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr + MLOAD_GENERAL + // stack: x11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr SWAP11 - // stack: ind00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 - %mload_bn254_pairing + // stack: addr00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 + MLOAD_GENERAL // stack: x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 %endmacro %macro store_fp254_12 // stack: ptr, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 + %create_bn254_pairing_address SWAP11 - // stack: x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - DUP12 + // stack: x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + DUP12 %add_const(10) - // stack: ind10, x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 + // stack: addr10, x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 DUP11 - // stack: ind00, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - DUP10 + // stack: addr00, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + DUP10 %add_const(01) - // stack: ind01, x01, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 + // stack: addr01, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 DUP9 %add_const(02) - // stack: ind02, x02, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x03, x04, x05, x06, x07, x08, x09, ptr, x11 + // stack: addr02, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x03, x04, x05, x06, x07, x08, x09, addr, x11 DUP8 %add_const(03) - // stack: ind03, x03, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x04, x05, x06, x07, x08, x09, ptr, x11 + // stack: addr03, x03, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x04, x05, x06, x07, x08, x09, addr, x11 DUP7 %add_const(04) - // stack: ind04, x04, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x05, x06, x07, x08, x09, ptr, x11 + // stack: addr04, x04, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x05, x06, x07, x08, x09, addr, x11 DUP6 %add_const(05) - // stack: ind05, x05, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x06, x07, x08, x09, ptr, x11 + // stack: addr05, x05, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x06, x07, x08, x09, addr, x11 DUP5 %add_const(06) - // stack: ind06, x06, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x07, x08, x09, ptr, x11 + // stack: addr06, x06, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x07, x08, x09, addr, x11 DUP4 %add_const(07) - // stack: ind07, x07, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x08, x09, ptr, x11 + // stack: addr07, x07, x08, x09, addr, x11 + %swap_mstore + // stack: x08, x09, addr, x11 DUP3 %add_const(08) - // stack: ind08, x08, x09, ptr, x11 - %mstore_bn254_pairing - // stack: x09, ptr, x11 + // stack: addr08, x08, x09, addr, x11 + %swap_mstore + // stack: x09, addr, x11 DUP2 %add_const(09) - // stack: ind09, x09, ptr, x11 - %mstore_bn254_pairing - // stack: ptr, x11 + // stack: addr09, x09, addr, x11 + %swap_mstore + // stack: addr, x11 %add_const(11) - // stack: ind11, x11 - %mstore_bn254_pairing + // stack: addr11, x11 + %swap_mstore // stack: %endmacro /// moves fp254_12 from src..src+12 to dest..dest+12 -/// these should not overlap. leaves dest on stack +/// these should not overlap. leaves scaled DEST on stack %macro move_fp254_12 // stack: src, dest - DUP1 - // stack: ind00, src, dest - %mload_bn254_pairing - // stack: x00, src, dest + PUSH @SEGMENT_BN_PAIRING + GET_CONTEXT + %build_address_no_offset + DUP1 + // stack: base_addr, base_addr, src, dest + SWAP3 ADD + // stack: DEST, src, base_addr + SWAP2 ADD + // stack: SRC, DEST + DUP1 + // stack: addr00, SRC, DEST + MLOAD_GENERAL + // stack: x00, SRC, DEST DUP3 - // stack: ind00', x00, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr00', x00, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(1) - // stack: ind01, src, dest - %mload_bn254_pairing - // stack: x01, src, dest - DUP3 + // stack: addr01, SRC, DEST + MLOAD_GENERAL + // stack: x01, SRC, DEST + DUP3 %add_const(1) - // stack: ind01', x01, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr01', x01, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(2) - // stack: ind02, src, dest - %mload_bn254_pairing - // stack: x02, src, dest - DUP3 + // stack: addr02, SRC, DEST + MLOAD_GENERAL + // stack: x02, SRC, DEST + DUP3 %add_const(2) - // stack: ind02', x02, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr02', x02, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(3) - // stack: ind03, src, dest - %mload_bn254_pairing - // stack: x03, src, dest - DUP3 + // stack: addr03, SRC, DEST + MLOAD_GENERAL + // stack: x03, SRC, DEST + DUP3 %add_const(3) - // stack: ind03', x03, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr03', x03, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(4) - // stack: ind04, src, dest - %mload_bn254_pairing - // stack: x04, src, dest + // stack: addr04, SRC, DEST + MLOAD_GENERAL + // stack: x04, SRC, DEST DUP3 %add_const(4) - // stack: ind04', x04, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr04', x04, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(5) - // stack: ind05, src, dest - %mload_bn254_pairing - // stack: x05, src, dest - DUP3 + // stack: addr05, SRC, DEST + MLOAD_GENERAL + // stack: x05, SRC, DEST + DUP3 %add_const(5) - // stack: ind05', x05, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr05', x05, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(6) - // stack: ind06, src, dest - %mload_bn254_pairing - // stack: x06, src, dest - DUP3 + // stack: addr06, SRC, DEST + MLOAD_GENERAL + // stack: x06, SRC, DEST + DUP3 %add_const(6) - // stack: ind06', x06, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr06', x06, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(7) - // stack: ind07, src, dest - %mload_bn254_pairing - // stack: x07, src, dest - DUP3 + // stack: addr07, SRC, DEST + MLOAD_GENERAL + // stack: x07, SRC, DEST + DUP3 %add_const(7) - // stack: ind07', x07, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr07', x07, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(8) - // stack: ind08, src, dest - %mload_bn254_pairing - // stack: x08, src, dest - DUP3 + // stack: addr08, SRC, DEST + MLOAD_GENERAL + // stack: x08, SRC, DEST + DUP3 %add_const(8) - // stack: ind08', x08, src, dest - %mstore_bn254_pairing - // stack: src, dest + // stack: addr08', x08, SRC, DEST + %swap_mstore + // stack: SRC, DEST DUP1 %add_const(9) - // stack: ind09, src, dest - %mload_bn254_pairing - // stack: x09, src, dest - DUP3 + // stack: addr09, SRC, DEST + MLOAD_GENERAL + // stack: x09, SRC, DEST + DUP3 %add_const(9) - // stack: ind09', x09, src, dest - %mstore_bn254_pairing - // stack: src, dest - DUP1 + // stack: addr09', x09, SRC, DEST + %swap_mstore + // stack: SRC, DEST + DUP1 %add_const(10) - // stack: ind10, src, dest - %mload_bn254_pairing - // stack: x10, src, dest - DUP3 + // stack: addr10, SRC, DEST + MLOAD_GENERAL + // stack: x10, SRC, DEST + DUP3 %add_const(10) - // stack: ind10', x10, src, dest - %mstore_bn254_pairing - // stack: src, dest + // stack: addr10', x10, SRC, DEST + %swap_mstore + // stack: SRC, DEST %add_const(11) - // stack: ind11, dest - %mload_bn254_pairing - // stack: x11, dest - DUP2 + // stack: addr11, DEST + MLOAD_GENERAL + // stack: x11, DEST + DUP2 %add_const(11) - // stack: ind11', x11, dest - %mstore_bn254_pairing + // stack: addr11', x11, DEST + %swap_mstore %endmacro %macro assert_eq_unit_fp254_12 %assert_eq_const(1) - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero - %assert_zero + %rep 10 + OR + %endrep %assert_zero %endmacro From c0700065dbee12e5a9969a9b7ce56274a4660eca Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 24 Jan 2024 05:03:49 -0500 Subject: [PATCH 121/175] Improve `BIGNUM` operations (#1482) --- evm/src/cpu/kernel/asm/bignum/add.asm | 64 +++++----- evm/src/cpu/kernel/asm/bignum/addmul.asm | 110 +++++++++--------- evm/src/cpu/kernel/asm/bignum/cmp.asm | 85 +++++++------- evm/src/cpu/kernel/asm/bignum/modmul.asm | 99 +++++++++------- evm/src/cpu/kernel/asm/bignum/mul.asm | 65 ++++++----- evm/src/cpu/kernel/asm/bignum/shr.asm | 56 +++++---- evm/src/cpu/kernel/asm/bignum/util.asm | 4 +- .../cpu/kernel/asm/hash/blake2/addresses.asm | 6 +- .../cpu/kernel/asm/hash/sha2/compression.asm | 4 +- evm/src/cpu/kernel/asm/hash/sha2/main.asm | 4 +- .../kernel/asm/hash/sha2/message_schedule.asm | 8 +- .../cpu/kernel/asm/hash/sha2/write_length.asm | 4 +- evm/src/cpu/kernel/asm/memory/core.asm | 46 ++------ evm/src/cpu/kernel/asm/util/basic_macros.asm | 16 +++ evm/src/generation/prover_input.rs | 8 +- 15 files changed, 294 insertions(+), 285 deletions(-) diff --git a/evm/src/cpu/kernel/asm/bignum/add.asm b/evm/src/cpu/kernel/asm/bignum/add.asm index c9070dd107..4433ab2245 100644 --- a/evm/src/cpu/kernel/asm/bignum/add.asm +++ b/evm/src/cpu/kernel/asm/bignum/add.asm @@ -9,49 +9,55 @@ global add_bignum: ISZERO %jumpi(len_zero) // stack: len, a_start_loc, b_start_loc, retdest + %build_current_general_address_no_offset PUSH 0 - // stack: carry=0, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, retdest + // stack: carry=0, base_addr, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, retdest add_loop: - // stack: carry, i, a_cur_loc, b_cur_loc, retdest - DUP4 - %mload_current_general - // stack: b[cur], carry, i, a_cur_loc, b_cur_loc, retdest - DUP4 - %mload_current_general - // stack: a[cur], b[cur], carry, i, a_cur_loc, b_cur_loc, retdest + // stack: carry, base_addr, i, a_cur_loc, b_cur_loc, retdest + DUP2 + // stack: base_addr, carry, base_addr, i, a_cur_loc, b_cur_loc, retdest + DUP6 ADD // base_addr + b_cur_loc + MLOAD_GENERAL + // stack: b[cur], carry, base_addr, i, a_cur_loc, b_cur_loc, retdest + DUP3 + DUP6 ADD // base_addr + a_cur_loc + MLOAD_GENERAL + // stack: a[cur], b[cur], carry, base_addr, i, a_cur_loc, b_cur_loc, retdest ADD ADD - // stack: a[cur] + b[cur] + carry, i, a_cur_loc, b_cur_loc, retdest + // stack: a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest DUP1 - // stack: a[cur] + b[cur] + carry, a[cur] + b[cur] + carry, i, a_cur_loc, b_cur_loc, retdest + // stack: a[cur] + b[cur] + carry, a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest %shr_const(128) - // stack: (a[cur] + b[cur] + carry) // 2^128, a[cur] + b[cur] + carry, i, a_cur_loc, b_cur_loc, retdest + // stack: (a[cur] + b[cur] + carry) // 2^128, a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest SWAP1 - // stack: a[cur] + b[cur] + carry, (a[cur] + b[cur] + carry) // 2^128, i, a_cur_loc, b_cur_loc, retdest + // stack: a[cur] + b[cur] + carry, (a[cur] + b[cur] + carry) // 2^128, base_addr, i, a_cur_loc, b_cur_loc, retdest %mod_const(0x100000000000000000000000000000000) - // stack: c[cur] = (a[cur] + b[cur] + carry) % 2^128, carry_new = (a[cur] + b[cur] + carry) // 2^128, i, a_cur_loc, b_cur_loc, retdest - DUP4 - // stack: a_cur_loc, c[cur], carry_new, i, a_cur_loc, b_cur_loc, retdest - %mstore_current_general - // stack: carry_new, i, a_cur_loc, b_cur_loc, retdest - SWAP2 - %increment - SWAP2 - // stack: carry_new, i, a_cur_loc + 1, b_cur_loc, retdest + // stack: c[cur] = (a[cur] + b[cur] + carry) % 2^128, carry_new = (a[cur] + b[cur] + carry) // 2^128, base_addr, i, a_cur_loc, b_cur_loc, retdest + DUP3 + DUP6 + ADD // base_addr + a_cur_loc + // stack: a_cur_addr, c[cur], carry_new, base_addr, i, a_cur_loc, b_cur_loc, retdest + %swap_mstore + // stack: carry_new, base_addr, i, a_cur_loc, b_cur_loc, retdest SWAP3 %increment SWAP3 - // stack: carry_new, i, a_cur_loc + 1, b_cur_loc + 1, retdest - SWAP1 + // stack: carry_new, base_addr, i, a_cur_loc + 1, b_cur_loc, retdest + SWAP4 + %increment + SWAP4 + // stack: carry_new, base_addr, i, a_cur_loc + 1, b_cur_loc + 1, retdest + SWAP2 %decrement - SWAP1 - // stack: carry_new, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest - DUP2 - // stack: i - 1, carry_new, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest + SWAP2 + // stack: carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest + DUP3 + // stack: i - 1, carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest %jumpi(add_loop) add_end: - // stack: carry_new, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest - %stack (c, i, a, b) -> (c) + // stack: carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest + %stack (c, addr, i, a, b) -> (c) // stack: carry_new, retdest SWAP1 // stack: retdest, carry_new diff --git a/evm/src/cpu/kernel/asm/bignum/addmul.asm b/evm/src/cpu/kernel/asm/bignum/addmul.asm index 13e59e6d80..9cdf904e1f 100644 --- a/evm/src/cpu/kernel/asm/bignum/addmul.asm +++ b/evm/src/cpu/kernel/asm/bignum/addmul.asm @@ -8,95 +8,99 @@ global addmul_bignum: // stack: len, len, a_start_loc, b_start_loc, val, retdest ISZERO %jumpi(len_zero) + %build_current_general_address_no_offset PUSH 0 - // stack: carry_limb=0, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, val, retdest + // stack: carry_limb=0, base_addr, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, val, retdest addmul_loop: - // stack: carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - DUP4 - // stack: b_cur_loc, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - %mload_current_general - // stack: b[cur], carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - DUP6 - // stack: val, b[cur], carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + DUP2 + DUP6 ADD // base_addr + b_cur_loc + // stack: b_cur_addr, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + MLOAD_GENERAL + // stack: b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + DUP7 + // stack: val, b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest MUL - // stack: val * b[cur], carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP1 - // stack: val * b[cur], val * b[cur], carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: val * b[cur], val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest %shr_const(128) - // stack: (val * b[cur]) // 2^128, val * b[cur], carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: (val * b[cur]) // 2^128, val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP1 - // stack: val * b[cur], (val * b[cur]) // 2^128, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: val * b[cur], (val * b[cur]) // 2^128, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest %shl_const(128) %shr_const(128) - // stack: prod_lo = val * b[cur] % 2^128, prod_hi = (val * b[cur]) // 2^128, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - DUP5 - // stack: a_cur_loc, prod_lo, prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - %mload_current_general - // stack: a[cur], prod_lo, prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo = val * b[cur] % 2^128, prod_hi = (val * b[cur]) // 2^128, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + DUP4 + DUP7 ADD // base_addr + a_cur_loc + // stack: a_cur_addr, prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + MLOAD_GENERAL + // stack: a[cur], prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP1 - // stack: a[cur], a[cur], prod_lo, prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: a[cur], a[cur], prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP2 - // stack: prod_lo, a[cur], a[cur], prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo, a[cur], a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest ADD %shl_const(128) %shr_const(128) - // stack: prod_lo' = (prod_lo + a[cur]) % 2^128, a[cur], prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo' = (prod_lo + a[cur]) % 2^128, a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP1 - // stack: prod_lo', prod_lo', a[cur], prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo', prod_lo', a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP2 - // stack: a[cur], prod_lo', prod_lo', prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: a[cur], prod_lo', prod_lo', prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest GT - // stack: prod_lo_carry_limb = a[cur] > prod_lo', prod_lo', prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo_carry_limb = a[cur] > prod_lo', prod_lo', prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP1 - // stack: prod_lo', prod_lo_carry_limb, prod_hi, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo', prod_lo_carry_limb, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP2 - // stack: prod_hi, prod_lo_carry_limb, prod_lo', carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_hi, prod_lo_carry_limb, prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest ADD - // stack: prod_hi' = prod_hi + prod_lo_carry_limb, prod_lo', carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_hi' = prod_hi + prod_lo_carry_limb, prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP3 - // stack: carry_limb, prod_hi', prod_lo', carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: carry_limb, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP3 - // stack: prod_lo', carry_limb, prod_hi', prod_lo', carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo', carry_limb, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest ADD %shl_const(128) %shr_const(128) - // stack: to_write = (prod_lo' + carry_limb) % 2^128, prod_hi', prod_lo', carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: to_write = (prod_lo' + carry_limb) % 2^128, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP2 - // stack: prod_lo', prod_hi', to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: prod_lo', prod_hi', to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest DUP3 - // stack: to_write, prod_lo', prod_hi', to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest + // stack: to_write, prod_lo', prod_hi', to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest LT // stack: carry_limb_new = to_write < prod_lo', prod_hi', to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest %stack (vals: 3, c) -> (vals) - // stack: carry_limb_new, prod_hi', to_write, i, a_cur_loc, b_cur_loc, val, retdest + // stack: carry_limb_new, prod_hi', to_write, addr, i, a_cur_loc, b_cur_loc, val, retdest ADD - // stack: carry_limb = carry_limb_new' + prod_hi', to_write, i, a_cur_loc, b_cur_loc, val, retdest + // stack: carry_limb = carry_limb_new' + prod_hi', to_write, addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP1 - // stack: to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - DUP4 - // stack: a_cur_loc, to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - %mstore_current_general - // stack: carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - SWAP1 - // stack: i, carry_limb, a_cur_loc, b_cur_loc, val, retdest - %decrement - // stack: i-1, carry_limb, a_cur_loc, b_cur_loc, val, retdest + // stack: to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + DUP3 + DUP6 ADD // base_addr + a_cur_loc + // stack: a_cur_addr, to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest + %swap_mstore + // stack: carry_limb, base_addr, i, a_cur_loc, b_cur_loc, val, retdest SWAP2 - // stack: a_cur_loc, carry_limb, i-1, b_cur_loc, val, retdest - %increment - // stack: a_cur_loc+1, carry_limb, i-1, b_cur_loc, val, retdest + // stack: i, base_addr, carry_limb, a_cur_loc, b_cur_loc, val, retdest + %decrement + // stack: i-1, base_addr, carry_limb, a_cur_loc, b_cur_loc, val, retdest SWAP3 - // stack: b_cur_loc, carry_limb, i-1, a_cur_loc+1, val, retdest + // stack: a_cur_loc, base_addr, carry_limb, i-1, b_cur_loc, val, retdest %increment - // stack: b_cur_loc+1, carry_limb, i-1, a_cur_loc+1, val, retdest - %stack (b, c, i, a) -> (c, i, a, b) - // stack: carry_limb, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest - DUP2 - // stack: i-1, carry_limb, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest + // stack: a_cur_loc+1, base_addr, carry_limb, i-1, b_cur_loc, val, retdest + SWAP4 + // stack: b_cur_loc, base_addr, carry_limb, i-1, a_cur_loc+1, val, retdest + %increment + // stack: b_cur_loc+1, base_addr, carry_limb, i-1, a_cur_loc+1, val, retdest + %stack (b, addr, c, i, a) -> (c, addr, i, a, b) + // stack: carry_limb, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest + DUP3 + // stack: i-1, carry_limb, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest %jumpi(addmul_loop) addmul_end: - // stack: carry_limb_new, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest - %stack (c, i, a, b, v) -> (c) + // stack: carry_limb_new, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest + %stack (c, addr, i, a, b, v) -> (c) // stack: carry_limb_new, retdest SWAP1 // stack: retdest, carry_limb_new diff --git a/evm/src/cpu/kernel/asm/bignum/cmp.asm b/evm/src/cpu/kernel/asm/bignum/cmp.asm index cf6b547f4b..c27687542e 100644 --- a/evm/src/cpu/kernel/asm/bignum/cmp.asm +++ b/evm/src/cpu/kernel/asm/bignum/cmp.asm @@ -5,82 +5,87 @@ // Returns 1 if a > b, 0 if a == b, and -1 (that is, 2^256 - 1) if a < b. global cmp_bignum: // stack: len, a_start_loc, b_start_loc, retdest - DUP1 - // stack: len, len, a_start_loc, b_start_loc, retdest + %build_current_general_address_no_offset + // stack: base_addr, len, a_start_loc, b_start_loc, retdest + DUP2 + // stack: len, base_addr, len, a_start_loc, b_start_loc, retdest ISZERO - %jumpi(equal) - // stack: len, a_start_loc, b_start_loc, retdest - SWAP1 - // stack: a_start_loc, len, b_start_loc, retdest + %jumpi(equal) // len and base_addr are swapped, but they will be popped anyway + // stack: base_addr, len, a_start_loc, b_start_loc, retdest + SWAP2 + // stack: a_start_loc, len, base_addr, b_start_loc, retdest PUSH 1 DUP3 SUB - // stack: len-1, a_start_loc, len, b_start_loc, retdest + // stack: len-1, a_start_loc, len, base_addr, b_start_loc, retdest ADD - // stack: a_end_loc, len, b_start_loc, retdest - SWAP2 - // stack: b_start_loc, len, a_end_loc, retdest + // stack: a_end_loc, len, base_addr, b_start_loc, retdest + SWAP3 + // stack: b_start_loc, len, base_addr, a_end_loc, retdest PUSH 1 DUP3 SUB - // stack: len-1, b_start_loc, len, a_end_loc, retdest + // stack: len-1, b_start_loc, len, base_addr, a_end_loc, retdest ADD - // stack: b_end_loc, len, a_end_loc, retdest - %stack (b, l, a) -> (l, a, b) - // stack: len, a_end_loc, b_end_loc, retdest + // stack: b_end_loc, len, base_addr, a_end_loc, retdest + + %stack (b, l, addr, a) -> (l, addr, a, b) + // stack: len, base_addr, a_end_loc, b_end_loc, retdest %decrement ge_loop: - // stack: i, a_i_loc, b_i_loc, retdest - DUP3 - DUP3 - // stack: a_i_loc, b_i_loc, i, a_i_loc, b_i_loc, retdest - %mload_current_general - SWAP1 - %mload_current_general - SWAP1 - // stack: a[i], b[i], i, a_i_loc, b_i_loc, retdest + // stack: i, base_addr, a_i_loc, b_i_loc, retdest + DUP4 + // stack: b_i_loc, i, base_addr, a_i_loc, b_i_loc, retdest + DUP3 ADD // b_i_addr + MLOAD_GENERAL + // stack: b[i], i, base_addr, a_i_loc, b_i_loc, retdest + DUP4 + // stack: a_i_loc, b[i], i, base_addr, a_i_loc, b_i_loc, retdest + DUP4 ADD // a_i_addr + MLOAD_GENERAL + // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest %stack (vals: 2) -> (vals, vals) GT %jumpi(greater) - // stack: a[i], b[i], i, a_i_loc, b_i_loc, retdest + // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest LT %jumpi(less) - // stack: i, a_i_loc, b_i_loc, retdest + // stack: i, base_addr, a_i_loc, b_i_loc, retdest DUP1 ISZERO %jumpi(equal) %decrement - // stack: i-1, a_i_loc, b_i_loc, retdest - SWAP1 - // stack: a_i_loc, i-1, b_i_loc, retdest - %decrement - // stack: a_i_loc_new, i-1, b_i_loc, retdest + // stack: i-1, base_addr, a_i_loc, b_i_loc, retdest SWAP2 - // stack: b_i_loc, i-1, a_i_loc_new, retdest + // stack: a_i_loc, base_addr, i-1, b_i_loc, retdest + %decrement + // stack: a_i_loc_new, base_addr, i-1, b_i_loc, retdest + SWAP3 + // stack: b_i_loc, base_addr, i-1, a_i_loc_new, retdest %decrement - // stack: b_i_loc_new, i-1, a_i_loc_new, retdest - %stack (b, i, a) -> (i, a, b) - // stack: i-1, a_i_loc_new, b_i_loc_new, retdest + // stack: b_i_loc_new, base_addr, i-1, a_i_loc_new, retdest + %stack (b, addr, i, a) -> (i, addr, a, b) + // stack: i-1, base_addr, a_i_loc_new, b_i_loc_new, retdest %jump(ge_loop) equal: - // stack: i, a_i_loc, b_i_loc, retdest - %pop3 + // stack: i, base_addr, a_i_loc, b_i_loc, retdest + %pop4 // stack: retdest PUSH 0 // stack: 0, retdest SWAP1 JUMP greater: - // stack: a[i], b[i], i, a_i_loc, b_i_loc, retdest - %pop5 + // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest + %pop6 // stack: retdest PUSH 1 // stack: 1, retdest SWAP1 JUMP less: - // stack: i, a_i_loc, b_i_loc, retdest - %pop3 + // stack: i, base_addr, a_i_loc, b_i_loc, retdest + %pop4 // stack: retdest PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // stack: -1, retdest diff --git a/evm/src/cpu/kernel/asm/bignum/modmul.asm b/evm/src/cpu/kernel/asm/bignum/modmul.asm index 8b19d3e102..9735f6108d 100644 --- a/evm/src/cpu/kernel/asm/bignum/modmul.asm +++ b/evm/src/cpu/kernel/asm/bignum/modmul.asm @@ -21,28 +21,32 @@ global modmul_bignum: // STEP 1: // The prover provides x := (a * b) % m, which we store in output_loc. + %build_current_general_address_no_offset + PUSH 0 - // stack: i=0, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i=0, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest modmul_remainder_loop: - // stack: i, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest PROVER_INPUT(bignum_modmul) - // stack: PI, i, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP7 + // stack: PI, i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + DUP8 DUP3 ADD - // stack: out_loc[i], PI, i, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %mstore_current_general - // stack: i, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: out_loc[i], PI, i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + DUP4 ADD // out_addr_i + %swap_mstore + // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest %increment + DUP3 DUP2 - DUP2 - // stack: i+1, len, i+1, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i+1, len, i+1, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest SUB // functions as NEQ - // stack: i+1!=len, i+1, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i+1!=len, i+1, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest %jumpi(modmul_remainder_loop) // end of modmul_remainder_loop - // stack: i, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - POP + // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + %pop2 + // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest @@ -69,28 +73,32 @@ modmul_return_1: // stack: len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest %mul_const(2) // stack: 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + + %build_current_general_address_no_offset + PUSH 0 - // stack: i=0, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i=0, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest modmul_quotient_loop: - // stack: i, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest PROVER_INPUT(bignum_modmul) - // stack: PI, i, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP9 + // stack: PI, i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + DUP10 DUP3 ADD - // stack: s1[i], PI, i, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %mstore_current_general - // stack: i, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: s1[i], PI, i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + DUP4 ADD // s1_addr_i + %swap_mstore + // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest %increment + DUP3 DUP2 - DUP2 - // stack: i+1, 2*len, i+1, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i+1, 2*len, i+1, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest SUB // functions as NEQ - // stack: i+1!=2*len, i+1, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + // stack: i+1!=2*len, i+1, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest %jumpi(modmul_quotient_loop) // end of modmul_quotient_loop - // stack: i, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %pop2 + // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest + %pop3 // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest // STEP 4: @@ -130,33 +138,36 @@ modmul_return_4: // STEP 6: // Check that x + k * m = a * b. - // Walk through scratch_2 and scratch_3, checking that they are equal. - // stack: n=len, i=s2, j=s3, retdest + %build_current_general_address_no_offset + // stack: base_addr, n=len, i=s2, j=s3, retdest modmul_check_loop: - // stack: n, i, j, retdest - %stack (l, idx: 2) -> (idx, l, idx) - // stack: i, j, n, i, j, retdest - %mload_current_general - SWAP1 - %mload_current_general - SWAP1 - // stack: mem[i], mem[j], n, i, j, retdest + // stack: base_addr, n, i, j, retdest + %stack (addr, l, i, j) -> (j, i, addr, addr, l, i, j) + // stack: j, i, base_addr, base_addr, n, i, j, retdest + DUP3 ADD // addr_j + MLOAD_GENERAL + // stack: mem[j], i, base_addr, base_addr, n, i, j, retdest + SWAP2 + ADD // addr_i + MLOAD_GENERAL + // stack: mem[i], mem[j], base_addr, n, i, j, retdest %assert_eq - // stack: n, i, j, retdest - %decrement + // stack: base_addr, n, i, j, retdest SWAP1 - %increment + %decrement + // stack: n-1, base_addr, i, j, retdest SWAP2 %increment - SWAP2 - SWAP1 - // stack: n-1, i+1, j+1, retdest - DUP1 - // stack: n-1, n-1, i+1, j+1, retdest + // stack: i+1, base_addr, n-1, j, retdest + SWAP3 + %increment + // stack: j+1, base_addr, n-1, i+1, retdest + %stack (j, addr, n, i) -> (n, addr, n, i, j) + // stack: n-1, base_addr, n-1, i+1, j+1, retdest %jumpi(modmul_check_loop) // end of modmul_check_loop - // stack: n-1, i+1, j+1, retdest - %pop3 + // stack: base_addr, n-1, i+1, j+1, retdest + %pop4 // stack: retdest JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/mul.asm b/evm/src/cpu/kernel/asm/bignum/mul.asm index ddb3346d6a..b3269f73a9 100644 --- a/evm/src/cpu/kernel/asm/bignum/mul.asm +++ b/evm/src/cpu/kernel/asm/bignum/mul.asm @@ -12,49 +12,54 @@ global mul_bignum: // stack: len, len, a_start_loc, b_start_loc, output_loc, retdest ISZERO %jumpi(len_zero) - DUP1 - // stack: n=len, len, a_start_loc, bi=b_start_loc, output_cur=output_loc, retdest + + %build_current_general_address_no_offset + + DUP2 + // stack: n=len, base_addr, len, a_start_loc, bi=b_start_loc, output_cur=output_loc, retdest mul_loop: - // stack: n, len, a_start_loc, bi, output_cur, retdest + // stack: n, base_addr, len, a_start_loc, bi, output_cur, retdest PUSH mul_addmul_return - // stack: mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest - DUP5 - // stack: bi, mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest - %mload_current_general - // stack: b[i], mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest, b - DUP5 - // stack: a_start_loc, b[i], mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest, b - DUP8 - // stack: output_loc, a_start_loc, b[i], mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest, b + // stack: mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP6 + // stack: bi, mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP4 ADD // bi_addr + MLOAD_GENERAL + // stack: b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest DUP6 - // stack: len, output_loc, a_start_loc, b[i], mul_addmul_return, n, len, a_start_loc, bi, output_cur, retdest, b + // stack: a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP9 + // stack: output_loc, a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP7 + // stack: len, output_loc, a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest %jump(addmul_bignum) mul_addmul_return: - // stack: carry_limb, n, len, a_start_loc, bi, output_cur, retdest - DUP6 - // stack: output_cur, carry_limb, n, len, a_start_loc, bi, output_cur, retdest - DUP4 - // stack: len, output_cur, carry_limb, n, len, a_start_loc, bi, output_cur, retdest + // stack: carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP7 + // stack: output_cur, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP5 + // stack: len, output_cur, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest ADD - // stack: output_cur + len, carry_limb, n, len, a_start_loc, bi, output_cur, retdest - %mstore_current_general - // stack: n, len, a_start_loc, bi, output_cur, retdest + // stack: output_cur + len, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest + DUP4 ADD + %swap_mstore + // stack: n, base_addr, len, a_start_loc, bi, output_cur, retdest %decrement - // stack: n-1, len, a_start_loc, bi, output_cur, retdest - SWAP3 - %increment - SWAP3 - // stack: n-1, len, a_start_loc, bi+1, output_cur, retdest + // stack: n-1, base_addr, len, a_start_loc, bi, output_cur, retdest SWAP4 %increment SWAP4 - // stack: n-1, len, a_start_loc, bi+1, output_cur+1, retdest + // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur, retdest + SWAP5 + %increment + SWAP5 + // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest DUP1 - // stack: n-1, n-1, len, a_start_loc, bi+1, output_cur+1, retdest + // stack: n-1, n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest %jumpi(mul_loop) mul_end: - // stack: n-1, len, a_start_loc, bi+1, output_cur+1, retdest - %pop5 + // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest + %pop6 // stack: retdest JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/shr.asm b/evm/src/cpu/kernel/asm/bignum/shr.asm index 16d8403c77..88d08f05f2 100644 --- a/evm/src/cpu/kernel/asm/bignum/shr.asm +++ b/evm/src/cpu/kernel/asm/bignum/shr.asm @@ -16,48 +16,54 @@ global shr_bignum: // stack: start_loc + len, start_loc, retdest %decrement // stack: end_loc, start_loc, retdest - %stack (e) -> (e, 0) - // stack: i=end_loc, carry=0, start_loc, retdest + + %build_current_general_address_no_offset + + // stack: base_addr, end_loc, start_loc, retdest + %stack (addr, e) -> (e, addr, 0) + // stack: i=end_loc, base_addr, carry=0, start_loc, retdest shr_loop: - // stack: i, carry, start_loc, retdest + // stack: i, base_addr, carry, start_loc, retdest DUP1 - // stack: i, i, carry, start_loc, retdest - %mload_current_general - // stack: a[i], i, carry, start_loc, retdest + // stack: i, i, base_addr, carry, start_loc, retdest + DUP3 ADD // addr_i + MLOAD_GENERAL + // stack: a[i], i, base_addr, carry, start_loc, retdest DUP1 - // stack: a[i], a[i], i, carry, start_loc, retdest + // stack: a[i], a[i], i, base_addr, carry, start_loc, retdest %shr_const(1) - // stack: a[i] >> 1, a[i], i, carry, start_loc, retdest + // stack: a[i] >> 1, a[i], i, base_addr, carry, start_loc, retdest SWAP1 - // stack: a[i], a[i] >> 1, i, carry, start_loc, retdest + // stack: a[i], a[i] >> 1, i, base_addr, carry, start_loc, retdest %mod_const(2) - // stack: new_carry = a[i] % 2, a[i] >> 1, i, carry, start_loc, retdest - SWAP3 - // stack: carry, a[i] >> 1, i, new_carry, start_loc, retdest + // stack: new_carry = a[i] % 2, a[i] >> 1, i, base_addr, carry, start_loc, retdest + SWAP4 + // stack: carry, a[i] >> 1, i, base_addr, new_carry, start_loc, retdest %shl_const(127) - // stack: carry << 127, a[i] >> 1, i, new_carry, start_loc, retdest + // stack: carry << 127, a[i] >> 1, i, base_addr, new_carry, start_loc, retdest ADD - // stack: carry << 127 | a[i] >> 1, i, new_carry, start_loc, retdest + // stack: carry << 127 | a[i] >> 1, i, base_addr, new_carry, start_loc, retdest DUP2 - // stack: i, carry << 127 | a[i] >> 1, i, new_carry, start_loc, retdest - %mstore_current_general - // stack: i, new_carry, start_loc, retdest + // stack: i, carry << 127 | a[i] >> 1, i, base_addr, new_carry, start_loc, retdest + DUP4 ADD // addr_i + %swap_mstore + // stack: i, base_addr, new_carry, start_loc, retdest PUSH 1 DUP2 SUB - // stack: i-1, i, new_carry, start_loc, retdest + // stack: i-1, i, base_addr, new_carry, start_loc, retdest SWAP1 - // stack: i, i-1, new_carry, start_loc, retdest - DUP4 - // stack: start_loc, i, i-1, new_carry, start_loc, retdest + // stack: i, i-1, base_addr, new_carry, start_loc, retdest + DUP5 + // stack: start_loc, i, i-1, base_addr, new_carry, start_loc, retdest EQ - // stack: i == start_loc, i-1, new_carry, start_loc, retdest + // stack: i == start_loc, i-1, base_addr, new_carry, start_loc, retdest ISZERO - // stack: i != start_loc, i-1, new_carry, start_loc, retdest + // stack: i != start_loc, i-1, base_addr, new_carry, start_loc, retdest %jumpi(shr_loop) shr_end: - // stack: i, new_carry, start_loc, retdest - %pop3 + // stack: i, base_addr, new_carry, start_loc, retdest + %pop4 // stack: retdest JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/util.asm b/evm/src/cpu/kernel/asm/bignum/util.asm index 0385deec2d..f0a1563450 100644 --- a/evm/src/cpu/kernel/asm/bignum/util.asm +++ b/evm/src/cpu/kernel/asm/bignum/util.asm @@ -1,7 +1,7 @@ %macro memcpy_current_general // stack: dst, src, len // DST and SRC are offsets, for the same memory segment - GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address_no_offset + %build_current_general_address_no_offset %stack (addr_no_offset, dst, src, len) -> (addr_no_offset, src, addr_no_offset, dst, len, %%after) ADD // stack: SRC, addr_no_offset, dst, len, %%after @@ -14,7 +14,7 @@ %macro clear_current_general // stack: dst, len - GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address + %build_current_general_address %stack (DST, len) -> (DST, len, %%after) %jump(memset) %%after: diff --git a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm b/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm index 5beb1dee64..3244cfa1f2 100644 --- a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm +++ b/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm @@ -2,11 +2,7 @@ // It is ready to be used, i.e. already containing the current context // and SEGMENT_KERNEL_GENERAL. %macro blake2_hash_value_addr - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment - GET_CONTEXT - // stack: context, segment - %build_address_no_offset + %build_current_general_address_no_offset DUP1 MLOAD_GENERAL // stack: num_blocks, addr diff --git a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm b/evm/src/cpu/kernel/asm/hash/sha2/compression.asm index f25ff30229..a9467a00bc 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/compression.asm @@ -4,9 +4,7 @@ // stack: num_blocks %mul_const(320) %add_const(2) - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address + %build_current_general_address %endmacro global sha2_compression: diff --git a/evm/src/cpu/kernel/asm/hash/sha2/main.asm b/evm/src/cpu/kernel/asm/hash/sha2/main.asm index 039379f39a..53967f8a17 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/main.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/main.asm @@ -1,8 +1,6 @@ global sha2: // stack: virt, num_bytes, retdest - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address + %build_current_general_address // stack: addr, num_bytes, retdest DUP1 SWAP2 // stack: num_bytes, addr, addr, retdest diff --git a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm b/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm index b789a7fbb8..66fa67a9b7 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm @@ -3,9 +3,7 @@ // stack: num_blocks %mul_const(64) %add_const(2) - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address + %build_current_general_address %endmacro // Precondition: stack contains address of one message block, followed by output address @@ -188,9 +186,7 @@ global sha2_gen_all_message_schedules: // stack: num_blocks, output_addr, output_addr, retdest PUSH 1 // stack: cur_offset = 1, counter = num_blocks, output_addr, output_addr, retdest - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address + %build_current_general_address // stack: cur_addr, counter, output_addr, output_addr, retdest gen_all_message_schedules_loop: // stack: cur_addr, counter, cur_output_addr, output_addr, retdest diff --git a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm index c9a0642fa2..9c2707b8d1 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm @@ -1,8 +1,6 @@ %macro sha2_write_length // stack: last_addr_offset, length - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address + %build_current_general_address SWAP1 // stack: length, last_addr DUP1 diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index dad5979f22..070e474f6e 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -127,9 +127,7 @@ // Load a single value from the kernel general memory, in the current context (not the kernel's context). %macro mload_current_general_no_offset // stack: - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address_no_offset + %build_current_general_address_no_offset MLOAD_GENERAL // stack: value %endmacro @@ -137,11 +135,7 @@ // Load a big-endian u32 from kernel general memory in the current context. %macro mload_current_general_u32 // stack: offset - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset - GET_CONTEXT - // stack: context, segment, offset - %build_address + %build_current_general_address %mload_u32 // stack: value %endmacro @@ -149,11 +143,7 @@ // Load a little-endian u32 from kernel general memory in the current context. %macro mload_current_general_u32_LE // stack: offset - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset - GET_CONTEXT - // stack: context, segment, offset - %build_address + %build_current_general_address %mload_u32_LE // stack: value %endmacro @@ -161,11 +151,7 @@ // Load a little-endian u64 from kernel general memory in the current context. %macro mload_current_general_u64_LE // stack: offset - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset - GET_CONTEXT - // stack: context, segment, offset - %build_address + %build_current_general_address %mload_u64_LE // stack: value %endmacro @@ -173,11 +159,7 @@ // Load a u256 from kernel general memory in the current context. %macro mload_current_general_u256 // stack: offset - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset - GET_CONTEXT - // stack: context, segment, offset - %build_address + %build_current_general_address %mload_u256 // stack: value %endmacro @@ -185,11 +167,7 @@ // Store a single value to kernel general memory in the current context. %macro mstore_current_general // stack: offset, value - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset, value - GET_CONTEXT - // stack: context, segment, offset, value - %build_address + %build_current_general_address SWAP1 MSTORE_GENERAL // stack: (empty) @@ -198,11 +176,7 @@ // Store a single value to kernel general memory in the current context. %macro mstore_current_general_no_offset // stack: value - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, value - GET_CONTEXT - // stack: context, segment, value - %build_address_no_offset + %build_current_general_address_no_offset SWAP1 MSTORE_GENERAL // stack: (empty) @@ -219,11 +193,7 @@ // Store a big-endian u32 to kernel general memory in the current context. %macro mstore_current_general_u32 // stack: offset, value - PUSH @SEGMENT_KERNEL_GENERAL - // stack: segment, offset, value - GET_CONTEXT - // stack: context, segment, offset, value - %build_address + %build_current_general_address %mstore_u32 // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index 8753e1cafb..78fd34fc1c 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -440,6 +440,22 @@ // stack: addr %endmacro +%macro build_current_general_address + // stack: offset + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address + // stack: addr +%endmacro + +%macro build_current_general_address_no_offset + // stack: + PUSH @SEGMENT_KERNEL_GENERAL + GET_CONTEXT + %build_address_no_offset + // stack: addr (offset == 0) +%endmacro + %macro build_kernel_address // stack: seg, off ADD diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index f3078239d8..a6d045d6d6 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -169,10 +169,10 @@ impl GenerationState { // Subsequent calls return one limb at a time, in order (first remainder and then quotient). fn run_bignum_modmul(&mut self) -> Result { if self.bignum_modmul_result_limbs.is_empty() { - let len = stack_peek(self, 1).map(u256_to_usize)??; - let a_start_loc = stack_peek(self, 2).map(u256_to_usize)??; - let b_start_loc = stack_peek(self, 3).map(u256_to_usize)??; - let m_start_loc = stack_peek(self, 4).map(u256_to_usize)??; + let len = stack_peek(self, 2).map(u256_to_usize)??; + let a_start_loc = stack_peek(self, 3).map(u256_to_usize)??; + let b_start_loc = stack_peek(self, 4).map(u256_to_usize)??; + let m_start_loc = stack_peek(self, 5).map(u256_to_usize)??; let (remainder, quotient) = self.bignum_modmul(len, a_start_loc, b_start_loc, m_start_loc); From ca2e56e23bd91859f387ae00c8647ca735efeb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Wed, 24 Jan 2024 17:16:41 +0100 Subject: [PATCH 122/175] Fix bugs in jumpdest analysis (#1474) * Fix simulation for jumpdest analysis * Fix bugs in jumpdest analysis * Update evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> * Address reviews --------- Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 126 +++++++++--------- evm/src/cpu/kernel/interpreter.rs | 24 ++-- .../kernel/tests/core/jumpdest_analysis.rs | 78 ++++++++++- evm/src/generation/mod.rs | 2 +- evm/src/generation/prover_input.rs | 67 ++++++---- evm/src/generation/state.rs | 6 +- 6 files changed, 198 insertions(+), 105 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index 6ed4df814f..934d1f6297 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -164,12 +164,10 @@ global write_table_if_jumpdest: // stack: (is_1_at_1 or is_0_at_2_or_3|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest // stack: (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // Compute in_range = - // - (0xFF|X⁷)³² for the first 15 bytes - // - (has_prefix => is_0_at_4 |X⁷)³² for the next 15 bytes - // - (~has_prefix|X⁷)³² for the last byte - // Compute also ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. We don't need to update ~has_prefix - // for the second half but it takes less cycles if we do it. + // Compute in_range and has_prefix' = + // - in_range = (0xFF|X⁷)³² and ~has_prefix' = ~has_prefix OR is_0_at_4, for the first 15 bytes + // - in_range = (has_prefix => is_0_at_4 |X⁷)³² and ~has_prefix' = ~has_prefix, for the next 15 bytes + // - in_range = (~has_prefix|X⁷)³² and ~has_prefix' = ~has_prefix, for the last byte. DUP2 %shl_const(3) NOT // stack: (is_0_at_4|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest @@ -177,93 +175,97 @@ global write_table_if_jumpdest: PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 AND // stack: (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP2 - DUP2 - OR + DUP1 // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000 + AND + // stack: (is_0_at_4|X⁷)¹⁵|(0⁸)¹⁷, (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP3 OR - // stack: (in_range|X⁷)³², (is_0_at_4|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // (~has_prefix'|X⁷)³², (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest SWAP2 OR - // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000 + OR + // stack: (in_range|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in positions 1-7 and 16-23 - // - (has_prefix => is_0_at_5 |X⁷)³² on the rest - // Compute also ~has_prefix = ~has_prefix OR is_0_at_5 for all bytes. + // Compute in_range' and ~has_prefix as + // - in_range' = in_range and has_prefix' = ~has_prefix OR is_0_at_5, for bytes in positions 1-7 and 16-23 + // - in_range' = in_range AND (has_prefix => is_0_at_5 |X⁷)³² and has_prefix' = ~has_prefix, for the rest. DUP3 %shl_const(4) NOT - // stack: (is_0_at_5|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP2 - DUP2 - OR + // stack: (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP1 // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0xFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000 + AND + // stack: (is_0_at_5|X⁷)⁷|(0⁸)⁸|(is_0_at_5|X⁷)⁸|(0⁸)⁸, (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP4 OR - // stack: (in_range'|X⁷)³², (is_0_at_5|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 + // stack: (~has_prefix'|X⁷)³², (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP3 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000 OR - // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 AND - SWAP1 - - // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in positions 1-3, 8-11, 16-19, and 24-27 - // - (has_prefix => is_0_at_6 |X⁷)³² on the rest - // Compute also that ~has_prefix = ~has_prefix OR is_0_at_4 for all bytes. + // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // Compute in_range' and ~has_prefix' as + // - in_range' = in_range and ~has_prefix' = ~has_prefix OR is_0_at_6, for bytes in positions 1-3, 8-11, 16-19, and 24-27 + // - in_range' = in_range AND (has_prefix => is_0_at_6 |X⁷)³² and ~has_prefix' = has_prefix, for the rest. DUP3 %shl_const(5) NOT - // stack: (is_0_at_6|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP2 - DUP2 - OR + // stack: (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP1 // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0xFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF0000000000 + AND + // stack: (is_0_at_6|X⁷)³|(0⁸)⁴|((is_0_at_6|X⁷)⁴|(0⁸)⁴)³, (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP4 OR - // stack: (in_range'|X⁷)³², (is_0_at_6|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 + // stack: (~has_prefix'|X⁷)³², (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP3 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF0000000000 OR - // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 AND - SWAP1 - - // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in 1, 4-5, 8-9, 12-13, 16-17, 20-21, 24-25, 28-29 - // - (has_prefix => is_0_at_7 |X⁷)³² on the rest - // Compute also that ~has_prefix = ~has_prefix OR is_0_at_7 for all bytes. + // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // Compute in_range' and ~has_prefix' as + // - in_range' = in_range and ~has_prefix' = has_prefix OR is_0_at_7, for bytes in 1, 4-5, 8-9, 12-13, 16-17, 20-21, 24-25, 28-29 + // - in_range' = in_range AND (has_prefix => is_0_at_7 |X⁷)³² and ~has_prefix' = ~has_prefix, for the rest. DUP3 %shl_const(6) NOT - // stack: (is_0_at_7|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP2 - DUP2 - OR + // stack: (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP1 // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0xFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF000000 + AND + // stack: is_0_at_7|X⁷|(0⁸)²|((is_0_at_7|X⁷)²|(0⁸)²)⁷, (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + DUP4 OR - // stack: (in_range'|X⁷)³², (is_0_at_7|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 + // (~has_prefix'|X⁷)³², (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + SWAP3 + OR + // pos 0102030405060708091011121314151617181920212223242526272829303132 + PUSH 0xFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF000000 OR - // stack: (~has_prefix|X⁷)³², (in_range'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 AND - SWAP1 + // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // Compute in_range' = in_range AND - // - (0xFF|X⁷)³² for bytes in odd positions - // - (has_prefix => is_0_at_8 |X⁷)³² on the rest + // Compute in_range' as + // - in_range' = in_range, for odd positions + // - in_range' = in_range AND (has_prefix => is_0_at_8 |X⁷)³², for the rest + SWAP1 // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest DUP3 %shl_const(7) NOT - // stack: (is_0_at_8|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest + // stack: (is_0_at_8|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest OR // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF @@ -275,14 +277,18 @@ global write_table_if_jumpdest: // pos 0102030405060708091011121314151617181920212223242526272829303132 PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 AND - %jump_neq_const(0x8080808080808080808080808080808080808080808080808080808080808080, return) + %jump_neq_const(0x8080808080808080808080808080808080808080808080808080808080808080, return_pop_opcode) POP %add_const(32) // check the remaining path %jump(verify_path_and_write_jumpdest_table) +return_pop_opcode: + POP return: - // stack: proof_prefix_addr, jumpdest, ctx, retdest + // stack: proof_prefix_addr, ctx, jumpdest, retdest + // or + // stack: jumpdest, ctx, proof_prefix_addr, retdest %pop3 JUMP diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 48eedb8417..be66d2e90e 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -566,6 +566,14 @@ impl<'a> Interpreter<'a> { .contexts .push(MemoryContextState::default()); } + self.generation_state.memory.set( + MemoryAddress::new( + context, + Segment::ContextMetadata, + ContextMetadata::CodeSize.unscale(), + ), + code.len().into(), + ); self.generation_state.memory.contexts[context].segments[Segment::Code.unscale()].content = code.into_iter().map(U256::from).collect(); } @@ -584,20 +592,8 @@ impl<'a> Interpreter<'a> { .collect() } - pub(crate) fn set_jumpdest_bits(&mut self, context: usize, jumpdest_bits: Vec) { - self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] - .content = jumpdest_bits.iter().map(|&x| u256_from_bool(x)).collect(); - self.generation_state - .set_proofs_and_jumpdests(HashMap::from([( - context, - BTreeSet::from_iter( - jumpdest_bits - .into_iter() - .enumerate() - .filter(|&(_, x)| x) - .map(|(i, _)| i), - ), - )])); + pub(crate) fn set_jumpdest_analysis_inputs(&mut self, jumps: HashMap>) { + self.generation_state.set_jumpdest_analysis_inputs(jumps); } pub(crate) fn incr(&mut self, n: usize) { diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 176014915c..d704cc198d 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -1,3 +1,5 @@ +use std::collections::{BTreeSet, HashMap}; + use anyhow::Result; use ethereum_types::U256; @@ -37,10 +39,84 @@ fn test_jumpdest_analysis() -> Result<()> { ]; let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); interpreter.set_code(CONTEXT, code); - interpreter.set_jumpdest_bits(CONTEXT, jumpdest_bits); + interpreter.set_jumpdest_analysis_inputs(HashMap::from([( + 3, + BTreeSet::from_iter( + jumpdest_bits + .iter() + .enumerate() + .filter(|&(_, &x)| x) + .map(|(i, _)| i), + ), + )])); + + assert_eq!( + interpreter.generation_state.jumpdest_table, + // Context 3 has jumpdest 1, 5, 7. All have proof 0 and hence + // the list [proof_0, jumpdest_0, ... ] is [0, 1, 0, 5, 0, 7] + Some(HashMap::from([(3, vec![0, 1, 0, 5, 0, 7])])) + ); interpreter.run()?; assert_eq!(interpreter.stack(), vec![]); + assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(3)); + + Ok(()) +} + +#[test] +fn test_packed_verification() -> Result<()> { + let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; + const CONTEXT: usize = 3; // arbitrary + + let add = get_opcode("ADD"); + let jumpdest = get_opcode("JUMPDEST"); + + // The last push(i=0) is 0x5f which is not a valid opcode. However, this + // is still meaningful for the test and makes things easier + let mut code: Vec = std::iter::once(add) + .chain( + (0..=31) + .rev() + .map(get_push_opcode) + .chain(std::iter::once(jumpdest)), + ) + .collect(); + + let jumpdest_bits: Vec = std::iter::repeat(false) + .take(33) + .chain(std::iter::once(true)) + .collect(); + + // Contract creation transaction. + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + code.len().into(), + U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, + ]; + let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack.clone()); + interpreter.set_code(CONTEXT, code.clone()); + interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); + + interpreter.run()?; + + assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(CONTEXT)); + + // If we add 1 to each opcode the jumpdest at position 32 is never a valid jumpdest + for i in 1..=32 { + code[i] += 1; + let mut interpreter = + Interpreter::new_with_kernel(jumpdest_analysis, initial_stack.clone()); + interpreter.set_code(CONTEXT, code.clone()); + interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); + + interpreter.run()?; + + assert!(interpreter.get_jumpdest_bits(CONTEXT).is_empty()); + + code[i] -= 1; + } + Ok(()) } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index c8f2dac10d..eeaafa800b 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -348,7 +348,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( final_label: &str, state: &mut GenerationState, ) -> Option>> { - if state.jumpdest_proofs.is_some() { + if state.jumpdest_table.is_some() { None } else { const JUMP_OPCODE: u8 = 0x56; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index a6d045d6d6..0214b84708 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,5 +1,5 @@ use std::cmp::min; -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use std::mem::transmute; use std::str::FromStr; @@ -251,38 +251,39 @@ impl GenerationState { /// Returns the next used jump address. fn run_next_jumpdest_table_address(&mut self) -> Result { - let context = self.registers.context; - let code_len = u256_to_usize(self.get_code_len()?.into()); + let context = u256_to_usize(stack_peek(self, 0)? >> CONTEXT_SCALING_FACTOR)?; + let code_len = u256_to_usize(self.get_current_code_len()?.into()); - if self.jumpdest_proofs.is_none() { - self.generate_jumpdest_proofs()?; + if self.jumpdest_table.is_none() { + self.generate_jumpdest_table()?; } - let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + let Some(jumpdest_table) = &mut self.jumpdest_table else { return Err(ProgramError::ProverInputError( ProverInputError::InvalidJumpdestSimulation, )); }; - if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) - && let Some(next_jumpdest_address) = ctx_jumpdest_proofs.pop() + if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) + && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop() { Ok((next_jumpdest_address + 1).into()) } else { - self.jumpdest_proofs = None; + self.jumpdest_table = None; Ok(U256::zero()) } } /// Returns the proof for the last jump address. fn run_next_jumpdest_table_proof(&mut self) -> Result { - let Some(jumpdest_proofs) = &mut self.jumpdest_proofs else { + let context = u256_to_usize(stack_peek(self, 1)? >> CONTEXT_SCALING_FACTOR)?; + let Some(jumpdest_table) = &mut self.jumpdest_table else { return Err(ProgramError::ProverInputError( ProverInputError::InvalidJumpdestSimulation, )); }; - if let Some(ctx_jumpdest_proofs) = jumpdest_proofs.get_mut(&self.registers.context) - && let Some(next_jumpdest_proof) = ctx_jumpdest_proofs.pop() + if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) + && let Some(next_jumpdest_proof) = ctx_jumpdest_table.pop() { Ok(next_jumpdest_proof.into()) } else { @@ -295,7 +296,7 @@ impl GenerationState { impl GenerationState { /// Simulate the user's code and store all the jump addresses with their respective contexts. - fn generate_jumpdest_proofs(&mut self) -> Result<(), ProgramError> { + fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { let checkpoint = self.checkpoint(); let memory = self.memory.clone(); @@ -309,7 +310,7 @@ impl GenerationState { "terminate_common", self, ) else { - self.jumpdest_proofs = Some(HashMap::new()); + self.jumpdest_table = Some(HashMap::new()); return Ok(()); }; @@ -318,18 +319,18 @@ impl GenerationState { self.memory = memory; // Find proofs for all contexts - self.set_proofs_and_jumpdests(jumpdest_table); + self.set_jumpdest_analysis_inputs(jumpdest_table); Ok(()) } /// Given a HashMap containing the contexts and the jumpdest addresses, compute their respective proofs, /// by calling `get_proofs_and_jumpdests` - pub(crate) fn set_proofs_and_jumpdests( + pub(crate) fn set_jumpdest_analysis_inputs( &mut self, - jumpdest_table: HashMap>, + jumpdest_table: HashMap>, ) { - self.jumpdest_proofs = Some(HashMap::from_iter(jumpdest_table.into_iter().map( + self.jumpdest_table = Some(HashMap::from_iter(jumpdest_table.into_iter().map( |(ctx, jumpdest_table)| { let code = self.get_code(ctx).unwrap(); if let Some(&largest_address) = jumpdest_table.last() { @@ -347,28 +348,42 @@ impl GenerationState { } fn get_code(&self, context: usize) -> Result, ProgramError> { - let code_len = self.get_code_len()?; + let code_len = self.get_code_len(context)?; let code = (0..code_len) .map(|i| { - u256_to_u8(self.memory.get(MemoryAddress::new( - self.registers.context, - Segment::Code, - i, - ))) + u256_to_u8( + self.memory + .get(MemoryAddress::new(context, Segment::Code, i)), + ) }) .collect::, _>>()?; Ok(code) } - fn get_code_len(&self) -> Result { + fn set_code_len(&mut self, len: usize) { + self.memory.set( + MemoryAddress::new( + self.registers.context, + Segment::ContextMetadata, + ContextMetadata::CodeSize.unscale(), + ), + len.into(), + ) + } + + fn get_code_len(&self, context: usize) -> Result { let code_len = u256_to_usize(self.memory.get(MemoryAddress::new( - self.registers.context, + context, Segment::ContextMetadata, ContextMetadata::CodeSize.unscale(), )))?; Ok(code_len) } + fn get_current_code_len(&self) -> Result { + self.get_code_len(self.registers.context) + } + fn set_jumpdest_bits(&mut self, code: &[u8]) { const JUMPDEST_OPCODE: u8 = 0x5b; for (pos, opcode) in CodeIterator::new(code) { diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 54512b2f97..7f52eda7e4 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -55,7 +55,7 @@ pub(crate) struct GenerationState { /// jump destinations with its corresponding "proof". A "proof" for a jump destination is /// either 0 or an address i > 32 in the code (not necessarily pointing to an opcode) such that /// for every j in [i, i+32] it holds that code[j] < 0x7f - j + i. - pub(crate) jumpdest_proofs: Option>>, + pub(crate) jumpdest_table: Option>>, } impl GenerationState { @@ -97,7 +97,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - jumpdest_proofs: None, + jumpdest_table: None, }; let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); @@ -189,7 +189,7 @@ impl GenerationState { txn_root_ptr: 0, receipt_root_ptr: 0, }, - jumpdest_proofs: None, + jumpdest_table: None, } } } From eff7cc0f58ea3e4d77c5eb1991a073bc0a70236c Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 25 Jan 2024 00:45:28 -0500 Subject: [PATCH 123/175] Fix circuit sizes (#1484) --- evm/tests/log_opcode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 8d56c6bd6f..37d874cdac 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -442,7 +442,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 13..16, 15..18, 14..15, 10..11, 12..13, 17..20], + &[16..17, 12..15, 14..18, 14..15, 9..10, 12..13, 17..20], &config, ); From 14bb5bdb82c6e2f252350a5bb29313408ed8aca4 Mon Sep 17 00:00:00 2001 From: Vivek Pandya Date: Thu, 25 Jan 2024 18:50:34 +0530 Subject: [PATCH 124/175] Use web_time crate instead of std::time (#1481) --- plonky2/Cargo.toml | 3 ++- plonky2/src/fri/reduction_strategies.rs | 4 ++-- plonky2/src/util/timing.rs | 5 ++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index d2727853b8..4cc44cccd3 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -15,7 +15,7 @@ default = ["gate_testing", "parallel", "rand_chacha", "std", "timing"] gate_testing = [] parallel = ["hashbrown/rayon", "plonky2_maybe_rayon/parallel"] std = ["anyhow/std", "rand/std", "itertools/use_std"] -timing = ["std"] +timing = ["std", "dep:web-time"] [dependencies] ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. @@ -34,6 +34,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "rc"] serde_json = "1.0" static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } +web-time = { version = "1.0.0", optional = true } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] getrandom = { version = "0.2", default-features = false, features = ["js"] } diff --git a/plonky2/src/fri/reduction_strategies.rs b/plonky2/src/fri/reduction_strategies.rs index df273eea11..6e5752296e 100644 --- a/plonky2/src/fri/reduction_strategies.rs +++ b/plonky2/src/fri/reduction_strategies.rs @@ -1,10 +1,10 @@ use alloc::vec; use alloc::vec::Vec; -#[cfg(feature = "timing")] -use std::time::Instant; use log::debug; use serde::Serialize; +#[cfg(feature = "timing")] +use web_time::Instant; /// A method for deciding what arity to use at each reduction layer. #[derive(Debug, Clone, Eq, PartialEq, Serialize)] diff --git a/plonky2/src/util/timing.rs b/plonky2/src/util/timing.rs index faa7442738..0ab721be52 100644 --- a/plonky2/src/util/timing.rs +++ b/plonky2/src/util/timing.rs @@ -1,7 +1,6 @@ -#[cfg(feature = "timing")] -use std::time::{Duration, Instant}; - use log::{log, Level}; +#[cfg(feature = "timing")] +use web_time::{Duration, Instant}; /// The hierarchy of scopes, and the time consumed by each one. Useful for profiling. #[cfg(feature = "timing")] From 36e62a134dccc2e2fe17452a41a84d467965640a Mon Sep 17 00:00:00 2001 From: Vivek Pandya Date: Fri, 26 Jan 2024 20:40:45 +0530 Subject: [PATCH 125/175] Use usize::BITS and wrapping_shr in reverse_index_bits_in_place_small (#1478) --- util/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/util/src/lib.rs b/util/src/lib.rs index 6c8b2ed586..613bf6ef5a 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -110,9 +110,12 @@ fn reverse_index_bits_large(arr: &[T], n_power: usize) -> Vec { unsafe fn reverse_index_bits_in_place_small(arr: &mut [T], lb_n: usize) { if lb_n <= 6 { // BIT_REVERSE_6BIT holds 6-bit reverses. This shift makes them lb_n-bit reverses. - let dst_shr_amt = 6 - lb_n; + let dst_shr_amt = 6 - lb_n as u32; for src in 0..arr.len() { - let dst = (BIT_REVERSE_6BIT[src] as usize) >> dst_shr_amt; + // `wrapping_shr` handles the case when `arr.len() == 1`. In that case `src == 0`, so + // `src.reverse_bits() == 0`. `usize::wrapping_shr` by 64 is a no-op, but it gives the + // correct result. + let dst = (BIT_REVERSE_6BIT[src] as usize).wrapping_shr(dst_shr_amt); if src < dst { swap(arr.get_unchecked_mut(src), arr.get_unchecked_mut(dst)); } @@ -121,11 +124,14 @@ unsafe fn reverse_index_bits_in_place_small(arr: &mut [T], lb_n: usize) { // LLVM does not know that it does not need to reverse src at each iteration (which is // expensive on x86). We take advantage of the fact that the low bits of dst change rarely and the high // bits of dst are dependent only on the low bits of src. - let dst_lo_shr_amt = 64 - (lb_n - 6); + let dst_lo_shr_amt = usize::BITS - (lb_n - 6) as u32; let dst_hi_shl_amt = lb_n - 6; for src_chunk in 0..(arr.len() >> 6) { let src_hi = src_chunk << 6; - let dst_lo = src_chunk.reverse_bits() >> dst_lo_shr_amt; + // `wrapping_shr` handles the case when `arr.len() == 1`. In that case `src == 0`, so + // `src.reverse_bits() == 0`. `usize::wrapping_shr` by 64 is a no-op, but it gives the + // correct result. + let dst_lo = src_chunk.reverse_bits().wrapping_shr(dst_lo_shr_amt); for src_lo in 0..(1 << 6) { let dst_hi = (BIT_REVERSE_6BIT[src_lo] as usize) << dst_hi_shl_amt; let src = src_hi + src_lo; From a9060e61b8004525b6f84c69dd21ad1a5cd1c3b2 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sun, 28 Jan 2024 09:03:58 -0800 Subject: [PATCH 126/175] Add LA audit report --- ...Polygon Zero Plonky2 Final Audit Report.pdf | Bin 0 -> 238137 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 audits/Least Authority - Polygon Zero Plonky2 Final Audit Report.pdf diff --git a/audits/Least Authority - Polygon Zero Plonky2 Final Audit Report.pdf b/audits/Least Authority - Polygon Zero Plonky2 Final Audit Report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4df70d30b9a15f48158f89cd543ab46a5b509264 GIT binary patch literal 238137 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
Q9rZSPy`5$0#2te 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ⅅaA)q;Lto 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
8W+!nX-D;aNA9q?@}+)>>i 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

g4WpynFpRkMVpi99To*L^VWIP;^Luh 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 bw9h! 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 LMnFJLN&f!;YRO7; literal 0 HcmV?d00001 From f76245e2986eee889b1cdb117a64d7cd32a44768 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 1 Feb 2024 07:16:28 -0500 Subject: [PATCH 127/175] Cleanup imports (#1492) --- evm/src/all_stark.rs | 2 +- evm/src/arithmetic/arithmetic_stark.rs | 4 ++-- evm/src/arithmetic/byte.rs | 2 +- evm/src/arithmetic/columns.rs | 2 +- evm/src/arithmetic/divmod.rs | 2 +- evm/src/arithmetic/modular.rs | 2 +- evm/src/arithmetic/utils.rs | 2 +- evm/src/byte_packing/byte_packing_stark.rs | 2 +- evm/src/constraint_consumer.rs | 2 +- evm/src/cpu/columns/general.rs | 6 +++--- evm/src/cpu/columns/mod.rs | 8 ++++---- evm/src/cpu/columns/ops.rs | 6 +++--- evm/src/cpu/cpu_stark.rs | 6 +++--- evm/src/cpu/kernel/assembler.rs | 6 +----- evm/src/cpu/kernel/constants/exc_bitfields.rs | 2 +- evm/src/cpu/kernel/constants/trie_type.rs | 2 +- evm/src/cpu/kernel/interpreter.rs | 11 ++--------- evm/src/cpu/kernel/stack/permutations.rs | 2 +- evm/src/cpu/kernel/stack/stack_manipulation.rs | 4 ++-- evm/src/cpu/kernel/tests/bignum/mod.rs | 2 +- evm/src/cpu/kernel/utils.rs | 2 +- evm/src/cpu/membus.rs | 2 +- evm/src/cpu/stack.rs | 2 +- evm/src/cross_table_lookup.rs | 8 ++++---- evm/src/curve_pairings.rs | 2 +- evm/src/extension_tower.rs | 4 ++-- evm/src/fixed_recursive_verifier.rs | 2 +- evm/src/generation/mpt.rs | 2 +- evm/src/generation/prover_input.rs | 4 ++-- evm/src/keccak/keccak_stark.rs | 12 ++++-------- evm/src/keccak_sponge/columns.rs | 6 +++--- evm/src/keccak_sponge/keccak_sponge_stark.rs | 15 +++++---------- evm/src/logic.rs | 6 +++--- evm/src/memory/memory_stark.rs | 2 +- evm/src/recursive_verifier.rs | 4 ++-- evm/src/util.rs | 2 +- evm/src/verifier.rs | 2 +- evm/src/witness/traces.rs | 2 +- evm/tests/empty_txn_list.rs | 2 +- 39 files changed, 68 insertions(+), 88 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index cf03a60088..fadb9d1622 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -1,4 +1,4 @@ -use std::iter; +use core::iter; use itertools::Itertools; use plonky2::field::extension::Extendable; diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 1d3af2b9af..0165542e16 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -1,5 +1,5 @@ -use std::marker::PhantomData; -use std::ops::Range; +use core::marker::PhantomData; +use core::ops::Range; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; diff --git a/evm/src/arithmetic/byte.rs b/evm/src/arithmetic/byte.rs index b7381ae0fa..f7581efa77 100644 --- a/evm/src/arithmetic/byte.rs +++ b/evm/src/arithmetic/byte.rs @@ -60,7 +60,7 @@ //! y * 256 ∈ {0, 256, 512, ..., 2^16 - 256} //! 8. Hence y ∈ {0, 1, ..., 255} -use std::ops::Range; +use core::ops::Range; use ethereum_types::U256; use plonky2::field::extension::Extendable; diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs index dc535492cd..e4172bc073 100644 --- a/evm/src/arithmetic/columns.rs +++ b/evm/src/arithmetic/columns.rs @@ -1,6 +1,6 @@ //! Arithmetic unit -use std::ops::Range; +use core::ops::Range; pub(crate) const LIMB_BITS: usize = 16; const EVM_REGISTER_BITS: usize = 256; diff --git a/evm/src/arithmetic/divmod.rs b/evm/src/arithmetic/divmod.rs index b284c829c5..a4599dc721 100644 --- a/evm/src/arithmetic/divmod.rs +++ b/evm/src/arithmetic/divmod.rs @@ -2,7 +2,7 @@ //! //! The logic for verifying them is detailed in the `modular` submodule. -use std::ops::Range; +use core::ops::Range; use ethereum_types::U256; use plonky2::field::extension::Extendable; diff --git a/evm/src/arithmetic/modular.rs b/evm/src/arithmetic/modular.rs index 05f9cf0da7..5a1df5c733 100644 --- a/evm/src/arithmetic/modular.rs +++ b/evm/src/arithmetic/modular.rs @@ -108,7 +108,7 @@ //! only require 96 columns, or 80 if the output doesn't need to be //! reduced. -use std::ops::Range; +use core::ops::Range; use ethereum_types::U256; use num::bigint::Sign; diff --git a/evm/src/arithmetic/utils.rs b/evm/src/arithmetic/utils.rs index 7fadb8f14d..7350dd3263 100644 --- a/evm/src/arithmetic/utils.rs +++ b/evm/src/arithmetic/utils.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, AddAssign, Mul, Neg, Range, Shr, Sub, SubAssign}; +use core::ops::{Add, AddAssign, Mul, Neg, Range, Shr, Sub, SubAssign}; use ethereum_types::U256; use plonky2::field::extension::Extendable; diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index 0de8968aa0..03449d0d12 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -25,7 +25,7 @@ //! This means that the higher-order bytes will be thrown away during the process, if the value //! is greater than 256^length, and as a result a different value will be stored in memory. -use std::marker::PhantomData; +use core::marker::PhantomData; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; diff --git a/evm/src/constraint_consumer.rs b/evm/src/constraint_consumer.rs index 470e2db218..919b51638a 100644 --- a/evm/src/constraint_consumer.rs +++ b/evm/src/constraint_consumer.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; diff --git a/evm/src/cpu/columns/general.rs b/evm/src/cpu/columns/general.rs index 08b6c82dd1..f565acc625 100644 --- a/evm/src/cpu/columns/general.rs +++ b/evm/src/cpu/columns/general.rs @@ -1,6 +1,6 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::fmt::{Debug, Formatter}; -use std::mem::{size_of, transmute}; +use core::borrow::{Borrow, BorrowMut}; +use core::fmt::{Debug, Formatter}; +use core::mem::{size_of, transmute}; /// General purpose columns, which can have different meanings depending on what CTL or other /// operation is occurring at this row. diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index 8a0ad0a594..92da4e9979 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -1,7 +1,7 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::fmt::Debug; -use std::mem::{size_of, transmute}; -use std::ops::{Index, IndexMut}; +use core::borrow::{Borrow, BorrowMut}; +use core::fmt::Debug; +use core::mem::{size_of, transmute}; +use core::ops::{Index, IndexMut}; use plonky2::field::types::Field; diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index 0c5be6ef52..c15d657229 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -1,6 +1,6 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::mem::{size_of, transmute}; -use std::ops::{Deref, DerefMut}; +use core::borrow::{Borrow, BorrowMut}; +use core::mem::{size_of, transmute}; +use core::ops::{Deref, DerefMut}; use crate::util::transmute_no_compile_time_size_checks; diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 1212e7a741..91e1f3ef63 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -1,6 +1,6 @@ -use std::borrow::Borrow; -use std::iter::repeat; -use std::marker::PhantomData; +use core::borrow::Borrow; +use core::iter::repeat; +use core::marker::PhantomData; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index 29c46ce1f0..7a546e7703 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -430,12 +430,8 @@ fn push_target_size(target: &PushTarget) -> u8 { #[cfg(test)] mod tests { - use std::collections::HashMap; - - use itertools::Itertools; - + use super::*; use crate::cpu::kernel::assembler::*; - use crate::cpu::kernel::ast::*; use crate::cpu::kernel::parser::parse; #[test] diff --git a/evm/src/cpu/kernel/constants/exc_bitfields.rs b/evm/src/cpu/kernel/constants/exc_bitfields.rs index 6054bf9bcc..59dec8b1e8 100644 --- a/evm/src/cpu/kernel/constants/exc_bitfields.rs +++ b/evm/src/cpu/kernel/constants/exc_bitfields.rs @@ -1,4 +1,4 @@ -use std::ops::RangeInclusive; +use core::ops::RangeInclusive; use ethereum_types::U256; diff --git a/evm/src/cpu/kernel/constants/trie_type.rs b/evm/src/cpu/kernel/constants/trie_type.rs index 5120c16389..fd89f41000 100644 --- a/evm/src/cpu/kernel/constants/trie_type.rs +++ b/evm/src/cpu/kernel/constants/trie_type.rs @@ -1,4 +1,4 @@ -use std::ops::Deref; +use core::ops::Deref; use eth_trie_utils::partial_trie::HashedPartialTrie; diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index be66d2e90e..7ba5809fed 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1,8 +1,8 @@ //! An EVM interpreter for testing and debugging purposes. use core::cmp::Ordering; +use core::ops::Range; use std::collections::{BTreeSet, HashMap, HashSet}; -use std::ops::Range; use anyhow::bail; use eth_trie_utils::partial_trie::PartialTrie; @@ -1664,15 +1664,8 @@ pub(crate) use unpack_address; #[cfg(test)] mod tests { - use std::collections::HashMap; - - use ethereum_types::U256; - - use crate::cpu::kernel::constants::context_metadata::ContextMetadata; - use crate::cpu::kernel::interpreter::{run, Interpreter}; + use super::*; use crate::memory::segments::Segment; - use crate::witness::memory::MemoryAddress; - use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_run() -> anyhow::Result<()> { diff --git a/evm/src/cpu/kernel/stack/permutations.rs b/evm/src/cpu/kernel/stack/permutations.rs index 69d5f2e672..71304edd0c 100644 --- a/evm/src/cpu/kernel/stack/permutations.rs +++ b/evm/src/cpu/kernel/stack/permutations.rs @@ -19,8 +19,8 @@ //! //! We typically represent a `(0 i)` transposition as a single scalar `i`. +use core::hash::Hash; use std::collections::{HashMap, HashSet}; -use std::hash::Hash; use crate::cpu::kernel::stack::stack_manipulation::{StackItem, StackOp}; diff --git a/evm/src/cpu/kernel/stack/stack_manipulation.rs b/evm/src/cpu/kernel/stack/stack_manipulation.rs index 5214351c5f..a7b376c5ea 100644 --- a/evm/src/cpu/kernel/stack/stack_manipulation.rs +++ b/evm/src/cpu/kernel/stack/stack_manipulation.rs @@ -1,7 +1,7 @@ -use std::cmp::Ordering; +use core::cmp::Ordering; +use core::hash::Hash; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{BinaryHeap, HashMap}; -use std::hash::Hash; use itertools::Itertools; diff --git a/evm/src/cpu/kernel/tests/bignum/mod.rs b/evm/src/cpu/kernel/tests/bignum/mod.rs index 9e15d96f02..0cc6f0dc1b 100644 --- a/evm/src/cpu/kernel/tests/bignum/mod.rs +++ b/evm/src/cpu/kernel/tests/bignum/mod.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use core::cmp::Ordering; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::PathBuf; diff --git a/evm/src/cpu/kernel/utils.rs b/evm/src/cpu/kernel/utils.rs index eea033d4e1..18b5f54822 100644 --- a/evm/src/cpu/kernel/utils.rs +++ b/evm/src/cpu/kernel/utils.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use core::fmt::Debug; use ethereum_types::U256; use plonky2_util::ceil_div_usize; diff --git a/evm/src/cpu/membus.rs b/evm/src/cpu/membus.rs index fb6c530113..6ce845613d 100644 --- a/evm/src/cpu/membus.rs +++ b/evm/src/cpu/membus.rs @@ -11,7 +11,7 @@ pub(crate) const NUM_GP_CHANNELS: usize = 3; /// Indices for code and general purpose memory channels. pub mod channel_indices { - use std::ops::Range; + use core::ops::Range; pub(crate) const CODE: usize = 0; pub(crate) const GP: Range = CODE + 1..(CODE + 1) + super::NUM_GP_CHANNELS; diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 9acf1f3af0..87ca7ee1c4 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -1,4 +1,4 @@ -use std::cmp::max; +use core::cmp::max; use itertools::izip; use plonky2::field::extension::Extendable; diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 428279ba89..c3110ee89a 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -27,10 +27,10 @@ //! is similar, but we provide not only `local_values` but also `next_values` -- corresponding to //! the current and next row values -- when computing the linear combinations. -use std::borrow::Borrow; -use std::cmp::min; -use std::fmt::Debug; -use std::iter::repeat; +use core::borrow::Borrow; +use core::cmp::min; +use core::fmt::Debug; +use core::iter::repeat; use anyhow::{ensure, Result}; use hashbrown::HashMap; diff --git a/evm/src/curve_pairings.rs b/evm/src/curve_pairings.rs index 71188a216f..af155cc506 100644 --- a/evm/src/curve_pairings.rs +++ b/evm/src/curve_pairings.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, Mul, Neg}; +use core::ops::{Add, Mul, Neg}; use ethereum_types::U256; use rand::distributions::Standard; diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs index da7cca554e..ea4e317641 100644 --- a/evm/src/extension_tower.rs +++ b/evm/src/extension_tower.rs @@ -1,5 +1,5 @@ -use std::fmt::Debug; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use core::fmt::Debug; +use core::ops::{Add, Div, Mul, Neg, Sub}; use ethereum_types::{U256, U512}; use rand::distributions::{Distribution, Standard}; diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 58db998731..c7a705c37a 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -1,6 +1,6 @@ use core::mem::{self, MaybeUninit}; +use core::ops::Range; use std::collections::BTreeMap; -use std::ops::Range; use std::path::Path; use std::sync::atomic::AtomicBool; use std::sync::Arc; diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index a2abe1b28a..71976bc012 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -1,5 +1,5 @@ +use core::ops::Deref; use std::collections::HashMap; -use std::ops::Deref; use bytes::Bytes; use eth_trie_utils::nibbles::Nibbles; diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 0214b84708..5c0aba1a4f 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,6 +1,6 @@ -use std::cmp::min; +use core::cmp::min; +use core::mem::transmute; use std::collections::{BTreeSet, HashMap}; -use std::mem::transmute; use std::str::FromStr; use anyhow::{bail, Error}; diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 63098e793c..26edc40ae0 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; @@ -622,21 +622,17 @@ impl, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakSpongeS #[cfg(test)] mod tests { - use std::borrow::Borrow; - use anyhow::Result; - use itertools::Itertools; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::field::types::PrimeField64; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use crate::keccak_sponge::columns::KeccakSpongeColumnsView; - use crate::keccak_sponge::keccak_sponge_stark::{KeccakSpongeOp, KeccakSpongeStark}; + use super::*; use crate::memory::segments::Segment; use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - use crate::witness::memory::MemoryAddress; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/logic.rs b/evm/src/logic.rs index 43ea23423e..c03991af79 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use ethereum_types::U256; use itertools::izip; @@ -28,8 +28,8 @@ const PACKED_LEN: usize = ceil_div_usize(VAL_BITS, PACKED_LIMB_BITS); /// `LogicStark` columns. pub(crate) mod columns { - use std::cmp::min; - use std::ops::Range; + use core::cmp::min; + use core::ops::Range; use super::{PACKED_LEN, PACKED_LIMB_BITS, VAL_BITS}; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 13ea1166f0..5adf46c7f8 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use core::marker::PhantomData; use ethereum_types::U256; use itertools::Itertools; diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 8d10b49866..f3065e10f1 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -1,5 +1,5 @@ -use std::array::from_fn; -use std::fmt::Debug; +use core::array::from_fn; +use core::fmt::Debug; use anyhow::Result; use ethereum_types::{BigEndianHash, H256, U256}; diff --git a/evm/src/util.rs b/evm/src/util.rs index f4d80859e7..aec2e63e17 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -1,4 +1,4 @@ -use std::mem::{size_of, transmute_copy, ManuallyDrop}; +use core::mem::{size_of, transmute_copy, ManuallyDrop}; use ethereum_types::{H160, H256, U256}; use itertools::Itertools; diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 9eb12100de..a173be5b69 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -1,4 +1,4 @@ -use std::any::type_name; +use core::any::type_name; use anyhow::{ensure, Result}; use ethereum_types::{BigEndianHash, U256}; diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index d767aa7f42..f7f5c9d365 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -1,4 +1,4 @@ -use std::mem::size_of; +use core::mem::size_of; use itertools::Itertools; use plonky2::field::extension::Extendable; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 2130e51bab..15416c8c8d 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -1,5 +1,5 @@ +use core::marker::PhantomData; use std::collections::HashMap; -use std::marker::PhantomData; use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; From 1dc22b761f22c2b2c9fa5334e0b87c72dc9154c5 Mon Sep 17 00:00:00 2001 From: Utsav Jain <43694826+utsavjnn@users.noreply.github.com> Date: Thu, 1 Feb 2024 19:19:36 +0530 Subject: [PATCH 128/175] Fix BaseSumGenerator and BaseSplitGenerator Ids (#1494) * fix: different ids for generators with different bases required for correct serialisation Signed-off-by: electron-team * fixing wasm32 build issue Signed-off-by: electron-team * fix fmt issue Signed-off-by: electron-team --------- Signed-off-by: electron-team --- plonky2/src/gadgets/split_base.rs | 6 +++--- plonky2/src/gates/base_sum.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index 1562323c8e..a2c98ac707 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -1,6 +1,6 @@ -use alloc::string::{String, ToString}; -use alloc::vec; +use alloc::string::String; use alloc::vec::Vec; +use alloc::{format, vec}; use core::borrow::Borrow; use itertools::Itertools; @@ -91,7 +91,7 @@ impl, const B: usize, const D: usize> SimpleGenerat for BaseSumGenerator { fn id(&self) -> String { - "BaseSumGenerator".to_string() + format!("BaseSumGenerator + Base: {B}") } fn dependencies(&self) -> Vec { diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 087f2bf6c7..1d0f8f809e 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -1,4 +1,4 @@ -use alloc::string::{String, ToString}; +use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; use core::ops::Range; @@ -179,7 +179,7 @@ impl, const B: usize, const D: usize> SimpleGenerat for BaseSplitGenerator { fn id(&self) -> String { - "BaseSplitGenerator".to_string() + format!("BaseSplitGenerator + Base: {B}") } fn dependencies(&self) -> Vec { From e502a0dfb11e8dc3543e18b367e0ab53d06aada8 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:41:42 -0500 Subject: [PATCH 129/175] Make CTLs more generic (#1493) * Make number of tables generic * Remove dependency on Table in the CTL module * Remove needless conversion * Remove more needless conversion * Clippy * Apply reviews --- evm/src/all_stark.rs | 57 ++++++---- evm/src/arithmetic/arithmetic_stark.rs | 2 +- evm/src/cpu/cpu_stark.rs | 2 +- evm/src/cross_table_lookup.rs | 139 ++++++++++++++----------- evm/src/fixed_recursive_verifier.rs | 18 ++-- evm/src/prover.rs | 2 +- evm/src/recursive_verifier.rs | 4 +- evm/src/verifier.rs | 2 +- 8 files changed, 125 insertions(+), 101 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index fadb9d1622..d69ff302cf 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -1,4 +1,5 @@ use core::iter; +use core::ops::Deref; use itertools::Itertools; use plonky2::field::extension::Extendable; @@ -12,7 +13,7 @@ use crate::config::StarkConfig; use crate::cpu::cpu_stark; use crate::cpu::cpu_stark::CpuStark; use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; +use crate::cross_table_lookup::{CrossTableLookup, TableIdx, TableWithColumns}; use crate::keccak::keccak_stark; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; @@ -79,6 +80,16 @@ pub enum Table { Memory = 6, } +impl Deref for Table { + type Target = TableIdx; + + fn deref(&self) -> &Self::Target { + // Hacky way to implement `Deref` for `Table` so that we don't have to + // call `Table::Foo as usize`, but perhaps too ugly to be worth it. + [&0, &1, &2, &3, &4, &5, &6][*self as TableIdx] + } +} + /// Number of STARK tables. pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1; @@ -121,27 +132,27 @@ fn ctl_arithmetic() -> CrossTableLookup { /// `CrossTableLookup` for `BytePackingStark`, to connect it with the `Cpu` module. fn ctl_byte_packing() -> CrossTableLookup { let cpu_packing_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_byte_packing(), Some(cpu_stark::ctl_filter_byte_packing()), ); let cpu_unpacking_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_byte_unpacking(), Some(cpu_stark::ctl_filter_byte_unpacking()), ); let cpu_push_packing_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_byte_packing_push(), Some(cpu_stark::ctl_filter_byte_packing_push()), ); let cpu_jumptable_read_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_jumptable_read(), Some(cpu_stark::ctl_filter_syscall_exceptions()), ); let byte_packing_looked = TableWithColumns::new( - Table::BytePacking, + *Table::BytePacking, byte_packing_stark::ctl_looked_data(), Some(byte_packing_stark::ctl_looked_filter()), ); @@ -161,12 +172,12 @@ fn ctl_byte_packing() -> CrossTableLookup { /// Its consistency with the 'output' CTL is ensured through a timestamp column on the `KeccakStark` side. fn ctl_keccak_inputs() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( - Table::KeccakSponge, + *Table::KeccakSponge, keccak_sponge_stark::ctl_looking_keccak_inputs(), Some(keccak_sponge_stark::ctl_looking_keccak_filter()), ); let keccak_looked = TableWithColumns::new( - Table::Keccak, + *Table::Keccak, keccak_stark::ctl_data_inputs(), Some(keccak_stark::ctl_filter_inputs()), ); @@ -177,12 +188,12 @@ fn ctl_keccak_inputs() -> CrossTableLookup { /// `KeccakStarkSponge` looks into `KeccakStark` to give the outputs of the sponge. fn ctl_keccak_outputs() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( - Table::KeccakSponge, + *Table::KeccakSponge, keccak_sponge_stark::ctl_looking_keccak_outputs(), Some(keccak_sponge_stark::ctl_looking_keccak_filter()), ); let keccak_looked = TableWithColumns::new( - Table::Keccak, + *Table::Keccak, keccak_stark::ctl_data_outputs(), Some(keccak_stark::ctl_filter_outputs()), ); @@ -192,12 +203,12 @@ fn ctl_keccak_outputs() -> CrossTableLookup { /// `CrossTableLookup` for `KeccakSpongeStark` to connect it with the `Cpu` module. fn ctl_keccak_sponge() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_keccak_sponge(), Some(cpu_stark::ctl_filter_keccak_sponge()), ); let keccak_sponge_looked = TableWithColumns::new( - Table::KeccakSponge, + *Table::KeccakSponge, keccak_sponge_stark::ctl_looked_data(), Some(keccak_sponge_stark::ctl_looked_filter()), ); @@ -207,63 +218,63 @@ fn ctl_keccak_sponge() -> CrossTableLookup { /// `CrossTableLookup` for `LogicStark` to connect it with the `Cpu` and `KeccakSponge` modules. fn ctl_logic() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_logic(), Some(cpu_stark::ctl_filter_logic()), ); let mut all_lookers = vec![cpu_looking]; for i in 0..keccak_sponge_stark::num_logic_ctls() { let keccak_sponge_looking = TableWithColumns::new( - Table::KeccakSponge, + *Table::KeccakSponge, keccak_sponge_stark::ctl_looking_logic(i), Some(keccak_sponge_stark::ctl_looking_logic_filter()), ); all_lookers.push(keccak_sponge_looking); } let logic_looked = - TableWithColumns::new(Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); + TableWithColumns::new(*Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); CrossTableLookup::new(all_lookers, logic_looked) } /// `CrossTableLookup` for `MemoryStark` to connect it with all the modules which need memory accesses. fn ctl_memory() -> CrossTableLookup { let cpu_memory_code_read = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_code_memory(), Some(cpu_stark::ctl_filter_code_memory()), ); let cpu_memory_gp_ops = (0..NUM_GP_CHANNELS).map(|channel| { TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_gp_memory(channel), Some(cpu_stark::ctl_filter_gp_memory(channel)), ) }); let cpu_push_write_ops = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_partial_memory::(), Some(cpu_stark::ctl_filter_partial_memory()), ); let cpu_set_context_write = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_memory_old_sp_write_set_context::(), Some(cpu_stark::ctl_filter_set_context()), ); let cpu_set_context_read = TableWithColumns::new( - Table::Cpu, + *Table::Cpu, cpu_stark::ctl_data_memory_new_sp_read_set_context::(), Some(cpu_stark::ctl_filter_set_context()), ); let keccak_sponge_reads = (0..KECCAK_RATE_BYTES).map(|i| { TableWithColumns::new( - Table::KeccakSponge, + *Table::KeccakSponge, keccak_sponge_stark::ctl_looking_memory(i), Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), ) }); let byte_packing_ops = (0..32).map(|i| { TableWithColumns::new( - Table::BytePacking, + *Table::BytePacking, byte_packing_stark::ctl_looking_memory(i), Some(byte_packing_stark::ctl_looking_memory_filter(i)), ) @@ -280,7 +291,7 @@ fn ctl_memory() -> CrossTableLookup { .chain(byte_packing_ops) .collect(); let memory_looked = TableWithColumns::new( - Table::Memory, + *Table::Memory, memory_stark::ctl_data(), Some(memory_stark::ctl_filter()), ); diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 0165542e16..5cd0697392 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -110,7 +110,7 @@ pub(crate) fn ctl_arithmetic_rows() -> TableWithColumns { // corresponding to a 256-bit input or output register (also `ops` // is used as the operation filter). TableWithColumns::new( - Table::Arithmetic, + *Table::Arithmetic, cpu_arith_data_link(&all_combined_cols, ®ISTER_MAP), filter, ) diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 91e1f3ef63..114525e99c 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -108,7 +108,7 @@ pub(crate) fn ctl_arithmetic_base_rows() -> TableWithColumns { F::ONE, ); TableWithColumns::new( - Table::Cpu, + *Table::Cpu, columns, Some(Filter::new( vec![(Column::single(COL_MAP.op.push_prover_input), col_bit)], diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index c3110ee89a..28d14ca0d5 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -52,7 +52,7 @@ use plonky2::plonk::plonk_common::{ use plonky2::util::ceil_div_usize; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; -use crate::all_stark::{Table, NUM_TABLES}; +use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::StarkEvaluationFrame; @@ -423,19 +423,22 @@ impl Filter { } } -/// A `Table` with a linear combination of columns and a filter. -/// `filter` is used to determine the rows to select in `Table`. -/// `columns` represents linear combinations of the columns of `Table`. +/// An alias for `usize`, to represent the index of a STARK table in a multi-STARK setting. +pub(crate) type TableIdx = usize; + +/// A `table` index with a linear combination of columns and a filter. +/// `filter` is used to determine the rows to select in `table`. +/// `columns` represents linear combinations of the columns of `table`. #[derive(Clone, Debug)] pub(crate) struct TableWithColumns { - table: Table, + table: TableIdx, columns: Vec>, pub(crate) filter: Option>, } impl TableWithColumns { - /// Generates a new `TableWithColumns` given a `Table`, a linear combination of columns `columns` and a `filter`. - pub(crate) fn new(table: Table, columns: Vec>, filter: Option>) -> Self { + /// Generates a new `TableWithColumns` given a `table` index, a linear combination of columns `columns` and a `filter`. + pub(crate) fn new(table: TableIdx, columns: Vec>, filter: Option>) -> Self { Self { table, columns, @@ -447,7 +450,7 @@ impl TableWithColumns { /// Cross-table lookup data consisting in the lookup table (`looked_table`) and all the tables that look into `looked_table` (`looking_tables`). /// Each `looking_table` corresponds to a STARK's table whose rows have been filtered out and whose columns have been through a linear combination (see `eval_table`). The concatenation of those smaller tables should result in the `looked_table`. #[derive(Clone)] -pub(crate) struct CrossTableLookup { +pub struct CrossTableLookup { /// Column linear combinations for all tables that are looking into the current table. pub(crate) looking_tables: Vec>, /// Column linear combination for the current table. @@ -476,7 +479,7 @@ impl CrossTableLookup { /// - the number of helper columns for this table, for each Cross-table lookup. pub(crate) fn num_ctl_helpers_zs_all( ctls: &[Self], - table: Table, + table: TableIdx, num_challenges: usize, constraint_degree: usize, ) -> (usize, usize, Vec) { @@ -698,17 +701,17 @@ pub(crate) fn get_grand_product_challenge_set_target< } /// Returns the number of helper columns for each `Table`. -pub(crate) fn num_ctl_helper_columns_by_table( +pub(crate) fn num_ctl_helper_columns_by_table( ctls: &[CrossTableLookup], constraint_degree: usize, -) -> Vec<[usize; NUM_TABLES]> { - let mut res = vec![[0; NUM_TABLES]; ctls.len()]; +) -> Vec<[usize; N]> { + let mut res = vec![[0; N]; ctls.len()]; for (i, ctl) in ctls.iter().enumerate() { let CrossTableLookup { looking_tables, looked_table, } = ctl; - let mut num_by_table = [0; NUM_TABLES]; + let mut num_by_table = [0; N]; let grouped_lookups = looking_tables.iter().group_by(|&a| a.table); @@ -716,7 +719,7 @@ pub(crate) fn num_ctl_helper_columns_by_table( let sum = group.count(); if sum > 2 { // We only need helper columns if there are more than 2 columns. - num_by_table[table as usize] = ceil_div_usize(sum, constraint_degree - 1); + num_by_table[table] = ceil_div_usize(sum, constraint_degree - 1); } } @@ -731,13 +734,13 @@ pub(crate) fn num_ctl_helper_columns_by_table( /// - `ctl_challenges` corresponds to the challenges used for CTLs. /// - `constraint_degree` is the maximal constraint degree for the table. /// For each `CrossTableLookup`, and each looking/looked table, the partial products for the CTL are computed, and added to the said table's `CtlZData`. -pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize>( - trace_poly_values: &[Vec>; NUM_TABLES], +pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize, const N: usize>( + trace_poly_values: &[Vec>; N], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &GrandProductChallengeSet, constraint_degree: usize, -) -> [CtlData<'a, F>; NUM_TABLES] { - let mut ctl_data_per_table = [0; NUM_TABLES].map(|_| CtlData::default()); +) -> [CtlData<'a, F>; N] { + let mut ctl_data_per_table = [0; N].map(|_| CtlData::default()); for CrossTableLookup { looking_tables, looked_table, @@ -753,7 +756,7 @@ pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize>( ); let mut z_looked = partial_sums( - &trace_poly_values[looked_table.table as usize], + &trace_poly_values[looked_table.table], &[(&looked_table.columns, &looked_table.filter)], challenge, constraint_degree, @@ -763,10 +766,10 @@ pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize>( let num_helpers = helpers_zs.len() - 1; let count = looking_tables .iter() - .filter(|looking_table| looking_table.table as usize == table) + .filter(|looking_table| looking_table.table == table) .count(); let cols_filts = looking_tables.iter().filter_map(|looking_table| { - if looking_table.table as usize == table { + if looking_table.table == table { Some((&looking_table.columns, &looking_table.filter)) } else { None @@ -788,7 +791,7 @@ pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize>( } // There is no helper column for the looking table. let looked_poly = z_looked[0].clone(); - ctl_data_per_table[looked_table.table as usize] + ctl_data_per_table[looked_table.table] .zs_columns .push(CtlZData { helper_columns: vec![], @@ -899,8 +902,8 @@ pub(crate) fn get_helper_cols( /// Computes helper columns and Z polynomials for all looking tables /// of one cross-table lookup (i.e. for one looked table). -fn ctl_helper_zs_cols( - all_stark_traces: &[Vec>; NUM_TABLES], +fn ctl_helper_zs_cols( + all_stark_traces: &[Vec>; N], looking_tables: Vec>, challenge: GrandProductChallenge, constraint_degree: usize, @@ -910,14 +913,14 @@ fn ctl_helper_zs_cols( grouped_lookups .into_iter() .map(|(table, group)| { - let degree = all_stark_traces[table as usize][0].len(); + let degree = all_stark_traces[table][0].len(); let columns_filters = group .map(|table| (&table.columns[..], &table.filter)) .collect::], &Option>)>>(); ( - table as usize, + table, partial_sums( - &all_stark_traces[table as usize], + &all_stark_traces[table], &columns_filters, challenge, constraint_degree, @@ -1000,16 +1003,16 @@ impl<'a, F: RichField + Extendable, const D: usize> CtlCheckVars<'a, F, F::Extension, F::Extension, D> { /// Extracts the `CtlCheckVars` for each STARK. - pub(crate) fn from_proofs>( - proofs: &[StarkProofWithMetadata; NUM_TABLES], + pub(crate) fn from_proofs, const N: usize>( + proofs: &[StarkProofWithMetadata; N], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, - num_lookup_columns: &[usize; NUM_TABLES], - num_helper_ctl_columns: &Vec<[usize; NUM_TABLES]>, - ) -> [Vec; NUM_TABLES] { - let mut total_num_helper_cols_by_table = [0; NUM_TABLES]; + num_lookup_columns: &[usize; N], + num_helper_ctl_columns: &Vec<[usize; N]>, + ) -> [Vec; N] { + let mut total_num_helper_cols_by_table = [0; N]; for p_ctls in num_helper_ctl_columns { - for j in 0..NUM_TABLES { + for j in 0..N { total_num_helper_cols_by_table[j] += p_ctls[j] * ctl_challenges.challenges.len(); } } @@ -1028,9 +1031,9 @@ impl<'a, F: RichField + Extendable, const D: usize> .collect::>(); // Put each cross-table lookup polynomial into the correct table data: if a CTL polynomial is extracted from looking/looked table t, then we add it to the `CtlCheckVars` of table t. - let mut start_indices = [0; NUM_TABLES]; - let mut z_indices = [0; NUM_TABLES]; - let mut ctl_vars_per_table = [0; NUM_TABLES].map(|_| vec![]); + let mut start_indices = [0; N]; + let mut z_indices = [0; N]; + let mut ctl_vars_per_table = [0; N].map(|_| vec![]); for ( CrossTableLookup { looking_tables, @@ -1042,11 +1045,10 @@ impl<'a, F: RichField + Extendable, const D: usize> for &challenges in &ctl_challenges.challenges { // Group looking tables by `Table`, since we bundle the looking tables taken from the same `Table` together thanks to helper columns. // We want to only iterate on each `Table` once. - let mut filtered_looking_tables = - Vec::with_capacity(min(looking_tables.len(), NUM_TABLES)); + let mut filtered_looking_tables = Vec::with_capacity(min(looking_tables.len(), N)); for table in looking_tables { - if !filtered_looking_tables.contains(&(table.table as usize)) { - filtered_looking_tables.push(table.table as usize); + if !filtered_looking_tables.contains(&(table.table)) { + filtered_looking_tables.push(table.table); } } @@ -1057,10 +1059,10 @@ impl<'a, F: RichField + Extendable, const D: usize> let count = looking_tables .iter() - .filter(|looking_table| looking_table.table as usize == table) + .filter(|looking_table| looking_table.table == table) .count(); let cols_filts = looking_tables.iter().filter_map(|looking_table| { - if looking_table.table as usize == table { + if looking_table.table == table { Some((&looking_table.columns, &looking_table.filter)) } else { None @@ -1091,15 +1093,15 @@ impl<'a, F: RichField + Extendable, const D: usize> }); } - let (looked_z, looked_z_next) = ctl_zs[looked_table.table as usize] - [total_num_helper_cols_by_table[looked_table.table as usize] - + z_indices[looked_table.table as usize]]; + let (looked_z, looked_z_next) = ctl_zs[looked_table.table] + [total_num_helper_cols_by_table[looked_table.table] + + z_indices[looked_table.table]]; - z_indices[looked_table.table as usize] += 1; + z_indices[looked_table.table] += 1; let columns = vec![&looked_table.columns[..]]; let filter = vec![looked_table.filter.clone()]; - ctl_vars_per_table[looked_table.table as usize].push(Self { + ctl_vars_per_table[looked_table.table].push(Self { helper_columns: vec![], local_z: *looked_z, next_z: *looked_z_next, @@ -1281,7 +1283,7 @@ pub(crate) struct CtlCheckVarsTarget { impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { /// Circuit version of `from_proofs`. Extracts the `CtlCheckVarsTarget` for each STARK. pub(crate) fn from_proof( - table: Table, + table: TableIdx, proof: &StarkProofTarget, cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, @@ -1544,9 +1546,13 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< } /// Verifies all cross-table lookups. -pub(crate) fn verify_cross_table_lookups, const D: usize>( +pub(crate) fn verify_cross_table_lookups< + F: RichField + Extendable, + const D: usize, + const N: usize, +>( cross_table_lookups: &[CrossTableLookup], - ctl_zs_first: [Vec; NUM_TABLES], + ctl_zs_first: [Vec; N], ctl_extra_looking_sums: Vec>, config: &StarkConfig, ) -> Result<()> { @@ -1560,12 +1566,12 @@ pub(crate) fn verify_cross_table_lookups, const D: ) in cross_table_lookups.iter().enumerate() { // Get elements looking into `looked_table` that are not associated to any STARK. - let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table as usize]; + let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table]; // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { - if !filtered_looking_tables.contains(&(table.table as usize)) { - filtered_looking_tables.push(table.table as usize); + if !filtered_looking_tables.contains(&(table.table)) { + filtered_looking_tables.push(table.table); } } for c in 0..config.num_challenges { @@ -1578,7 +1584,7 @@ pub(crate) fn verify_cross_table_lookups, const D: + extra_sum_vec[c]; // Get the looked table CTL polynomial opening. - let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); + let looked_z = *ctl_zs_openings[looked_table.table].next().unwrap(); // Ensure that the combination of looking table openings is equal to the looked table opening. ensure!( looking_zs_sum == looked_z, @@ -1593,10 +1599,14 @@ pub(crate) fn verify_cross_table_lookups, const D: } /// Circuit version of `verify_cross_table_lookups`. Verifies all cross-table lookups. -pub(crate) fn verify_cross_table_lookups_circuit, const D: usize>( +pub(crate) fn verify_cross_table_lookups_circuit< + F: RichField + Extendable, + const D: usize, + const N: usize, +>( builder: &mut CircuitBuilder, cross_table_lookups: Vec>, - ctl_zs_first: [Vec; NUM_TABLES], + ctl_zs_first: [Vec; N], ctl_extra_looking_sums: Vec>, inner_config: &StarkConfig, ) { @@ -1607,12 +1617,12 @@ pub(crate) fn verify_cross_table_lookups_circuit, c } in cross_table_lookups.into_iter() { // Get elements looking into `looked_table` that are not associated to any STARK. - let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table as usize]; + let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table]; // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { - if !filtered_looking_tables.contains(&(table.table as usize)) { - filtered_looking_tables.push(table.table as usize); + if !filtered_looking_tables.contains(&(table.table)) { + filtered_looking_tables.push(table.table); } } for c in 0..inner_config.num_challenges { @@ -1626,7 +1636,7 @@ pub(crate) fn verify_cross_table_lookups_circuit, c looking_zs_sum = builder.add(looking_zs_sum, extra_sum_vec[c]); // Get the looked table CTL polynomial opening. - let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); + let looked_z = *ctl_zs_openings[looked_table.table].next().unwrap(); // Verify that the combination of looking table openings is equal to the looked table opening. builder.connect(looked_z, looking_zs_sum); } @@ -1708,7 +1718,7 @@ pub(crate) mod testutils { table: &TableWithColumns, multiset: &mut MultiSet, ) { - let trace = &trace_poly_values[table.table as usize]; + let trace = &trace_poly_values[table.table]; for i in 0..trace[0].len() { let filter = if let Some(combin) = &table.filter { combin.eval_table(trace, i) @@ -1721,7 +1731,10 @@ pub(crate) mod testutils { .iter() .map(|c| c.eval_table(trace, i)) .collect::>(); - multiset.entry(row).or_default().push((table.table, i)); + multiset + .entry(row) + .or_default() + .push((Table::all()[table.table], i)); } else { assert_eq!(filter, F::ZERO, "Non-binary filter?") } diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index c7a705c37a..2e57ea3026 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -419,49 +419,49 @@ where let arithmetic = RecursiveCircuitsForTable::new( Table::Arithmetic, &all_stark.arithmetic_stark, - degree_bits_ranges[Table::Arithmetic as usize].clone(), + degree_bits_ranges[*Table::Arithmetic].clone(), &all_stark.cross_table_lookups, stark_config, ); let byte_packing = RecursiveCircuitsForTable::new( Table::BytePacking, &all_stark.byte_packing_stark, - degree_bits_ranges[Table::BytePacking as usize].clone(), + degree_bits_ranges[*Table::BytePacking].clone(), &all_stark.cross_table_lookups, stark_config, ); let cpu = RecursiveCircuitsForTable::new( Table::Cpu, &all_stark.cpu_stark, - degree_bits_ranges[Table::Cpu as usize].clone(), + degree_bits_ranges[*Table::Cpu].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak = RecursiveCircuitsForTable::new( Table::Keccak, &all_stark.keccak_stark, - degree_bits_ranges[Table::Keccak as usize].clone(), + degree_bits_ranges[*Table::Keccak].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak_sponge = RecursiveCircuitsForTable::new( Table::KeccakSponge, &all_stark.keccak_sponge_stark, - degree_bits_ranges[Table::KeccakSponge as usize].clone(), + degree_bits_ranges[*Table::KeccakSponge].clone(), &all_stark.cross_table_lookups, stark_config, ); let logic = RecursiveCircuitsForTable::new( Table::Logic, &all_stark.logic_stark, - degree_bits_ranges[Table::Logic as usize].clone(), + degree_bits_ranges[*Table::Logic].clone(), &all_stark.cross_table_lookups, stark_config, ); let memory = RecursiveCircuitsForTable::new( Table::Memory, &all_stark.memory_stark, - degree_bits_ranges[Table::Memory as usize].clone(), + degree_bits_ranges[*Table::Memory].clone(), &all_stark.cross_table_lookups, stark_config, ); @@ -574,7 +574,7 @@ where vec![vec![builder.zero(); stark_config.num_challenges]; NUM_TABLES]; // Memory - extra_looking_sums[Table::Memory as usize] = (0..stark_config.num_challenges) + extra_looking_sums[*Table::Memory] = (0..stark_config.num_challenges) .map(|c| { get_memory_extra_looking_sum_circuit( &mut builder, @@ -585,7 +585,7 @@ where .collect_vec(); // Verify the CTL checks. - verify_cross_table_lookups_circuit::( + verify_cross_table_lookups_circuit::( &mut builder, all_cross_table_lookups(), pis.map(|p| p.ctl_zs_first), diff --git a/evm/src/prover.rs b/evm/src/prover.rs index e642602315..8ca00d7659 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -131,7 +131,7 @@ where let ctl_data_per_table = timed!( timing, "compute CTL data", - cross_table_lookup_data::( + cross_table_lookup_data::( &trace_poly_values, &all_stark.cross_table_lookups, &ctl_challenges, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index f3065e10f1..0d8e7367ce 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -232,7 +232,7 @@ where let (total_num_helpers, num_ctl_zs, num_helpers_by_ctl) = CrossTableLookup::num_ctl_helpers_zs_all( cross_table_lookups, - table, + *table, inner_config.num_challenges, stark.constraint_degree(), ); @@ -266,7 +266,7 @@ where }; let ctl_vars = CtlCheckVarsTarget::from_proof( - table, + *table, &proof_target, cross_table_lookups, &ctl_challenges_target, diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index a173be5b69..106f6983ae 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -138,7 +138,7 @@ where .map(|i| get_memory_extra_looking_sum(&public_values, ctl_challenges.challenges[i])) .collect_vec(); - verify_cross_table_lookups::( + verify_cross_table_lookups::( cross_table_lookups, all_proof .stark_proofs From 635796365406419372717259d70c70d6dc6fdaae Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:19:49 -0500 Subject: [PATCH 130/175] Reorganize lookup / ctl modules (#1495) * Reorganize lookup / ctl modules * Apply review --- evm/src/arithmetic/arithmetic_stark.rs | 4 +- evm/src/byte_packing/byte_packing_stark.rs | 3 +- evm/src/cpu/cpu_stark.rs | 3 +- evm/src/cross_table_lookup.rs | 624 +----------------- evm/src/keccak/columns.rs | 2 +- evm/src/keccak/keccak_stark.rs | 7 +- evm/src/keccak_sponge/keccak_sponge_stark.rs | 3 +- evm/src/logic.rs | 2 +- evm/src/lookup.rs | 631 ++++++++++++++++++- evm/src/memory/memory_stark.rs | 3 +- evm/src/recursive_verifier.rs | 6 +- evm/src/verifier.rs | 4 +- 12 files changed, 647 insertions(+), 645 deletions(-) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 5cd0697392..dcf966ee59 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -17,9 +17,9 @@ use crate::all_stark::Table; use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; +use crate::cross_table_lookup::TableWithColumns; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::Lookup; +use crate::lookup::{Column, Filter, Lookup}; use crate::stark::Stark; /// Creates a vector of `Columns` to link the 16-bit columns of the arithmetic table, diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index 03449d0d12..6520b8f7ae 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -45,9 +45,8 @@ use crate::byte_packing::columns::{ NUM_COLUMNS, RANGE_COUNTER, RC_FREQUENCIES, TIMESTAMP, }; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::Lookup; +use crate::lookup::{Column, Filter, Lookup}; use crate::stark::Stark; use crate::witness::memory::MemoryAddress; diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 114525e99c..9b6cb32949 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -20,8 +20,9 @@ use crate::cpu::{ byte_unpacking, clock, contextops, control_flow, decode, dup_swap, gas, jumps, membus, memio, modfp254, pc, push0, shift, simple_logic, stack, syscalls_exceptions, }; -use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; +use crate::cross_table_lookup::TableWithColumns; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +use crate::lookup::{Column, Filter, Lookup}; use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 28d14ca0d5..fe5bf035e1 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -56,373 +56,13 @@ use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::StarkEvaluationFrame; +use crate::lookup::{ + eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, ColumnFilter, + Filter, GrandProductChallenge, +}; use crate::proof::{StarkProofTarget, StarkProofWithMetadata}; use crate::stark::Stark; -/// Represent two linear combination of columns, corresponding to the current and next row values. -/// Each linear combination is represented as: -/// - a vector of `(usize, F)` corresponding to the column number and the associated multiplicand -/// - the constant of the linear combination. -#[derive(Clone, Debug)] -pub(crate) struct Column { - linear_combination: Vec<(usize, F)>, - next_row_linear_combination: Vec<(usize, F)>, - constant: F, -} - -impl Column { - /// Returns the representation of a single column in the current row. - pub(crate) fn single(c: usize) -> Self { - Self { - linear_combination: vec![(c, F::ONE)], - next_row_linear_combination: vec![], - constant: F::ZERO, - } - } - - /// Returns multiple single columns in the current row. - pub(crate) fn singles>>( - cs: I, - ) -> impl Iterator { - cs.into_iter().map(|c| Self::single(*c.borrow())) - } - - /// Returns the representation of a single column in the next row. - pub(crate) fn single_next_row(c: usize) -> Self { - Self { - linear_combination: vec![], - next_row_linear_combination: vec![(c, F::ONE)], - constant: F::ZERO, - } - } - - /// Returns multiple single columns for the next row. - pub(crate) fn singles_next_row>>( - cs: I, - ) -> impl Iterator { - cs.into_iter().map(|c| Self::single_next_row(*c.borrow())) - } - - /// Returns a linear combination corresponding to a constant. - pub(crate) fn constant(constant: F) -> Self { - Self { - linear_combination: vec![], - next_row_linear_combination: vec![], - constant, - } - } - - /// Returns a linear combination corresponding to 0. - pub(crate) fn zero() -> Self { - Self::constant(F::ZERO) - } - - /// Returns a linear combination corresponding to 1. - pub(crate) fn one() -> Self { - Self::constant(F::ONE) - } - - /// Given an iterator of `(usize, F)` and a constant, returns the association linear combination of columns for the current row. - pub(crate) fn linear_combination_with_constant>( - iter: I, - constant: F, - ) -> Self { - let v = iter.into_iter().collect::>(); - assert!(!v.is_empty()); - debug_assert_eq!( - v.iter().map(|(c, _)| c).unique().count(), - v.len(), - "Duplicate columns." - ); - Self { - linear_combination: v, - next_row_linear_combination: vec![], - constant, - } - } - - /// Given an iterator of `(usize, F)` and a constant, returns the associated linear combination of columns for the current and the next rows. - pub(crate) fn linear_combination_and_next_row_with_constant< - I: IntoIterator, - >( - iter: I, - next_row_iter: I, - constant: F, - ) -> Self { - let v = iter.into_iter().collect::>(); - let next_row_v = next_row_iter.into_iter().collect::>(); - - assert!(!v.is_empty() || !next_row_v.is_empty()); - debug_assert_eq!( - v.iter().map(|(c, _)| c).unique().count(), - v.len(), - "Duplicate columns." - ); - debug_assert_eq!( - next_row_v.iter().map(|(c, _)| c).unique().count(), - next_row_v.len(), - "Duplicate columns." - ); - - Self { - linear_combination: v, - next_row_linear_combination: next_row_v, - constant, - } - } - - /// Returns a linear combination of columns, with no additional constant. - pub(crate) fn linear_combination>(iter: I) -> Self { - Self::linear_combination_with_constant(iter, F::ZERO) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: - /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n. - pub(crate) fn le_bits>>(cs: I) -> Self { - Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers())) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: - /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n + k where `k` is an - /// additional constant. - pub(crate) fn le_bits_with_constant>>( - cs: I, - constant: F, - ) -> Self { - Self::linear_combination_with_constant( - cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers()), - constant, - ) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bytes in little endian order: - /// returns the representation of c_0 + 256 * c_1 + ... + 256^n * c_n. - pub(crate) fn le_bytes>>(cs: I) -> Self { - Self::linear_combination( - cs.into_iter() - .map(|c| *c.borrow()) - .zip(F::from_canonical_u16(256).powers()), - ) - } - - /// Given an iterator of columns, returns the representation of their sum. - pub(crate) fn sum>>(cs: I) -> Self { - Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(repeat(F::ONE))) - } - - /// Given the column values for the current row, returns the evaluation of the linear combination. - pub(crate) fn eval(&self, v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.linear_combination - .iter() - .map(|&(c, f)| v[c] * FE::from_basefield(f)) - .sum::

() - + FE::from_basefield(self.constant) - } - - /// Given the column values for the current and next rows, evaluates the current and next linear combinations and returns their sum. - pub(crate) fn eval_with_next(&self, v: &[P], next_v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.linear_combination - .iter() - .map(|&(c, f)| v[c] * FE::from_basefield(f)) - .sum::

() - + self - .next_row_linear_combination - .iter() - .map(|&(c, f)| next_v[c] * FE::from_basefield(f)) - .sum::

() - + FE::from_basefield(self.constant) - } - - /// Evaluate on a row of a table given in column-major form. - pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - let mut res = self - .linear_combination - .iter() - .map(|&(c, f)| table[c].values[row] * f) - .sum::() - + self.constant; - - // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. - // If CTLs are correctly written, the filter should be 0 in that case anyway. - if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { - res += self - .next_row_linear_combination - .iter() - .map(|&(c, f)| table[c].values[row + 1] * f) - .sum::(); - } - - res - } - - /// Evaluates the column on all rows. - pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { - let length = table[0].len(); - (0..length) - .map(|row| self.eval_table(table, row)) - .collect::>() - } - - /// Circuit version of `eval`: Given a row's targets, returns their linear combination. - pub(crate) fn eval_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let pairs = self - .linear_combination - .iter() - .map(|&(c, f)| { - ( - v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }) - .collect::>(); - let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); - builder.inner_product_extension(F::ONE, constant, pairs) - } - - /// Circuit version of `eval_with_next`: - /// Given the targets of the current and next row, returns the sum of their linear combinations. - pub(crate) fn eval_with_next_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - next_v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let mut pairs = self - .linear_combination - .iter() - .map(|&(c, f)| { - ( - v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }) - .collect::>(); - let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| { - ( - next_v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }); - pairs.extend(next_row_pairs); - let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); - builder.inner_product_extension(F::ONE, constant, pairs) - } -} - -/// Represents a CTL filter, which evaluates to 1 if the row must be considered for the CTL and 0 otherwise. -/// It's an arbitrary degree 2 combination of columns: `products` are the degree 2 terms, and `constants` are -/// the degree 1 terms. -#[derive(Clone, Debug)] -pub(crate) struct Filter { - products: Vec<(Column, Column)>, - constants: Vec>, -} - -impl Filter { - pub(crate) fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { - Self { - products, - constants, - } - } - - /// Returns a filter made of a single column. - pub(crate) fn new_simple(col: Column) -> Self { - Self { - products: vec![], - constants: vec![col], - } - } - - /// Given the column values for the current and next rows, evaluates the filter. - pub(crate) fn eval_filter(&self, v: &[P], next_v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.products - .iter() - .map(|(col1, col2)| col1.eval_with_next(v, next_v) * col2.eval_with_next(v, next_v)) - .sum::

() - + self - .constants - .iter() - .map(|col| col.eval_with_next(v, next_v)) - .sum::

() - } - - /// Circuit version of `eval_filter`: - /// Given the column values for the current and next rows, evaluates the filter. - pub(crate) fn eval_filter_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - next_v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let prods = self - .products - .iter() - .map(|(col1, col2)| { - let col1_eval = col1.eval_with_next_circuit(builder, v, next_v); - let col2_eval = col2.eval_with_next_circuit(builder, v, next_v); - builder.mul_extension(col1_eval, col2_eval) - }) - .collect::>(); - - let consts = self - .constants - .iter() - .map(|col| col.eval_with_next_circuit(builder, v, next_v)) - .collect::>(); - - let prods = builder.add_many_extension(prods); - let consts = builder.add_many_extension(consts); - builder.add_extension(prods, consts) - } - - /// Evaluate on a row of a table given in column-major form. - pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - self.products - .iter() - .map(|(col1, col2)| col1.eval_table(table, row) * col2.eval_table(table, row)) - .sum::() - + self - .constants - .iter() - .map(|col| col.eval_table(table, row)) - .sum() - } - - pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { - let length = table[0].len(); - - (0..length) - .map(|row| self.eval_table(table, row)) - .collect::>() - } -} - /// An alias for `usize`, to represent the index of a STARK table in a multi-STARK setting. pub(crate) type TableIdx = usize; @@ -578,52 +218,6 @@ impl<'a, F: Field> CtlData<'a, F> { } } -/// Randomness for a single instance of a permutation check protocol. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) struct GrandProductChallenge { - /// Randomness used to combine multiple columns into one. - pub(crate) beta: T, - /// Random offset that's added to the beta-reduced column values. - pub(crate) gamma: T, -} - -impl GrandProductChallenge { - pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( - &self, - terms: T, - ) -> P - where - FE: FieldExtension, - P: PackedField, - T::IntoIter: DoubleEndedIterator, - { - reduce_with_powers(terms, FE::from_basefield(self.beta)) + FE::from_basefield(self.gamma) - } -} - -impl GrandProductChallenge { - pub(crate) fn combine_circuit, const D: usize>( - &self, - builder: &mut CircuitBuilder, - terms: &[ExtensionTarget], - ) -> ExtensionTarget { - let reduced = reduce_with_powers_ext_circuit(builder, terms, self.beta); - let gamma = builder.convert_to_ext(self.gamma); - builder.add_extension(reduced, gamma) - } -} - -impl GrandProductChallenge { - pub(crate) fn combine_base_circuit, const D: usize>( - &self, - builder: &mut CircuitBuilder, - terms: &[Target], - ) -> Target { - let reduced = reduce_with_powers_circuit(builder, terms, self.beta); - builder.add(reduced, self.gamma) - } -} - /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone, Eq, PartialEq, Debug)] pub struct GrandProductChallengeSet { @@ -805,101 +399,6 @@ pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize, const N: ctl_data_per_table } -type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); - -/// Given a STARK's trace, and the data associated to one lookup (either CTL or range check), -/// returns the associated helper polynomials. -pub(crate) fn get_helper_cols( - trace: &[PolynomialValues], - degree: usize, - columns_filters: &[ColumnFilter], - challenge: GrandProductChallenge, - constraint_degree: usize, -) -> Vec> { - let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); - - let mut helper_columns = Vec::with_capacity(num_helper_columns); - - let mut filter_index = 0; - for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { - let (first_col, first_filter) = cols_filts.next().unwrap(); - - let mut filter_col = Vec::with_capacity(degree); - let first_combined = (0..degree) - .map(|d| { - let f = if let Some(filter) = first_filter { - let f = filter.eval_table(trace, d); - filter_col.push(f); - f - } else { - filter_col.push(F::ONE); - F::ONE - }; - if f.is_one() { - let evals = first_col - .iter() - .map(|c| c.eval_table(trace, d)) - .collect::>(); - challenge.combine(evals.iter()) - } else { - assert_eq!(f, F::ZERO, "Non-binary filter?"); - // Dummy value. Cannot be zero since it will be batch-inverted. - F::ONE - } - }) - .collect::>(); - - let mut acc = F::batch_multiplicative_inverse(&first_combined); - for d in 0..degree { - if filter_col[d].is_zero() { - acc[d] = F::ZERO; - } - } - - for (col, filt) in cols_filts { - let mut filter_col = Vec::with_capacity(degree); - let mut combined = (0..degree) - .map(|d| { - let f = if let Some(filter) = filt { - let f = filter.eval_table(trace, d); - filter_col.push(f); - f - } else { - filter_col.push(F::ONE); - F::ONE - }; - if f.is_one() { - let evals = col - .iter() - .map(|c| c.eval_table(trace, d)) - .collect::>(); - challenge.combine(evals.iter()) - } else { - assert_eq!(f, F::ZERO, "Non-binary filter?"); - // Dummy value. Cannot be zero since it will be batch-inverted. - F::ONE - } - }) - .collect::>(); - - combined = F::batch_multiplicative_inverse(&combined); - - for d in 0..degree { - if filter_col[d].is_zero() { - combined[d] = F::ZERO; - } - } - - batch_add_inplace(&mut acc, &combined); - } - - helper_columns.push(acc.into()); - } - assert_eq!(helper_columns.len(), num_helper_columns); - - helper_columns -} - /// Computes helper columns and Z polynomials for all looking tables /// of one cross-table lookup (i.e. for one looked table). fn ctl_helper_zs_cols( @@ -1115,61 +614,6 @@ impl<'a, F: RichField + Extendable, const D: usize> } } -/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. -pub(crate) fn eval_helper_columns( - filter: &[Option>], - columns: &[Vec

], - local_values: &[P], - next_values: &[P], - helper_columns: &[P], - constraint_degree: usize, - challenges: &GrandProductChallenge, - consumer: &mut ConstraintConsumer

, -) where - F: RichField + Extendable, - FE: FieldExtension, - P: PackedField, -{ - if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - - match chunk.len() { - 2 => { - let combin0 = challenges.combine(&chunk[0]); - let combin1 = challenges.combine(chunk[1].iter()); - - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter(local_values, next_values) - } else { - P::ONES - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; - - consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); - } - 1 => { - let combin = challenges.combine(&chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; - consumer.constraint(combin * h - f0); - } - - _ => todo!("Allow other constraint degrees"), - } - } - } -} - /// Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. @@ -1378,66 +822,6 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { } } -/// Circuit version of `eval_helper_columns`. -/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. -pub(crate) fn eval_helper_columns_circuit, const D: usize>( - builder: &mut CircuitBuilder, - filter: &[Option>], - columns: &[Vec>], - local_values: &[ExtensionTarget], - next_values: &[ExtensionTarget], - helper_columns: &[ExtensionTarget], - constraint_degree: usize, - challenges: &GrandProductChallenge, - consumer: &mut RecursiveConstraintConsumer, -) { - if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - - let one = builder.one_extension(); - match chunk.len() { - 2 => { - let combin0 = challenges.combine_circuit(builder, &chunk[0]); - let combin1 = challenges.combine_circuit(builder, &chunk[1]); - - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - - let constr = builder.mul_sub_extension(combin0, h, f0); - let constr = builder.mul_extension(constr, combin1); - let f1_constr = builder.mul_extension(f1, combin0); - let constr = builder.sub_extension(constr, f1_constr); - - consumer.constraint(builder, constr); - } - 1 => { - let combin = challenges.combine_circuit(builder, &chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let constr = builder.mul_sub_extension(combin, h, f0); - consumer.constraint(builder, constr); - } - - _ => todo!("Allow other constraint degrees"), - } - } - } -} - /// Circuit version of `eval_cross_table_lookup_checks`. Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. diff --git a/evm/src/keccak/columns.rs b/evm/src/keccak/columns.rs index bcdf0190ba..eedba41c0f 100644 --- a/evm/src/keccak/columns.rs +++ b/evm/src/keccak/columns.rs @@ -1,7 +1,7 @@ use plonky2::field::types::Field; -use crate::cross_table_lookup::Column; use crate::keccak::keccak_stark::{NUM_INPUTS, NUM_ROUNDS}; +use crate::lookup::Column; /// A register which is set to 1 if we are in the `i`th round, otherwise 0. pub(crate) const fn reg_step(i: usize) -> usize { diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 26edc40ae0..771c9b4371 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -13,7 +13,6 @@ use plonky2::util::timing::TimingTree; use super::columns::reg_input_limb; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::keccak::columns::{ reg_a, reg_a_prime, reg_a_prime_prime, reg_a_prime_prime_0_0_bit, reg_a_prime_prime_prime, @@ -24,6 +23,7 @@ use crate::keccak::logic::{ andn, andn_gen, andn_gen_circuit, xor, xor3_gen, xor3_gen_circuit, xor_gen, xor_gen_circuit, }; use crate::keccak::round_flags::{eval_round_flags, eval_round_flags_recursively}; +use crate::lookup::{Column, Filter}; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; @@ -630,9 +630,8 @@ mod tests { use super::*; use crate::config::StarkConfig; - use crate::cross_table_lookup::{ - CtlData, CtlZData, GrandProductChallenge, GrandProductChallengeSet, - }; + use crate::cross_table_lookup::{CtlData, CtlZData, GrandProductChallengeSet}; + use crate::lookup::GrandProductChallenge; use crate::prover::prove_single_table; use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index e63c6f5aa7..ddf2bca00e 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -17,10 +17,9 @@ use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::kernel::keccak_util::keccakf_u32s; -use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::keccak_sponge::columns::*; -use crate::lookup::Lookup; +use crate::lookup::{Column, Filter, Lookup}; use crate::stark::Stark; use crate::witness::memory::MemoryAddress; diff --git a/evm/src/logic.rs b/evm/src/logic.rs index c03991af79..fa83fa94c1 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -13,9 +13,9 @@ use plonky2::util::timing::TimingTree; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::logic::columns::NUM_COLUMNS; +use crate::lookup::{Column, Filter}; use crate::stark::Stark; use crate::util::{limb_from_bits_le, limb_from_bits_le_recursive, trace_rows_to_poly_values}; diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index e0d827bd3b..b16706ae0a 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -1,3 +1,7 @@ +use core::borrow::Borrow; +use core::fmt::Debug; +use core::iter::repeat; + use itertools::Itertools; use num_bigint::BigUint; use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; @@ -9,16 +13,381 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::plonk_common::{ + reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, +}; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{ - eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, Filter, - GrandProductChallenge, -}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::stark::Stark; +/// Represents a filter, which evaluates to 1 if the row must be considered and 0 if it should be ignored. +/// It's an arbitrary degree 2 combination of columns: `products` are the degree 2 terms, and `constants` are +/// the degree 1 terms. +#[derive(Clone, Debug)] +pub(crate) struct Filter { + products: Vec<(Column, Column)>, + constants: Vec>, +} + +impl Filter { + pub(crate) fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { + Self { + products, + constants, + } + } + + /// Returns a filter made of a single column. + pub(crate) fn new_simple(col: Column) -> Self { + Self { + products: vec![], + constants: vec![col], + } + } + + /// Given the column values for the current and next rows, evaluates the filter. + pub(crate) fn eval_filter(&self, v: &[P], next_v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.products + .iter() + .map(|(col1, col2)| col1.eval_with_next(v, next_v) * col2.eval_with_next(v, next_v)) + .sum::

() + + self + .constants + .iter() + .map(|col| col.eval_with_next(v, next_v)) + .sum::

() + } + + /// Circuit version of `eval_filter`: + /// Given the column values for the current and next rows, evaluates the filter. + pub(crate) fn eval_filter_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + next_v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let prods = self + .products + .iter() + .map(|(col1, col2)| { + let col1_eval = col1.eval_with_next_circuit(builder, v, next_v); + let col2_eval = col2.eval_with_next_circuit(builder, v, next_v); + builder.mul_extension(col1_eval, col2_eval) + }) + .collect::>(); + + let consts = self + .constants + .iter() + .map(|col| col.eval_with_next_circuit(builder, v, next_v)) + .collect::>(); + + let prods = builder.add_many_extension(prods); + let consts = builder.add_many_extension(consts); + builder.add_extension(prods, consts) + } + + /// Evaluate on a row of a table given in column-major form. + pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { + self.products + .iter() + .map(|(col1, col2)| col1.eval_table(table, row) * col2.eval_table(table, row)) + .sum::() + + self + .constants + .iter() + .map(|col| col.eval_table(table, row)) + .sum() + } + + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } +} + +/// Represent two linear combination of columns, corresponding to the current and next row values. +/// Each linear combination is represented as: +/// - a vector of `(usize, F)` corresponding to the column number and the associated multiplicand +/// - the constant of the linear combination. +#[derive(Clone, Debug)] +pub(crate) struct Column { + linear_combination: Vec<(usize, F)>, + next_row_linear_combination: Vec<(usize, F)>, + constant: F, +} + +impl Column { + /// Returns the representation of a single column in the current row. + pub(crate) fn single(c: usize) -> Self { + Self { + linear_combination: vec![(c, F::ONE)], + next_row_linear_combination: vec![], + constant: F::ZERO, + } + } + + /// Returns multiple single columns in the current row. + pub(crate) fn singles>>( + cs: I, + ) -> impl Iterator { + cs.into_iter().map(|c| Self::single(*c.borrow())) + } + + /// Returns the representation of a single column in the next row. + pub(crate) fn single_next_row(c: usize) -> Self { + Self { + linear_combination: vec![], + next_row_linear_combination: vec![(c, F::ONE)], + constant: F::ZERO, + } + } + + /// Returns multiple single columns for the next row. + pub(crate) fn singles_next_row>>( + cs: I, + ) -> impl Iterator { + cs.into_iter().map(|c| Self::single_next_row(*c.borrow())) + } + + /// Returns a linear combination corresponding to a constant. + pub(crate) fn constant(constant: F) -> Self { + Self { + linear_combination: vec![], + next_row_linear_combination: vec![], + constant, + } + } + + /// Returns a linear combination corresponding to 0. + pub(crate) fn zero() -> Self { + Self::constant(F::ZERO) + } + + /// Returns a linear combination corresponding to 1. + pub(crate) fn one() -> Self { + Self::constant(F::ONE) + } + + /// Given an iterator of `(usize, F)` and a constant, returns the association linear combination of columns for the current row. + pub(crate) fn linear_combination_with_constant>( + iter: I, + constant: F, + ) -> Self { + let v = iter.into_iter().collect::>(); + assert!(!v.is_empty()); + debug_assert_eq!( + v.iter().map(|(c, _)| c).unique().count(), + v.len(), + "Duplicate columns." + ); + Self { + linear_combination: v, + next_row_linear_combination: vec![], + constant, + } + } + + /// Given an iterator of `(usize, F)` and a constant, returns the associated linear combination of columns for the current and the next rows. + pub(crate) fn linear_combination_and_next_row_with_constant< + I: IntoIterator, + >( + iter: I, + next_row_iter: I, + constant: F, + ) -> Self { + let v = iter.into_iter().collect::>(); + let next_row_v = next_row_iter.into_iter().collect::>(); + + assert!(!v.is_empty() || !next_row_v.is_empty()); + debug_assert_eq!( + v.iter().map(|(c, _)| c).unique().count(), + v.len(), + "Duplicate columns." + ); + debug_assert_eq!( + next_row_v.iter().map(|(c, _)| c).unique().count(), + next_row_v.len(), + "Duplicate columns." + ); + + Self { + linear_combination: v, + next_row_linear_combination: next_row_v, + constant, + } + } + + /// Returns a linear combination of columns, with no additional constant. + pub(crate) fn linear_combination>(iter: I) -> Self { + Self::linear_combination_with_constant(iter, F::ZERO) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: + /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n. + pub(crate) fn le_bits>>(cs: I) -> Self { + Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers())) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: + /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n + k where `k` is an + /// additional constant. + pub(crate) fn le_bits_with_constant>>( + cs: I, + constant: F, + ) -> Self { + Self::linear_combination_with_constant( + cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers()), + constant, + ) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bytes in little endian order: + /// returns the representation of c_0 + 256 * c_1 + ... + 256^n * c_n. + pub(crate) fn le_bytes>>(cs: I) -> Self { + Self::linear_combination( + cs.into_iter() + .map(|c| *c.borrow()) + .zip(F::from_canonical_u16(256).powers()), + ) + } + + /// Given an iterator of columns, returns the representation of their sum. + pub(crate) fn sum>>(cs: I) -> Self { + Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(repeat(F::ONE))) + } + + /// Given the column values for the current row, returns the evaluation of the linear combination. + pub(crate) fn eval(&self, v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.linear_combination + .iter() + .map(|&(c, f)| v[c] * FE::from_basefield(f)) + .sum::

() + + FE::from_basefield(self.constant) + } + + /// Given the column values for the current and next rows, evaluates the current and next linear combinations and returns their sum. + pub(crate) fn eval_with_next(&self, v: &[P], next_v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.linear_combination + .iter() + .map(|&(c, f)| v[c] * FE::from_basefield(f)) + .sum::

() + + self + .next_row_linear_combination + .iter() + .map(|&(c, f)| next_v[c] * FE::from_basefield(f)) + .sum::

() + + FE::from_basefield(self.constant) + } + + /// Evaluate on a row of a table given in column-major form. + pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { + let mut res = self + .linear_combination + .iter() + .map(|&(c, f)| table[c].values[row] * f) + .sum::() + + self.constant; + + // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. + // If the lookups are correctly written, the filter should be 0 in that case anyway. + if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { + res += self + .next_row_linear_combination + .iter() + .map(|&(c, f)| table[c].values[row + 1] * f) + .sum::(); + } + + res + } + + /// Evaluates the column on all rows. + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } + + /// Circuit version of `eval`: Given a row's targets, returns their linear combination. + pub(crate) fn eval_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let pairs = self + .linear_combination + .iter() + .map(|&(c, f)| { + ( + v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }) + .collect::>(); + let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); + builder.inner_product_extension(F::ONE, constant, pairs) + } + + /// Circuit version of `eval_with_next`: + /// Given the targets of the current and next row, returns the sum of their linear combinations. + pub(crate) fn eval_with_next_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + next_v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let mut pairs = self + .linear_combination + .iter() + .map(|&(c, f)| { + ( + v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }) + .collect::>(); + let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| { + ( + next_v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }); + pairs.extend(next_row_pairs); + let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); + builder.inner_product_extension(F::ONE, constant, pairs) + } +} + +pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); + pub struct Lookup { /// Columns whose values should be contained in the lookup table. /// These are the f_i(x) polynomials in the logUp paper. @@ -43,6 +412,52 @@ impl Lookup { } } +/// Randomness for a single instance of a permutation check protocol. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct GrandProductChallenge { + /// Randomness used to combine multiple columns into one. + pub(crate) beta: T, + /// Random offset that's added to the beta-reduced column values. + pub(crate) gamma: T, +} + +impl GrandProductChallenge { + pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( + &self, + terms: T, + ) -> P + where + FE: FieldExtension, + P: PackedField, + T::IntoIter: DoubleEndedIterator, + { + reduce_with_powers(terms, FE::from_basefield(self.beta)) + FE::from_basefield(self.gamma) + } +} + +impl GrandProductChallenge { + pub(crate) fn combine_circuit, const D: usize>( + &self, + builder: &mut CircuitBuilder, + terms: &[ExtensionTarget], + ) -> ExtensionTarget { + let reduced = reduce_with_powers_ext_circuit(builder, terms, self.beta); + let gamma = builder.convert_to_ext(self.gamma); + builder.add_extension(reduced, gamma) + } +} + +impl GrandProductChallenge { + pub(crate) fn combine_base_circuit, const D: usize>( + &self, + builder: &mut CircuitBuilder, + terms: &[Target], + ) -> Target { + let reduced = reduce_with_powers_circuit(builder, terms, self.beta); + builder.add(reduced, self.gamma) + } +} + /// logUp protocol from /// Compute the helper columns for the lookup argument. /// Given columns `f0,...,fk` and a column `t`, such that `∪fi ⊆ t`, and challenges `x`, @@ -129,6 +544,214 @@ pub(crate) fn lookup_helper_columns( helper_columns } +/// Given data associated to a lookup, check the associated helper polynomials. +pub(crate) fn eval_helper_columns( + filter: &[Option>], + columns: &[Vec

], + local_values: &[P], + next_values: &[P], + helper_columns: &[P], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, +{ + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + match chunk.len() { + 2 => { + let combin0 = challenges.combine(&chunk[0]); + let combin1 = challenges.combine(chunk[1].iter()); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter(local_values, next_values) + } else { + P::ONES + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + + consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); + } + 1 => { + let combin = challenges.combine(&chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + consumer.constraint(combin * h - f0); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + +/// Circuit version of `eval_helper_columns`. +/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. +pub(crate) fn eval_helper_columns_circuit, const D: usize>( + builder: &mut CircuitBuilder, + filter: &[Option>], + columns: &[Vec>], + local_values: &[ExtensionTarget], + next_values: &[ExtensionTarget], + helper_columns: &[ExtensionTarget], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut RecursiveConstraintConsumer, +) { + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + let one = builder.one_extension(); + match chunk.len() { + 2 => { + let combin0 = challenges.combine_circuit(builder, &chunk[0]); + let combin1 = challenges.combine_circuit(builder, &chunk[1]); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + + let constr = builder.mul_sub_extension(combin0, h, f0); + let constr = builder.mul_extension(constr, combin1); + let f1_constr = builder.mul_extension(f1, combin0); + let constr = builder.sub_extension(constr, f1_constr); + + consumer.constraint(builder, constr); + } + 1 => { + let combin = challenges.combine_circuit(builder, &chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let constr = builder.mul_sub_extension(combin, h, f0); + consumer.constraint(builder, constr); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + +/// Given a STARK's trace, and the data associated to one lookup (either CTL or range check), +/// returns the associated helper polynomials. +pub(crate) fn get_helper_cols( + trace: &[PolynomialValues], + degree: usize, + columns_filters: &[ColumnFilter], + challenge: GrandProductChallenge, + constraint_degree: usize, +) -> Vec> { + let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); + + let mut helper_columns = Vec::with_capacity(num_helper_columns); + + let mut filter_index = 0; + for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { + let (first_col, first_filter) = cols_filts.next().unwrap(); + + let mut filter_col = Vec::with_capacity(degree); + let first_combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = first_filter { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = first_col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + let mut acc = F::batch_multiplicative_inverse(&first_combined); + for d in 0..degree { + if filter_col[d].is_zero() { + acc[d] = F::ZERO; + } + } + + for (col, filt) in cols_filts { + let mut filter_col = Vec::with_capacity(degree); + let mut combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = filt { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + combined = F::batch_multiplicative_inverse(&combined); + + for d in 0..degree { + if filter_col[d].is_zero() { + combined[d] = F::ZERO; + } + } + + batch_add_inplace(&mut acc, &combined); + } + + helper_columns.push(acc.into()); + } + assert_eq!(helper_columns.len(), num_helper_columns); + + helper_columns +} + pub(crate) struct LookupCheckVars where F: Field, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 5adf46c7f8..44d2af6ae2 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -15,9 +15,8 @@ use plonky2_maybe_rayon::*; use super::segments::Segment; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::Lookup; +use crate::lookup::{Column, Filter, Lookup}; use crate::memory::columns::{ value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, FILTER, FREQUENCIES, INITIALIZE_AUX, IS_READ, NUM_COLUMNS, RANGE_CHECK, SEGMENT_FIRST_CHANGE, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 0d8e7367ce..c62e967e9c 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -31,11 +31,9 @@ use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cross_table_lookup::{ - CrossTableLookup, CtlCheckVarsTarget, GrandProductChallenge, GrandProductChallengeSet, -}; +use crate::cross_table_lookup::{CrossTableLookup, CtlCheckVarsTarget, GrandProductChallengeSet}; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::lookup::LookupCheckVarsTarget; +use crate::lookup::{GrandProductChallenge, LookupCheckVarsTarget}; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::proof::{ diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 106f6983ae..bb18f5434e 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -17,10 +17,10 @@ use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{ num_ctl_helper_columns_by_table, verify_cross_table_lookups, CtlCheckVars, - GrandProductChallenge, GrandProductChallengeSet, + GrandProductChallengeSet, }; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::lookup::LookupCheckVars; +use crate::lookup::{GrandProductChallenge, LookupCheckVars}; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::proof::{ From f3f7433c29a3e12db5d13ff1ff9f09c48b3ef441 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:21:38 -0500 Subject: [PATCH 131/175] Some cleanup (#1498) --- evm/src/all_stark.rs | 2 - evm/src/byte_packing/byte_packing_stark.rs | 3 +- evm/src/byte_packing/columns.rs | 4 -- evm/src/cpu/contextops.rs | 11 ++- evm/src/cpu/cpu_stark.rs | 2 +- evm/src/cpu/decode.rs | 1 - evm/src/cpu/dup_swap.rs | 1 - evm/src/cpu/kernel/assembler.rs | 1 - evm/src/cpu/kernel/constants/mod.rs | 1 - evm/src/cpu/kernel/cost_estimator.rs | 1 - evm/src/cpu/kernel/interpreter.rs | 72 +------------------ evm/src/cpu/kernel/tests/account_code.rs | 6 +- evm/src/cpu/kernel/tests/add11.rs | 8 +-- evm/src/cpu/kernel/tests/balance.rs | 2 +- evm/src/cpu/kernel/tests/exp.rs | 2 +- .../cpu/kernel/tests/kernel_consistency.rs | 3 - evm/src/cpu/kernel/tests/mpt/delete.rs | 2 +- evm/src/cpu/kernel/tests/mpt/hash.rs | 2 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 2 +- evm/src/cpu/kernel/tests/mpt/load.rs | 3 +- evm/src/cpu/kernel/tests/mpt/read.rs | 2 +- evm/src/cpu/kernel/tests/receipt.rs | 5 +- evm/src/cpu/syscalls_exceptions.rs | 3 +- evm/src/cross_table_lookup.rs | 19 ++--- evm/src/fixed_recursive_verifier.rs | 6 -- evm/src/generation/mod.rs | 11 +-- evm/src/generation/mpt.rs | 3 - evm/src/generation/prover_input.rs | 45 +----------- evm/src/generation/state.rs | 4 +- evm/src/lookup.rs | 13 +--- evm/src/memory/segments.rs | 1 - evm/src/proof.rs | 3 +- evm/src/prover.rs | 1 - evm/src/recursive_verifier.rs | 5 +- evm/src/stark.rs | 1 - evm/src/verifier.rs | 2 +- evm/src/witness/operation.rs | 2 +- evm/src/witness/transition.rs | 2 +- 38 files changed, 38 insertions(+), 219 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index d69ff302cf..cd7a2d3c38 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -1,7 +1,5 @@ -use core::iter; use core::ops::Deref; -use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index 6520b8f7ae..ff7a18c06d 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -38,7 +38,6 @@ use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; -use super::columns::BYTE_VALUES_RANGE; use super::NUM_BYTES; use crate::byte_packing::columns::{ index_len, value_bytes, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, IS_READ, LEN_INDICES_COLS, @@ -62,7 +61,7 @@ pub(crate) fn ctl_looked_data() -> Vec> { // obtain the corresponding limb. let outputs: Vec> = (0..8) .map(|i| { - let range = (value_bytes(i * 4)..value_bytes(i * 4) + 4); + let range = value_bytes(i * 4)..value_bytes(i * 4) + 4; Column::linear_combination( range .enumerate() diff --git a/evm/src/byte_packing/columns.rs b/evm/src/byte_packing/columns.rs index ae58afc440..cbed53de1d 100644 --- a/evm/src/byte_packing/columns.rs +++ b/evm/src/byte_packing/columns.rs @@ -33,10 +33,6 @@ pub(crate) const fn value_bytes(i: usize) -> usize { BYTES_VALUES_START + i } -/// Range of columns containing the bytes values. -pub(crate) const BYTE_VALUES_RANGE: Range = - BYTES_VALUES_START..BYTES_VALUES_START + NUM_BYTES; - /// The counter column (used for the range check) starts from 0 and increments. pub(crate) const RANGE_COUNTER: usize = BYTES_VALUES_START + NUM_BYTES; /// The frequencies column used in logUp. diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 7f46d1418f..ec4e5e5e6e 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -8,12 +8,9 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use super::columns::ops::OpsColumnsView; use super::cpu_stark::{disable_unused_channels, disable_unused_channels_circuit}; -use super::membus::NUM_GP_CHANNELS; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::memory::segments::Segment; -use crate::memory::VALUE_LIMBS; // If true, the instruction will keep the current context for the next row. // If false, next row's context is handled manually. @@ -88,7 +85,7 @@ fn eval_packed_get( // Context is scaled by 2^64, hence stored in the 3rd limb. yield_constr.constraint(filter * (new_stack_top[2] - lv.context)); - for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { + for (_, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -119,7 +116,7 @@ fn eval_ext_circuit_get, const D: usize>( yield_constr.constraint(builder, constr); } - for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { + for (_, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } @@ -151,7 +148,7 @@ fn eval_packed_set( // The next row's context is read from stack_top. yield_constr.constraint(filter * (stack_top[2] - nv.context)); - for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { + for (_, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -199,7 +196,7 @@ fn eval_ext_circuit_set, const D: usize>( let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); } - for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { + for (_, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 9b6cb32949..8bcada2f3b 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -22,7 +22,7 @@ use crate::cpu::{ }; use crate::cross_table_lookup::TableWithColumns; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Filter, Lookup}; +use crate::lookup::{Column, Filter}; use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs index 831f95ffaf..4c2c43221e 100644 --- a/evm/src/cpu/decode.rs +++ b/evm/src/cpu/decode.rs @@ -3,7 +3,6 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP}; diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 7b9d80f37c..1abec5fc61 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -6,7 +6,6 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; -use super::membus::NUM_GP_CHANNELS; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, MemoryChannelView}; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index 7a546e7703..2dc79d6111 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -431,7 +431,6 @@ fn push_target_size(target: &PushTarget) -> u8 { #[cfg(test)] mod tests { use super::*; - use crate::cpu::kernel::assembler::*; use crate::cpu::kernel::parser::parse; #[test] diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index f6f021db76..82c820f054 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use ethereum_types::U256; use hex_literal::hex; -use static_assertions::const_assert; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; diff --git a/evm/src/cpu/kernel/cost_estimator.rs b/evm/src/cpu/kernel/cost_estimator.rs index fdfeee5603..70cc726772 100644 --- a/evm/src/cpu/kernel/cost_estimator.rs +++ b/evm/src/cpu/kernel/cost_estimator.rs @@ -1,4 +1,3 @@ -use super::opcodes::get_opcode; use crate::cpu::kernel::assembler::BYTES_PER_OFFSET; use crate::cpu::kernel::ast::Item; use crate::cpu::kernel::ast::Item::*; diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 7ba5809fed..5a65ace3d5 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -2,7 +2,7 @@ use core::cmp::Ordering; use core::ops::Range; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap}; use anyhow::bail; use eth_trie_utils::partial_trie::PartialTrie; @@ -11,7 +11,6 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use super::assembler::BYTES_PER_OFFSET; -use super::utils::u256_from_bool; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -1393,70 +1392,6 @@ impl<'a> Interpreter<'a> { } } -// Computes the two's complement of the given integer. -fn two_complement(x: U256) -> U256 { - let flipped_bits = x ^ MINUS_ONE; - flipped_bits.overflowing_add(U256::one()).0 -} - -fn signed_cmp(x: U256, y: U256) -> Ordering { - let x_is_zero = x.is_zero(); - let y_is_zero = y.is_zero(); - - if x_is_zero && y_is_zero { - return Ordering::Equal; - } - - let x_is_pos = x.eq(&(x & SIGN_MASK)); - let y_is_pos = y.eq(&(y & SIGN_MASK)); - - if x_is_zero { - if y_is_pos { - return Ordering::Less; - } else { - return Ordering::Greater; - } - }; - - if y_is_zero { - if x_is_pos { - return Ordering::Greater; - } else { - return Ordering::Less; - } - }; - - match (x_is_pos, y_is_pos) { - (true, true) => x.cmp(&y), - (true, false) => Ordering::Greater, - (false, true) => Ordering::Less, - (false, false) => x.cmp(&y).reverse(), - } -} - -/// -1 in two's complement representation consists in all bits set to 1. -const MINUS_ONE: U256 = U256([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, -]); - -/// -2^255 in two's complement representation consists in the MSB set to 1. -const MIN_VALUE: U256 = U256([ - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, - 0x8000000000000000, -]); - -const SIGN_MASK: U256 = U256([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x7fffffffffffffff, -]); - fn get_mnemonic(opcode: u8) -> &'static str { match opcode { 0x00 => "STOP", @@ -1651,7 +1586,6 @@ fn get_mnemonic(opcode: u8) -> &'static str { } } -#[macro_use] macro_rules! unpack_address { ($addr:ident) => {{ let offset = $addr.low_u32() as usize; @@ -1729,8 +1663,8 @@ mod tests { interpreter.run()?; // sys_stop returns `success` and `cum_gas_used`, that we need to pop. - interpreter.pop(); - interpreter.pop(); + interpreter.pop().expect("Stack should not be empty"); + interpreter.pop().expect("Stack should not be empty"); assert_eq!(interpreter.stack(), &[0xff.into(), 0xff00.into()]); assert_eq!( diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 28b9ae7d97..5e2dddca9e 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256, U256}; @@ -327,8 +327,8 @@ fn sstore() -> Result<()> { // The first two elements in the stack are `success` and `leftover_gas`, // returned by the `sys_stop` opcode. - interpreter.pop(); - interpreter.pop(); + interpreter.pop().expect("Stack should not be empty"); + interpreter.pop().expect("Stack should not be empty"); // The code should have added an element to the storage of `to_account`. We run // `mpt_hash_state_trie` to check that. diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index c2725488a6..c5eb29397e 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -1,24 +1,18 @@ use std::collections::HashMap; use std::str::FromStr; -use anyhow::{anyhow, Result}; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H256}; use hex_literal::hex; use keccak_hash::keccak; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; -use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use crate::util::h2u; use crate::GenerationInputs; #[test] diff --git a/evm/src/cpu/kernel/tests/balance.rs b/evm/src/cpu/kernel/tests/balance.rs index 22b484e779..b393c05cf5 100644 --- a/evm/src/cpu/kernel/tests/balance.rs +++ b/evm/src/cpu/kernel/tests/balance.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256, U256}; use keccak_hash::keccak; diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs index 8fee16d97b..482c6b7216 100644 --- a/evm/src/cpu/kernel/tests/exp.rs +++ b/evm/src/cpu/kernel/tests/exp.rs @@ -3,7 +3,7 @@ use ethereum_types::U256; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::{run, run_interpreter, Interpreter}; +use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter}; #[test] fn test_exp() -> Result<()> { diff --git a/evm/src/cpu/kernel/tests/kernel_consistency.rs b/evm/src/cpu/kernel/tests/kernel_consistency.rs index ec9c649f0d..b02c11a234 100644 --- a/evm/src/cpu/kernel/tests/kernel_consistency.rs +++ b/evm/src/cpu/kernel/tests/kernel_consistency.rs @@ -1,9 +1,6 @@ use anyhow::Result; -use ethereum_types::U256; use crate::cpu::kernel::aggregator::{combined_kernel, KERNEL}; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; #[test] fn test_kernel_code_hash_consistency() -> Result<()> { diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs index 38342dc99c..0d4d5e71f7 100644 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm/src/cpu/kernel/tests/mpt/delete.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{BigEndianHash, H256, U512}; diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs index f432144af2..a06dd2a0b5 100644 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm/src/cpu/kernel/tests/mpt/hash.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::partial_trie::PartialTrie; use ethereum_types::{BigEndianHash, H256}; diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index 9c2fd50b24..19b82f74a2 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{BigEndianHash, H256}; diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs index 68ba209d8d..bff1d8cb39 100644 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ b/evm/src/cpu/kernel/tests/mpt/load.rs @@ -1,12 +1,11 @@ use std::str::FromStr; -use anyhow::{anyhow, Result}; +use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::HashedPartialTrie; use ethereum_types::{BigEndianHash, H256, U256}; use hex_literal::hex; -use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::cpu::kernel::interpreter::Interpreter; diff --git a/evm/src/cpu/kernel/tests/mpt/read.rs b/evm/src/cpu/kernel/tests/mpt/read.rs index d5913f9867..16206d1390 100644 --- a/evm/src/cpu/kernel/tests/mpt/read.rs +++ b/evm/src/cpu/kernel/tests/mpt/read.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use ethereum_types::BigEndianHash; use crate::cpu::kernel::aggregator::KERNEL; diff --git a/evm/src/cpu/kernel/tests/receipt.rs b/evm/src/cpu/kernel/tests/receipt.rs index d185948b0a..7d00cb2746 100644 --- a/evm/src/cpu/kernel/tests/receipt.rs +++ b/evm/src/cpu/kernel/tests/receipt.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use ethereum_types::{Address, U256}; use hex_literal::hex; use keccak_hash::keccak; @@ -408,9 +408,6 @@ fn test_mpt_insert_receipt() -> Result<()> { receipt.push(num_logs.into()); // num_logs receipt.extend(logs_0.clone()); - // First, we load all mpts. - let initial_stack: Vec = vec![retdest]; - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); initialize_mpts(&mut interpreter, &trie_inputs); diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index debae9dcd5..1dfdb8fa2c 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -7,7 +7,6 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; -use static_assertions::const_assert; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; @@ -227,7 +226,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( { let diff_syscall = builder.sub_extension(jumpdest_channel.addr_virtual, opcode_handler_addr_start); - let constr = builder.mul_extension((filter_syscall), diff_syscall); + let constr = builder.mul_extension(filter_syscall, diff_syscall); yield_constr.constraint(builder, constr); } { diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index fe5bf035e1..359b5309e8 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -27,15 +27,11 @@ //! is similar, but we provide not only `local_values` but also `next_values` -- corresponding to //! the current and next row values -- when computing the linear combinations. -use core::borrow::Borrow; use core::cmp::min; use core::fmt::Debug; -use core::iter::repeat; use anyhow::{ensure, Result}; -use hashbrown::HashMap; use itertools::Itertools; -use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -46,13 +42,9 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; -use plonky2::plonk::plonk_common::{ - reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, -}; use plonky2::util::ceil_div_usize; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; -use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::StarkEvaluationFrame; @@ -303,7 +295,7 @@ pub(crate) fn num_ctl_helper_columns_by_table( for (i, ctl) in ctls.iter().enumerate() { let CrossTableLookup { looking_tables, - looked_table, + looked_table: _, } = ctl; let mut num_by_table = [0; N]; @@ -349,7 +341,7 @@ pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize, const N: constraint_degree, ); - let mut z_looked = partial_sums( + let z_looked = partial_sums( &trace_poly_values[looked_table.table], &[(&looked_table.columns, &looked_table.filter)], challenge, @@ -412,7 +404,6 @@ fn ctl_helper_zs_cols( grouped_lookups .into_iter() .map(|(table, group)| { - let degree = all_stark_traces[table][0].len(); let columns_filters = group .map(|table| (&table.columns[..], &table.filter)) .collect::], &Option>)>>(); @@ -517,7 +508,7 @@ impl<'a, F: RichField + Extendable, const D: usize> } // Get all cross-table lookup polynomial openings for each STARK proof. - let mut ctl_zs = proofs + let ctl_zs = proofs .iter() .zip(num_lookup_columns) .map(|(p, &num_lookup)| { @@ -551,7 +542,7 @@ impl<'a, F: RichField + Extendable, const D: usize> } } - for (i, &table) in filtered_looking_tables.iter().enumerate() { + for &table in filtered_looking_tables.iter() { // We have first all the helper polynomials, then all the z polynomials. let (looking_z, looking_z_next) = ctl_zs[table][total_num_helper_cols_by_table[table] + z_indices[table]]; @@ -736,7 +727,7 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { num_helper_ctl_columns: &[usize], ) -> Vec { // Get all cross-table lookup polynomial openings for each STARK proof. - let mut ctl_zs = { + let ctl_zs = { let openings = &proof.openings; let ctl_zs = openings.auxiliary_polys.iter().skip(num_lookup_columns); let ctl_zs_next = openings diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 2e57ea3026..2df85b03de 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -1,7 +1,6 @@ use core::mem::{self, MaybeUninit}; use core::ops::Range; use std::collections::BTreeMap; -use std::path::Path; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -25,7 +24,6 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; use plonky2::recursion::dummy_circuit::cyclic_base_proof; -use plonky2::util::serialization::gate_serialization::default; use plonky2::util::serialization::{ Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, }; @@ -1100,11 +1098,8 @@ where /// ``` pub fn prove_root_after_initial_stark( &self, - all_stark: &AllStark, - config: &StarkConfig, all_proof: AllProof, table_circuits: &[(RecursiveCircuitsForTableSize, u8); NUM_TABLES], - timing: &mut TimingTree, abort_signal: Option>, ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut root_inputs = PartialWitness::new(); @@ -1113,7 +1108,6 @@ where let (table_circuit, index_verifier_data) = &table_circuits[table]; let stark_proof = &all_proof.stark_proofs[table]; - let original_degree_bits = stark_proof.proof.recover_degree_bits(config); let shrunk_proof = table_circuit.shrink(stark_proof, &all_proof.ctl_challenges)?; root_inputs.set_target( diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index eeaafa800b..b63f48a1c7 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -1,11 +1,8 @@ use std::collections::{BTreeSet, HashMap}; -use std::sync::atomic::AtomicBool; -use std::sync::Arc; use anyhow::anyhow; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use itertools::enumerate; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; @@ -22,16 +19,12 @@ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::opcodes::get_opcode; use crate::generation::state::GenerationState; use crate::generation::trie_extractor::{get_receipt_trie, get_state_trie, get_txn_trie}; use crate::memory::segments::Segment; use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; -use crate::prover::check_abort_signal; use crate::util::{h2u, u256_to_u8, u256_to_usize}; -use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::memory::{MemoryAddress, MemoryChannel}; use crate::witness::transition::transition; @@ -41,7 +34,6 @@ pub(crate) mod rlp; pub(crate) mod state; mod trie_extractor; -use self::mpt::{load_all_mpts, TrieRootPtrs}; use crate::witness::util::{mem_write_log, stack_peek}; /// Inputs needed for trace generation. @@ -282,7 +274,6 @@ pub fn generate_traces, const D: usize>( let gas_used_after = read_metadata(GlobalMetadata::BlockGasUsedAfter); let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); - let trie_root_ptrs = state.trie_root_ptrs; let extra_block_data = ExtraBlockData { checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, txn_number_before: inputs.txn_number_before, @@ -372,7 +363,7 @@ fn simulate_cpu_between_labels_and_get_user_jumps( } let pc = state.registers.program_counter; let context = state.registers.context; - let mut halt = state.registers.is_kernel + let halt = state.registers.is_kernel && pc == halt_pc && state.registers.context == initial_context; let Ok(opcode) = u256_to_u8(state.memory.get(MemoryAddress::new( diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index 71976bc012..ee530ddef5 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -9,13 +9,10 @@ use keccak_hash::keccak; use rlp::{Decodable, DecoderError, Encodable, PayloadInfo, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::generation::TrieInputs; -use crate::memory::segments::Segment; use crate::util::h2u; use crate::witness::errors::{ProgramError, ProverInputError}; -use crate::witness::memory::MemoryAddress; use crate::Node; #[derive(RlpEncodable, RlpDecodable, Debug)] diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 5c0aba1a4f..9662d6b6b7 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -1,20 +1,15 @@ -use core::cmp::min; use core::mem::transmute; use std::collections::{BTreeSet, HashMap}; use std::str::FromStr; use anyhow::{bail, Error}; use ethereum_types::{BigEndianHash, H256, U256, U512}; -use itertools::{enumerate, Itertools}; +use itertools::Itertools; use num_bigint::BigUint; -use plonky2::field::extension::Extendable; use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; -use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, @@ -252,7 +247,6 @@ impl GenerationState { /// Returns the next used jump address. fn run_next_jumpdest_table_address(&mut self) -> Result { let context = u256_to_usize(stack_peek(self, 0)? >> CONTEXT_SCALING_FACTOR)?; - let code_len = u256_to_usize(self.get_current_code_len()?.into()); if self.jumpdest_table.is_none() { self.generate_jumpdest_table()?; @@ -300,10 +294,6 @@ impl GenerationState { let checkpoint = self.checkpoint(); let memory = self.memory.clone(); - let code = self.get_current_code()?; - // We need to set the simulated jumpdest bits to one as otherwise - // the simulation will fail. - // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( "jumpdest_analysis_end", @@ -343,10 +333,6 @@ impl GenerationState { ))); } - fn get_current_code(&self) -> Result, ProgramError> { - self.get_code(self.registers.context) - } - fn get_code(&self, context: usize) -> Result, ProgramError> { let code_len = self.get_code_len(context)?; let code = (0..code_len) @@ -360,17 +346,6 @@ impl GenerationState { Ok(code) } - fn set_code_len(&mut self, len: usize) { - self.memory.set( - MemoryAddress::new( - self.registers.context, - Segment::ContextMetadata, - ContextMetadata::CodeSize.unscale(), - ), - len.into(), - ) - } - fn get_code_len(&self, context: usize) -> Result { let code_len = u256_to_usize(self.memory.get(MemoryAddress::new( context, @@ -379,22 +354,6 @@ impl GenerationState { )))?; Ok(code_len) } - - fn get_current_code_len(&self) -> Result { - self.get_code_len(self.registers.context) - } - - fn set_jumpdest_bits(&mut self, code: &[u8]) { - const JUMPDEST_OPCODE: u8 = 0x5b; - for (pos, opcode) in CodeIterator::new(code) { - if opcode == JUMPDEST_OPCODE { - self.memory.set( - MemoryAddress::new(self.registers.context, Segment::JumpdestBits, pos), - U256::one(), - ); - } - } - } } /// For all address in `jumpdest_table`, each bounded by `largest_address`, @@ -411,7 +370,7 @@ fn get_proofs_and_jumpdests( const PUSH32_OPCODE: u8 = 0x7f; let (proofs, _) = CodeIterator::until(code, largest_address + 1).fold( (vec![], 0), - |(mut proofs, acc), (pos, opcode)| { + |(mut proofs, acc), (pos, _opcode)| { let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { code[prefix_start..pos] .iter() diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 7f52eda7e4..a6df4b3331 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,10 +1,8 @@ -use std::collections::{BTreeSet, HashMap}; +use std::collections::HashMap; use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; -use plonky2::field::extension::Extendable; use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; use super::mpt::{load_all_mpts, TrieRootPtrs}; use super::TrieInputs; diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index b16706ae0a..f98814f9a1 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -4,7 +4,7 @@ use core::iter::repeat; use itertools::Itertools; use num_bigint::BigUint; -use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; +use plonky2::field::batch_util::batch_add_inplace; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -108,14 +108,6 @@ impl Filter { .map(|col| col.eval_table(table, row)) .sum() } - - pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { - let length = table[0].len(); - - (0..length) - .map(|row| self.eval_table(table, row)) - .collect::>() - } } /// Represent two linear combination of columns, corresponding to the current and next row values. @@ -480,7 +472,6 @@ pub(crate) fn lookup_helper_columns( assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); let num_helper_columns = lookup.num_helper_columns(constraint_degree); - let mut helper_columns: Vec> = Vec::with_capacity(num_helper_columns); let looking_cols = lookup .columns @@ -672,7 +663,6 @@ pub(crate) fn get_helper_cols( let mut helper_columns = Vec::with_capacity(num_helper_columns); - let mut filter_index = 0; for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { let (first_col, first_filter) = cols_filts.next().unwrap(); @@ -843,7 +833,6 @@ pub(crate) fn eval_ext_lookups_circuit< lookup_vars: LookupCheckVarsTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let one = builder.one_extension(); let degree = stark.constraint_degree(); let lookups = stark.lookups(); diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 6177d9962e..1b4bcbfbf7 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -1,5 +1,4 @@ use ethereum_types::U256; -use num::traits::AsPrimitive; pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 4cc1867738..33640458d6 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -1,4 +1,4 @@ -use ethereum_types::{Address, H160, H256, U256}; +use ethereum_types::{Address, H256, U256}; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::fri::oracle::PolynomialBatch; @@ -19,7 +19,6 @@ use serde::{Deserialize, Serialize}; use crate::all_stark::NUM_TABLES; use crate::config::StarkConfig; use crate::cross_table_lookup::GrandProductChallengeSet; -use crate::generation::mpt::TrieRootPtrs; use crate::util::{get_h160, get_h256, h2u}; /// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs. diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 8ca00d7659..faef64a033 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -35,7 +35,6 @@ use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -use crate::witness::errors::ProgramError; #[cfg(test)] use crate::{ cross_table_lookup::testutils::check_ctls, verifier::testutils::get_memory_extra_looking_values, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index c62e967e9c..5220ba32a7 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -2,7 +2,7 @@ use core::array::from_fn; use core::fmt::Debug; use anyhow::Result; -use ethereum_types::{BigEndianHash, H256, U256}; +use ethereum_types::{BigEndianHash, U256}; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; @@ -43,7 +43,7 @@ use crate::proof::{ TrieRootsTarget, }; use crate::stark::Stark; -use crate::util::{h256_limbs, h2u, u256_limbs, u256_to_u32, u256_to_u64}; +use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::witness::errors::ProgramError; @@ -348,7 +348,6 @@ fn verify_stark_proof_with_challenges_circuit< .iter() .map(|ctl| ctl.helper_columns.len()) .sum::(); - let num_ctl_z_polys = ctl_vars.len(); let StarkOpeningSetTarget { local_values, diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 6ee26f585a..5ff578f9fc 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -111,7 +111,6 @@ pub trait Stark, const D: usize>: Sync { let auxiliary_polys_info = FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); - let mut start_index = num_lookup_columns; let ctl_zs_info = FriPolynomialInfo::from_range( AUXILIARY_ORACLE_INDEX, num_lookup_columns + num_ctl_helpers..num_auxiliary_polys, diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index bb18f5434e..3e284c7fc4 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -318,7 +318,7 @@ pub(crate) fn verify_stark_proof_with_challenges< next_values, auxiliary_polys, auxiliary_polys_next, - ctl_zs_first, + ctl_zs_first: _, quotient_polys, } = &proof.openings; let vars = S::EvaluationFrame::from_values(local_values, next_values); diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index e87c1465e1..8c09fa00a2 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -928,7 +928,7 @@ pub(crate) fn generate_exception( row.general.stack_mut().stack_inv_aux = F::ONE; } - fill_stack_fields(state, &mut row); + fill_stack_fields(state, &mut row)?; row.general.exception_mut().exc_code_bits = [ F::from_bool(exc_code & 1 != 0), diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 835ff59372..aed6ff5397 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -400,7 +400,7 @@ fn try_perform_instruction( fill_op_flag(op, &mut row); - fill_stack_fields(state, &mut row); + fill_stack_fields(state, &mut row)?; // Might write in general CPU columns when it shouldn't, but the correct values will // overwrite these ones during the op generation. From af0259c5eb010304cfc04a5e2a77e88cf8cc2378 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:15:02 -0500 Subject: [PATCH 132/175] Remove StarkProofWithMetadata (#1497) --- evm/src/cross_table_lookup.rs | 8 ++++---- evm/src/fixed_recursive_verifier.rs | 10 ++++------ evm/src/get_challenges.rs | 6 ++---- evm/src/proof.rs | 18 ++---------------- evm/src/prover.rs | 14 ++++---------- evm/src/recursive_verifier.rs | 12 +++--------- evm/src/verifier.rs | 16 ++++++++-------- 7 files changed, 27 insertions(+), 57 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 359b5309e8..df5bfbfc65 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -52,7 +52,7 @@ use crate::lookup::{ eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, ColumnFilter, Filter, GrandProductChallenge, }; -use crate::proof::{StarkProofTarget, StarkProofWithMetadata}; +use crate::proof::{StarkProof, StarkProofTarget}; use crate::stark::Stark; /// An alias for `usize`, to represent the index of a STARK table in a multi-STARK setting. @@ -494,7 +494,7 @@ impl<'a, F: RichField + Extendable, const D: usize> { /// Extracts the `CtlCheckVars` for each STARK. pub(crate) fn from_proofs, const N: usize>( - proofs: &[StarkProofWithMetadata; N], + proofs: &[StarkProof; N], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_lookup_columns: &[usize; N], @@ -511,8 +511,8 @@ impl<'a, F: RichField + Extendable, const D: usize> let ctl_zs = proofs .iter() .zip(num_lookup_columns) - .map(|(p, &num_lookup)| { - let openings = &p.proof.openings; + .map(|(proof, &num_lookup)| { + let openings = &proof.openings; let ctl_zs = &openings.auxiliary_polys[num_lookup..]; let ctl_zs_next = &openings.auxiliary_polys_next[num_lookup..]; diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 2df85b03de..b8844f5c00 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -40,7 +40,7 @@ use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, - PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, + PublicValues, PublicValuesTarget, StarkProof, TrieRoots, TrieRootsTarget, }; use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ @@ -1003,7 +1003,7 @@ where for table in 0..NUM_TABLES { let stark_proof = &all_proof.stark_proofs[table]; - let original_degree_bits = stark_proof.proof.recover_degree_bits(config); + let original_degree_bits = stark_proof.recover_degree_bits(config); let table_circuits = &self.by_table[table]; let shrunk_proof = table_circuits .by_stark_size @@ -1629,12 +1629,10 @@ where pub fn shrink( &self, - stark_proof_with_metadata: &StarkProofWithMetadata, + stark_proof: &StarkProof, ctl_challenges: &GrandProductChallengeSet, ) -> anyhow::Result> { - let mut proof = self - .initial_wrapper - .prove(stark_proof_with_metadata, ctl_challenges)?; + let mut proof = self.initial_wrapper.prove(stark_proof, ctl_challenges)?; for wrapper_circuit in &self.shrinking_wrappers { proof = wrapper_circuit.prove(&proof)?; } diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 756b0650da..a9ea705a33 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -199,7 +199,7 @@ impl, C: GenericConfig, const D: usize> A let mut challenger = Challenger::::new(); for proof in &self.stark_proofs { - challenger.observe_cap(&proof.proof.trace_cap); + challenger.observe_cap(&proof.trace_cap); } observe_public_values::(&mut challenger, &self.public_values)?; @@ -210,9 +210,7 @@ impl, C: GenericConfig, const D: usize> A Ok(AllProofChallenges { stark_challenges: core::array::from_fn(|i| { challenger.compact(); - self.stark_proofs[i] - .proof - .get_challenges(&mut challenger, config) + self.stark_proofs[i].get_challenges(&mut challenger, config) }), ctl_challenges, }) diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 33640458d6..ef63431b98 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -25,7 +25,7 @@ use crate::util::{get_h160, get_h256, h2u}; #[derive(Debug, Clone)] pub struct AllProof, C: GenericConfig, const D: usize> { /// Proofs for all the different STARK modules. - pub stark_proofs: [StarkProofWithMetadata; NUM_TABLES], + pub stark_proofs: [StarkProof; NUM_TABLES], /// Cross-table lookup challenges. pub(crate) ctl_challenges: GrandProductChallengeSet, /// Public memory values used for the recursive proofs. @@ -35,7 +35,7 @@ pub struct AllProof, C: GenericConfig, co impl, C: GenericConfig, const D: usize> AllProof { /// Returns the degree (i.e. the trace length) of each STARK. pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) + core::array::from_fn(|i| self.stark_proofs[i].recover_degree_bits(config)) } } @@ -837,20 +837,6 @@ pub struct StarkProof, C: GenericConfig, pub opening_proof: FriProof, } -/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when -/// creating a recursive wrapper proof around a STARK proof. -#[derive(Debug, Clone)] -pub struct StarkProofWithMetadata -where - F: RichField + Extendable, - C: GenericConfig, -{ - /// Initial Fiat-Shamir state. - pub(crate) init_challenger_state: >::Permutation, - /// Proof for a single STARK. - pub(crate) proof: StarkProof, -} - impl, C: GenericConfig, const D: usize> StarkProof { /// Recover the length of the trace from a STARK proof and a STARK config. pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index faef64a033..2da098f2a3 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -32,7 +32,7 @@ use crate::evaluation_frame::StarkEvaluationFrame; use crate::generation::{generate_traces, GenerationInputs}; use crate::get_challenges::observe_public_values; use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; -use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; +use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; #[cfg(test)] @@ -187,7 +187,7 @@ fn prove_with_commitments( ctl_challenges: &GrandProductChallengeSet, timing: &mut TimingTree, abort_signal: Option>, -) -> Result<[StarkProofWithMetadata; NUM_TABLES]> +) -> Result<[StarkProof; NUM_TABLES]> where F: RichField + Extendable, C: GenericConfig, @@ -323,7 +323,7 @@ pub(crate) fn prove_single_table( challenger: &mut Challenger, timing: &mut TimingTree, abort_signal: Option>, -) -> Result> +) -> Result> where F: RichField + Extendable, C: GenericConfig, @@ -341,8 +341,6 @@ where "FRI total reduction arity is too large.", ); - let init_challenger_state = challenger.compact(); - let constraint_degree = stark.constraint_degree(); let lookup_challenges = stark.uses_lookups().then(|| { ctl_challenges @@ -520,16 +518,12 @@ where ) ); - let proof = StarkProof { + Ok(StarkProof { trace_cap: trace_commitment.merkle_tree.cap.clone(), auxiliary_polys_cap, quotient_polys_cap, openings, opening_proof, - }; - Ok(StarkProofWithMetadata { - init_challenger_state, - proof, }) } diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 5220ba32a7..2c3827a50f 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -39,8 +39,7 @@ use crate::memory::VALUE_LIMBS; use crate::proof::{ BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, StarkProof, - StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots, - TrieRootsTarget, + StarkProofChallengesTarget, StarkProofTarget, TrieRoots, TrieRootsTarget, }; use crate::stark::Stark; use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; @@ -147,7 +146,7 @@ where pub(crate) fn prove( &self, - proof_with_metadata: &StarkProofWithMetadata, + proof: &StarkProof, ctl_challenges: &GrandProductChallengeSet, ) -> Result> { let mut inputs = PartialWitness::new(); @@ -155,7 +154,7 @@ where set_stark_proof_target( &mut inputs, &self.stark_proof_target, - &proof_with_metadata.proof, + proof, self.zero_target, ); @@ -169,11 +168,6 @@ where inputs.set_target(challenge_target.gamma, challenge.gamma); } - inputs.set_target_arr( - self.init_challenger_state_target.as_ref(), - proof_with_metadata.init_challenger_state.as_ref(), - ); - self.circuit.prove(inputs) } } diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 3e284c7fc4..ae4fbf4aff 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -72,7 +72,7 @@ where verify_stark_proof_with_challenges( arithmetic_stark, - &all_proof.stark_proofs[Table::Arithmetic as usize].proof, + &all_proof.stark_proofs[Table::Arithmetic as usize], &stark_challenges[Table::Arithmetic as usize], &ctl_vars_per_table[Table::Arithmetic as usize], &ctl_challenges, @@ -80,7 +80,7 @@ where )?; verify_stark_proof_with_challenges( byte_packing_stark, - &all_proof.stark_proofs[Table::BytePacking as usize].proof, + &all_proof.stark_proofs[Table::BytePacking as usize], &stark_challenges[Table::BytePacking as usize], &ctl_vars_per_table[Table::BytePacking as usize], &ctl_challenges, @@ -88,7 +88,7 @@ where )?; verify_stark_proof_with_challenges( cpu_stark, - &all_proof.stark_proofs[Table::Cpu as usize].proof, + &all_proof.stark_proofs[Table::Cpu as usize], &stark_challenges[Table::Cpu as usize], &ctl_vars_per_table[Table::Cpu as usize], &ctl_challenges, @@ -96,7 +96,7 @@ where )?; verify_stark_proof_with_challenges( keccak_stark, - &all_proof.stark_proofs[Table::Keccak as usize].proof, + &all_proof.stark_proofs[Table::Keccak as usize], &stark_challenges[Table::Keccak as usize], &ctl_vars_per_table[Table::Keccak as usize], &ctl_challenges, @@ -104,7 +104,7 @@ where )?; verify_stark_proof_with_challenges( keccak_sponge_stark, - &all_proof.stark_proofs[Table::KeccakSponge as usize].proof, + &all_proof.stark_proofs[Table::KeccakSponge as usize], &stark_challenges[Table::KeccakSponge as usize], &ctl_vars_per_table[Table::KeccakSponge as usize], &ctl_challenges, @@ -112,7 +112,7 @@ where )?; verify_stark_proof_with_challenges( logic_stark, - &all_proof.stark_proofs[Table::Logic as usize].proof, + &all_proof.stark_proofs[Table::Logic as usize], &stark_challenges[Table::Logic as usize], &ctl_vars_per_table[Table::Logic as usize], &ctl_challenges, @@ -120,7 +120,7 @@ where )?; verify_stark_proof_with_challenges( memory_stark, - &all_proof.stark_proofs[Table::Memory as usize].proof, + &all_proof.stark_proofs[Table::Memory as usize], &stark_challenges[Table::Memory as usize], &ctl_vars_per_table[Table::Memory as usize], &ctl_challenges, @@ -142,7 +142,7 @@ where cross_table_lookups, all_proof .stark_proofs - .map(|p| p.proof.openings.ctl_zs_first), + .map(|proof| proof.openings.ctl_zs_first), extra_looking_sums, config, ) From 212f29cfb9ba968757ea6bafd6039820b84f5a16 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:56:49 -0500 Subject: [PATCH 133/175] Add missing constraints mentioned by auditors (#1499) --- evm/src/arithmetic/arithmetic_stark.rs | 9 +++++++++ evm/src/logic.rs | 22 ++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index dcf966ee59..5e3f039cdf 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -214,6 +214,10 @@ impl, const D: usize> Stark for ArithmeticSta yield_constr.constraint(flag * (flag - P::ONES)); } + // Only a single flag must be activated at once. + let all_flags = op_flags().map(|i| lv[i]).sum::

(); + yield_constr.constraint(all_flags * (all_flags - P::ONES)); + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. let opcode_constraint = (P::ONES - lv[columns::IS_RANGE_CHECK]) * lv[columns::OPCODE_COL]; yield_constr.constraint(opcode_constraint); @@ -261,6 +265,11 @@ impl, const D: usize> Stark for ArithmeticSta yield_constr.constraint(builder, constraint); } + // Only a single flag must be activated at once. + let all_flags = builder.add_many_extension(op_flags().map(|i| lv[i])); + let constraint = builder.mul_sub_extension(all_flags, all_flags, all_flags); + yield_constr.constraint(builder, constraint); + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. let opcode_constraint = builder.arithmetic_extension( F::NEG_ONE, diff --git a/evm/src/logic.rs b/evm/src/logic.rs index fa83fa94c1..7300c6af65 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -227,11 +227,19 @@ impl, const D: usize> Stark for LogicStark sum_coeff = 0, and_coeff = 1` // `OR => sum_coeff = 1, and_coeff = -1` @@ -276,11 +284,21 @@ impl, const D: usize> Stark for LogicStark sum_coeff = 0, and_coeff = 1` // `OR => sum_coeff = 1, and_coeff = -1` From 8f919133379213698ba43fda5a39a153a17324b7 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 6 Feb 2024 09:32:45 -0500 Subject: [PATCH 134/175] Fix nightly version --- .github/workflows/continuous-integration-workflow.yml | 6 +++--- rust-toolchain | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 7c18a405a9..5cd623127e 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@nightly-2024-02-01 - name: Set up rust cache uses: Swatinem/rust-cache@v2 @@ -77,7 +77,7 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@nightly-2024-02-01 with: targets: wasm32-unknown-unknown @@ -112,7 +112,7 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@nightly-2024-02-01 with: components: rustfmt, clippy diff --git a/rust-toolchain b/rust-toolchain index 07ade694b1..471d867dd0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly \ No newline at end of file +nightly-2024-02-01 \ No newline at end of file From 246c2b6263f71313192801b1c27a3c08e241f545 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 6 Feb 2024 09:39:29 -0500 Subject: [PATCH 135/175] Fix workflow --- .github/workflows/continuous-integration-workflow.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 5cd623127e..29f471380a 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -28,7 +28,9 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly-2024-02-01 + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-02-01 - name: Set up rust cache uses: Swatinem/rust-cache@v2 @@ -77,8 +79,9 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly-2024-02-01 + uses: dtolnay/rust-toolchain@master with: + toolchain: nightly-2024-02-01 targets: wasm32-unknown-unknown - name: Set up rust cache @@ -112,8 +115,9 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@nightly-2024-02-01 + uses: dtolnay/rust-toolchain@master with: + toolchain: nightly-2024-02-01 components: rustfmt, clippy - name: Set up rust cache From 06444eaaf32cd2e9005ec2f0c53b41df911b867e Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:57:40 -0500 Subject: [PATCH 136/175] Switch permutation argument for logUp in `starky` (#1496) * Switch permutation argument for logUp in starky * Apply comments * Refactor check_lookup_options * Comments * Add more visibility * std -> core * Revert "Add more visibility" This reverts commit 2b4e50e0e7fc7676814b1bc1f4071d9ec0ab9d5c. * Add more visibility to lookup items --- evm/src/prover.rs | 4 +- starky/Cargo.toml | 2 + starky/src/fibonacci_stark.rs | 18 +- starky/src/get_challenges.rs | 72 +-- starky/src/lib.rs | 3 +- starky/src/lookup.rs | 1002 ++++++++++++++++++++++++++++++ starky/src/permutation.rs | 398 ------------ starky/src/proof.rs | 32 +- starky/src/prover.rs | 282 +++++++-- starky/src/recursive_verifier.rs | 101 +-- starky/src/stark.rs | 132 ++-- starky/src/vanishing_poly.rs | 35 +- starky/src/verifier.rs | 119 ++-- 13 files changed, 1476 insertions(+), 724 deletions(-) create mode 100644 starky/src/lookup.rs delete mode 100644 starky/src/permutation.rs diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 2da098f2a3..0b7858d3b9 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -612,7 +612,9 @@ where local_values: auxiliary_polys_commitment.get_lde_values_packed(i_start, step) [..num_lookup_columns] .to_vec(), - next_values: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step), + next_values: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step) + [..num_lookup_columns] + .to_vec(), challenges: challenges.to_vec(), }); diff --git a/starky/Cargo.toml b/starky/Cargo.toml index a3f9b37c5d..0efae5fcf9 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -20,8 +20,10 @@ timing = ["plonky2/timing"] anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.11.0", default-features = false } log = { version = "0.4.14", default-features = false } +num-bigint = { version = "0.4.3", default-features = false } plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } plonky2 = { path = "../plonky2", default-features = false } +plonky2_util = { path = "../util", default-features = false } [dev-dependencies] env_logger = { version = "0.9.0", default-features = false } diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index d34ccfd2d0..903c0abff4 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -11,7 +11,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::permutation::PermutationPair; +use crate::lookup::{Column, Lookup}; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; @@ -41,15 +41,16 @@ impl, const D: usize> FibonacciStark { } } - /// Generate the trace using `x0, x1, 0, 1` as initial state values. + /// Generate the trace using `x0, x1, 0, 1, 1` as initial state values. fn generate_trace(&self, x0: F, x1: F) -> Vec> { let mut trace_rows = (0..self.num_rows) - .scan([x0, x1, F::ZERO, F::ONE], |acc, _| { + .scan([x0, x1, F::ZERO, F::ONE, F::ONE], |acc, _| { let tmp = *acc; acc[0] = tmp[1]; acc[1] = tmp[0] + tmp[1]; acc[2] = tmp[2] + F::ONE; acc[3] = tmp[3] + F::ONE; + // acc[4] (i.e. frequency column) remains unchanged, as we're permuting a strictly monotonous sequence. Some(tmp) }) .collect::>(); @@ -58,7 +59,7 @@ impl, const D: usize> FibonacciStark { } } -const COLUMNS: usize = 4; +const COLUMNS: usize = 5; const PUBLIC_INPUTS: usize = 3; impl, const D: usize> Stark for FibonacciStark { @@ -127,8 +128,13 @@ impl, const D: usize> Stark for FibonacciStar 2 } - fn permutation_pairs(&self) -> Vec { - vec![PermutationPair::singletons(2, 3)] + fn lookups(&self) -> Vec> { + vec![Lookup { + columns: vec![Column::single(2)], + table_column: Column::single(3), + frequencies_column: Column::single(4), + filter_columns: vec![None; 1], + }] } } diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index b34b427d08..5f9beddc3e 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -12,16 +12,13 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::config::StarkConfig; -use crate::permutation::{ - get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, -}; +use crate::lookup::{get_grand_product_challenge_set, get_grand_product_challenge_set_target}; use crate::proof::*; use crate::stark::Stark; -fn get_challenges( - stark: &S, +fn get_challenges( trace_cap: &MerkleCap, - permutation_zs_cap: Option<&MerkleCap>, + auxiliary_polys_cap: Option<&MerkleCap>, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, commit_phase_merkle_caps: &[MerkleCap], @@ -33,7 +30,6 @@ fn get_challenges( where F: RichField + Extendable, C: GenericConfig, - S: Stark, { let num_challenges = config.num_challenges; @@ -41,13 +37,9 @@ where challenger.observe_cap(trace_cap); - let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_permutation_challenge_sets( - &mut challenger, - num_challenges, - stark.permutation_batch_size(), - ); - challenger.observe_cap(permutation_zs_cap); + let lookup_challenge_set = auxiliary_polys_cap.map(|auxiliary_polys_cap| { + let tmp = get_grand_product_challenge_set(&mut challenger, num_challenges); + challenger.observe_cap(auxiliary_polys_cap); tmp }); @@ -59,7 +51,7 @@ where challenger.observe_openings(&openings.to_fri_openings()); StarkProofChallenges { - permutation_challenge_sets, + lookup_challenge_set, stark_alphas, stark_zeta, fri_challenges: challenger.fri_challenges::( @@ -79,27 +71,21 @@ where { // TODO: Should be used later in compression? #![allow(dead_code)] - pub(crate) fn fri_query_indices>( - &self, - stark: &S, - config: &StarkConfig, - degree_bits: usize, - ) -> Vec { - self.get_challenges(stark, config, degree_bits) + pub(crate) fn fri_query_indices(&self, config: &StarkConfig, degree_bits: usize) -> Vec { + self.get_challenges(config, degree_bits) .fri_challenges .fri_query_indices } /// Computes all Fiat-Shamir challenges used in the STARK proof. - pub(crate) fn get_challenges>( + pub(crate) fn get_challenges( &self, - stark: &S, config: &StarkConfig, degree_bits: usize, ) -> StarkProofChallenges { let StarkProof { trace_cap, - permutation_zs_cap, + auxiliary_polys_cap, quotient_polys_cap, openings, opening_proof: @@ -111,10 +97,9 @@ where }, } = &self.proof; - get_challenges::( - stark, + get_challenges::( trace_cap, - permutation_zs_cap.as_ref(), + auxiliary_polys_cap.as_ref(), quotient_polys_cap, openings, commit_phase_merkle_caps, @@ -130,13 +115,11 @@ where pub(crate) fn get_challenges_target< F: RichField + Extendable, C: GenericConfig, - S: Stark, const D: usize, >( builder: &mut CircuitBuilder, - stark: &S, trace_cap: &MerkleCapTarget, - permutation_zs_cap: Option<&MerkleCapTarget>, + auxiliary_polys_cap: Option<&MerkleCapTarget>, quotient_polys_cap: &MerkleCapTarget, openings: &StarkOpeningSetTarget, commit_phase_merkle_caps: &[MerkleCapTarget], @@ -153,13 +136,8 @@ where challenger.observe_cap(trace_cap); - let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_permutation_challenge_sets_target( - builder, - &mut challenger, - num_challenges, - stark.permutation_batch_size(), - ); + let lookup_challenge_set = auxiliary_polys_cap.map(|permutation_zs_cap| { + let tmp = get_grand_product_challenge_set_target(builder, &mut challenger, num_challenges); challenger.observe_cap(permutation_zs_cap); tmp }); @@ -172,7 +150,7 @@ where challenger.observe_openings(&openings.to_fri_openings()); StarkProofChallengesTarget { - permutation_challenge_sets, + lookup_challenge_set, stark_alphas, stark_zeta, fri_challenges: challenger.fri_challenges( @@ -186,22 +164,19 @@ where } impl StarkProofWithPublicInputsTarget { - pub(crate) fn get_challenges< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - >( + pub(crate) fn get_challenges( &self, builder: &mut CircuitBuilder, - stark: &S, config: &StarkConfig, ) -> StarkProofChallengesTarget where + F: RichField + Extendable, + C: GenericConfig, C::Hasher: AlgebraicHasher, { let StarkProofTarget { trace_cap, - permutation_zs_cap, + auxiliary_polys_cap, quotient_polys_cap, openings, opening_proof: @@ -213,11 +188,10 @@ impl StarkProofWithPublicInputsTarget { }, } = &self.proof; - get_challenges_target::( + get_challenges_target::( builder, - stark, trace_cap, - permutation_zs_cap.as_ref(), + auxiliary_polys_cap.as_ref(), quotient_polys_cap, openings, commit_phase_merkle_caps, diff --git a/starky/src/lib.rs b/starky/src/lib.rs index 635e57bd0b..f6b4f5e0c7 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] +#![allow(unused)] // TODO: Remove post code migration #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; @@ -9,7 +10,7 @@ mod get_challenges; pub mod config; pub mod constraint_consumer; pub mod evaluation_frame; -pub mod permutation; +pub mod lookup; pub mod proof; pub mod prover; pub mod recursive_verifier; diff --git a/starky/src/lookup.rs b/starky/src/lookup.rs new file mode 100644 index 0000000000..19f2042481 --- /dev/null +++ b/starky/src/lookup.rs @@ -0,0 +1,1002 @@ +use alloc::vec; +use alloc::vec::Vec; +use core::borrow::Borrow; +use core::fmt::Debug; +use core::iter::repeat; + +use itertools::Itertools; +use num_bigint::BigUint; +use plonky2::field::batch_util::batch_add_inplace; +use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::field::packed::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::{AlgebraicHasher, Hasher}; +use plonky2::plonk::plonk_common::{ + reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, +}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; +use plonky2_util::ceil_div_usize; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::evaluation_frame::StarkEvaluationFrame; +use crate::stark::Stark; + +/// Represents a filter, which evaluates to 1 if the row must be considered and 0 if it should be ignored. +/// It's an arbitrary degree 2 combination of columns: `products` are the degree 2 terms, and `constants` are +/// the degree 1 terms. +#[derive(Clone, Debug)] +pub struct Filter { + products: Vec<(Column, Column)>, + constants: Vec>, +} + +impl Filter { + pub fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { + Self { + products, + constants, + } + } + + /// Returns a filter made of a single column. + pub fn new_simple(col: Column) -> Self { + Self { + products: vec![], + constants: vec![col], + } + } + + /// Given the column values for the current and next rows, evaluates the filter. + pub(crate) fn eval_filter(&self, v: &[P], next_v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.products + .iter() + .map(|(col1, col2)| col1.eval_with_next(v, next_v) * col2.eval_with_next(v, next_v)) + .sum::

() + + self + .constants + .iter() + .map(|col| col.eval_with_next(v, next_v)) + .sum::

() + } + + /// Circuit version of `eval_filter`: + /// Given the column values for the current and next rows, evaluates the filter. + pub(crate) fn eval_filter_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + next_v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let prods = self + .products + .iter() + .map(|(col1, col2)| { + let col1_eval = col1.eval_with_next_circuit(builder, v, next_v); + let col2_eval = col2.eval_with_next_circuit(builder, v, next_v); + builder.mul_extension(col1_eval, col2_eval) + }) + .collect::>(); + + let consts = self + .constants + .iter() + .map(|col| col.eval_with_next_circuit(builder, v, next_v)) + .collect::>(); + + let prods = builder.add_many_extension(prods); + let consts = builder.add_many_extension(consts); + builder.add_extension(prods, consts) + } + + /// Evaluate on a row of a table given in column-major form. + pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { + self.products + .iter() + .map(|(col1, col2)| col1.eval_table(table, row) * col2.eval_table(table, row)) + .sum::() + + self + .constants + .iter() + .map(|col| col.eval_table(table, row)) + .sum() + } + + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } +} + +/// Represent two linear combination of columns, corresponding to the current and next row values. +/// Each linear combination is represented as: +/// - a vector of `(usize, F)` corresponding to the column number and the associated multiplicand +/// - the constant of the linear combination. +#[derive(Clone, Debug)] +pub struct Column { + linear_combination: Vec<(usize, F)>, + next_row_linear_combination: Vec<(usize, F)>, + constant: F, +} + +impl Column { + /// Returns the representation of a single column in the current row. + pub fn single(c: usize) -> Self { + Self { + linear_combination: vec![(c, F::ONE)], + next_row_linear_combination: vec![], + constant: F::ZERO, + } + } + + /// Returns multiple single columns in the current row. + pub fn singles>>( + cs: I, + ) -> impl Iterator { + cs.into_iter().map(|c| Self::single(*c.borrow())) + } + + /// Returns the representation of a single column in the next row. + pub fn single_next_row(c: usize) -> Self { + Self { + linear_combination: vec![], + next_row_linear_combination: vec![(c, F::ONE)], + constant: F::ZERO, + } + } + + /// Returns multiple single columns for the next row. + pub fn singles_next_row>>( + cs: I, + ) -> impl Iterator { + cs.into_iter().map(|c| Self::single_next_row(*c.borrow())) + } + + /// Returns a linear combination corresponding to a constant. + pub fn constant(constant: F) -> Self { + Self { + linear_combination: vec![], + next_row_linear_combination: vec![], + constant, + } + } + + /// Returns a linear combination corresponding to 0. + pub fn zero() -> Self { + Self::constant(F::ZERO) + } + + /// Returns a linear combination corresponding to 1. + pub fn one() -> Self { + Self::constant(F::ONE) + } + + /// Given an iterator of `(usize, F)` and a constant, returns the association linear combination of columns for the current row. + pub fn linear_combination_with_constant>( + iter: I, + constant: F, + ) -> Self { + let v = iter.into_iter().collect::>(); + assert!(!v.is_empty()); + + // Because this is a debug assertion, we only check it when the `std` + // feature is activated, as `Itertools::unique` relies on collections. + #[cfg(feature = "std")] + debug_assert_eq!( + v.iter().map(|(c, _)| c).unique().count(), + v.len(), + "Duplicate columns." + ); + + Self { + linear_combination: v, + next_row_linear_combination: vec![], + constant, + } + } + + /// Given an iterator of `(usize, F)` and a constant, returns the associated linear combination of columns for the current and the next rows. + pub fn linear_combination_and_next_row_with_constant>( + iter: I, + next_row_iter: I, + constant: F, + ) -> Self { + let v = iter.into_iter().collect::>(); + let next_row_v = next_row_iter.into_iter().collect::>(); + + assert!(!v.is_empty() || !next_row_v.is_empty()); + + // Because these are debug assertions, we only check them when the `std` + // feature is activated, as `Itertools::unique` relies on collections. + #[cfg(feature = "std")] + { + debug_assert_eq!( + v.iter().map(|(c, _)| c).unique().count(), + v.len(), + "Duplicate columns." + ); + debug_assert_eq!( + next_row_v.iter().map(|(c, _)| c).unique().count(), + next_row_v.len(), + "Duplicate columns." + ); + } + + Self { + linear_combination: v, + next_row_linear_combination: next_row_v, + constant, + } + } + + /// Returns a linear combination of columns, with no additional constant. + pub fn linear_combination>(iter: I) -> Self { + Self::linear_combination_with_constant(iter, F::ZERO) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: + /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n. + pub fn le_bits>>(cs: I) -> Self { + Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers())) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: + /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n + k where `k` is an + /// additional constant. + pub fn le_bits_with_constant>>( + cs: I, + constant: F, + ) -> Self { + Self::linear_combination_with_constant( + cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers()), + constant, + ) + } + + /// Given an iterator of columns (c_0, ..., c_n) containing bytes in little endian order: + /// returns the representation of c_0 + 256 * c_1 + ... + 256^n * c_n. + pub fn le_bytes>>(cs: I) -> Self { + Self::linear_combination( + cs.into_iter() + .map(|c| *c.borrow()) + .zip(F::from_canonical_u16(256).powers()), + ) + } + + /// Given an iterator of columns, returns the representation of their sum. + pub fn sum>>(cs: I) -> Self { + Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(repeat(F::ONE))) + } + + /// Given the column values for the current row, returns the evaluation of the linear combination. + pub(crate) fn eval(&self, v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.linear_combination + .iter() + .map(|&(c, f)| v[c] * FE::from_basefield(f)) + .sum::

() + + FE::from_basefield(self.constant) + } + + /// Given the column values for the current and next rows, evaluates the current and next linear combinations and returns their sum. + pub(crate) fn eval_with_next(&self, v: &[P], next_v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.linear_combination + .iter() + .map(|&(c, f)| v[c] * FE::from_basefield(f)) + .sum::

() + + self + .next_row_linear_combination + .iter() + .map(|&(c, f)| next_v[c] * FE::from_basefield(f)) + .sum::

() + + FE::from_basefield(self.constant) + } + + /// Evaluate on a row of a table given in column-major form. + pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { + let mut res = self + .linear_combination + .iter() + .map(|&(c, f)| table[c].values[row] * f) + .sum::() + + self.constant; + + // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. + // If the lookups are correctly written, the filter should be 0 in that case anyway. + if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { + res += self + .next_row_linear_combination + .iter() + .map(|&(c, f)| table[c].values[row + 1] * f) + .sum::(); + } + + res + } + + /// Evaluates the column on all rows. + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } + + /// Circuit version of `eval`: Given a row's targets, returns their linear combination. + pub(crate) fn eval_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let pairs = self + .linear_combination + .iter() + .map(|&(c, f)| { + ( + v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }) + .collect::>(); + let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); + builder.inner_product_extension(F::ONE, constant, pairs) + } + + /// Circuit version of `eval_with_next`: + /// Given the targets of the current and next row, returns the sum of their linear combinations. + pub(crate) fn eval_with_next_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + next_v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let mut pairs = self + .linear_combination + .iter() + .map(|&(c, f)| { + ( + v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }) + .collect::>(); + let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| { + ( + next_v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }); + pairs.extend(next_row_pairs); + let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); + builder.inner_product_extension(F::ONE, constant, pairs) + } +} + +pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); + +pub struct Lookup { + /// Columns whose values should be contained in the lookup table. + /// These are the f_i(x) polynomials in the logUp paper. + pub columns: Vec>, + /// Column containing the lookup table. + /// This is the t(x) polynomial in the paper. + pub table_column: Column, + /// Column containing the frequencies of `columns` in `table_column`. + /// This is the m(x) polynomial in the paper. + pub frequencies_column: Column, + + /// Columns to filter some elements. There is at most one filter + /// column per column to lookup. + pub filter_columns: Vec>>, +} + +impl Lookup { + pub fn num_helper_columns(&self, constraint_degree: usize) -> usize { + // One helper column for each column batch of size `constraint_degree-1`, + // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. + ceil_div_usize(self.columns.len(), constraint_degree - 1) + 1 + } +} + +/// Randomness for a single instance of a permutation check protocol. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct GrandProductChallenge { + /// Randomness used to combine multiple columns into one. + pub(crate) beta: T, + /// Random offset that's added to the beta-reduced column values. + pub(crate) gamma: T, +} + +impl GrandProductChallenge { + pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( + &self, + terms: T, + ) -> P + where + FE: FieldExtension, + P: PackedField, + T::IntoIter: DoubleEndedIterator, + { + reduce_with_powers(terms, FE::from_basefield(self.beta)) + FE::from_basefield(self.gamma) + } +} + +impl GrandProductChallenge { + pub(crate) fn combine_circuit, const D: usize>( + &self, + builder: &mut CircuitBuilder, + terms: &[ExtensionTarget], + ) -> ExtensionTarget { + let reduced = reduce_with_powers_ext_circuit(builder, terms, self.beta); + let gamma = builder.convert_to_ext(self.gamma); + builder.add_extension(reduced, gamma) + } +} + +impl GrandProductChallenge { + pub(crate) fn combine_base_circuit, const D: usize>( + &self, + builder: &mut CircuitBuilder, + terms: &[Target], + ) -> Target { + let reduced = reduce_with_powers_circuit(builder, terms, self.beta); + builder.add(reduced, self.gamma) + } +} + +/// Like `GrandProductChallenge`, but with `num_challenges` copies to boost soundness. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct GrandProductChallengeSet { + pub(crate) challenges: Vec>, +} + +impl GrandProductChallengeSet { + pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_usize(self.challenges.len())?; + for challenge in &self.challenges { + buffer.write_target(challenge.beta)?; + buffer.write_target(challenge.gamma)?; + } + Ok(()) + } + + pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { + let length = buffer.read_usize()?; + let mut challenges = Vec::with_capacity(length); + for _ in 0..length { + challenges.push(GrandProductChallenge { + beta: buffer.read_target()?, + gamma: buffer.read_target()?, + }); + } + + Ok(GrandProductChallengeSet { challenges }) + } +} + +fn get_grand_product_challenge>( + challenger: &mut Challenger, +) -> GrandProductChallenge { + let beta = challenger.get_challenge(); + let gamma = challenger.get_challenge(); + GrandProductChallenge { beta, gamma } +} + +pub(crate) fn get_grand_product_challenge_set>( + challenger: &mut Challenger, + num_challenges: usize, +) -> GrandProductChallengeSet { + let challenges = (0..num_challenges) + .map(|_| get_grand_product_challenge(challenger)) + .collect(); + GrandProductChallengeSet { challenges } +} + +fn get_grand_product_challenge_target< + F: RichField + Extendable, + H: AlgebraicHasher, + const D: usize, +>( + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, +) -> GrandProductChallenge { + let beta = challenger.get_challenge(builder); + let gamma = challenger.get_challenge(builder); + GrandProductChallenge { beta, gamma } +} + +pub(crate) fn get_grand_product_challenge_set_target< + F: RichField + Extendable, + H: AlgebraicHasher, + const D: usize, +>( + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, + num_challenges: usize, +) -> GrandProductChallengeSet { + let challenges = (0..num_challenges) + .map(|_| get_grand_product_challenge_target(builder, challenger)) + .collect(); + GrandProductChallengeSet { challenges } +} + +/// logUp protocol from +/// Compute the helper columns for the lookup argument. +/// Given columns `f0,...,fk` and a column `t`, such that `∪fi ⊆ t`, and challenges `x`, +/// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`, +/// and `Z(gx) = Z(x) + sum h_i(x) - m(x)g(x)` where `m` is the frequencies column. +pub(crate) fn lookup_helper_columns( + lookup: &Lookup, + trace_poly_values: &[PolynomialValues], + challenge: F, + constraint_degree: usize, +) -> Vec> { + assert!( + constraint_degree == 2 || constraint_degree == 3, + "TODO: Allow other constraint degrees." + ); + + assert_eq!(lookup.columns.len(), lookup.filter_columns.len()); + + let num_total_logup_entries = trace_poly_values[0].values.len() * lookup.columns.len(); + assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); + + let num_helper_columns = lookup.num_helper_columns(constraint_degree); + let mut helper_columns: Vec> = Vec::with_capacity(num_helper_columns); + + let looking_cols = lookup + .columns + .iter() + .map(|col| vec![col.clone()]) + .collect::>>>(); + + let grand_challenge = GrandProductChallenge { + beta: F::ONE, + gamma: challenge, + }; + + let columns_filters = looking_cols + .iter() + .zip(lookup.filter_columns.iter()) + .map(|(col, filter)| (&col[..], filter)) + .collect::>(); + // For each batch of `constraint_degree-1` columns `fi`, compute `sum 1/(f_i+challenge)` and + // add it to the helper columns. + // Note: these are the h_k(x) polynomials in the paper, with a few differences: + // * Here, the first ratio m_0(x)/phi_0(x) is not included with the columns batched up to create the + // h_k polynomials; instead there's a separate helper column for it (see below). + // * Here, we use 1 instead of -1 as the numerator (and subtract later). + // * Here, for now, the batch size (l) is always constraint_degree - 1 = 2. + // * Here, there are filters for the columns, to only select some rows + // in a given column. + let mut helper_columns = get_helper_cols( + trace_poly_values, + trace_poly_values[0].len(), + &columns_filters, + grand_challenge, + constraint_degree, + ); + + // Add `1/(table+challenge)` to the helper columns. + // This is 1/phi_0(x) = 1/(x + t(x)) from the paper. + // Here, we don't include m(x) in the numerator, instead multiplying it with this column later. + let mut table = lookup.table_column.eval_all_rows(trace_poly_values); + for x in table.iter_mut() { + *x = challenge + *x; + } + let table_inverse: Vec = F::batch_multiplicative_inverse(&table); + + // Compute the `Z` polynomial with `Z(1)=0` and `Z(gx) = Z(x) + sum h_i(x) - frequencies(x)g(x)`. + // This enforces the check from the paper, that the sum of the h_k(x) polynomials is 0 over H. + // In the paper, that sum includes m(x)/(x + t(x)) = frequencies(x)/g(x), because that was bundled + // into the h_k(x) polynomials. + let frequencies = &lookup.frequencies_column.eval_all_rows(trace_poly_values); + let mut z = Vec::with_capacity(frequencies.len()); + z.push(F::ZERO); + for i in 0..frequencies.len() - 1 { + let x = helper_columns[..num_helper_columns - 1] + .iter() + .map(|col| col.values[i]) + .sum::() + - frequencies[i] * table_inverse[i]; + z.push(z[i] + x); + } + helper_columns.push(z.into()); + + helper_columns +} + +/// Given data associated to a lookup, check the associated helper polynomials. +pub(crate) fn eval_helper_columns( + filter: &[Option>], + columns: &[Vec

], + local_values: &[P], + next_values: &[P], + helper_columns: &[P], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, +{ + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + match chunk.len() { + 2 => { + let combin0 = challenges.combine(&chunk[0]); + let combin1 = challenges.combine(chunk[1].iter()); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter(local_values, next_values) + } else { + P::ONES + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + + consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); + } + 1 => { + let combin = challenges.combine(&chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter(local_values, next_values) + } else { + P::ONES + }; + consumer.constraint(combin * h - f0); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + +/// Circuit version of `eval_helper_columns`. +/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. +pub(crate) fn eval_helper_columns_circuit, const D: usize>( + builder: &mut CircuitBuilder, + filter: &[Option>], + columns: &[Vec>], + local_values: &[ExtensionTarget], + next_values: &[ExtensionTarget], + helper_columns: &[ExtensionTarget], + constraint_degree: usize, + challenges: &GrandProductChallenge, + consumer: &mut RecursiveConstraintConsumer, +) { + if !helper_columns.is_empty() { + for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { + let fs = + &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; + let h = helper_columns[j]; + + let one = builder.one_extension(); + match chunk.len() { + 2 => { + let combin0 = challenges.combine_circuit(builder, &chunk[0]); + let combin1 = challenges.combine_circuit(builder, &chunk[1]); + + let f0 = if let Some(filter0) = &fs[0] { + filter0.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let f1 = if let Some(filter1) = &fs[1] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + + let constr = builder.mul_sub_extension(combin0, h, f0); + let constr = builder.mul_extension(constr, combin1); + let f1_constr = builder.mul_extension(f1, combin0); + let constr = builder.sub_extension(constr, f1_constr); + + consumer.constraint(builder, constr); + } + 1 => { + let combin = challenges.combine_circuit(builder, &chunk[0]); + let f0 = if let Some(filter1) = &fs[0] { + filter1.eval_filter_circuit(builder, local_values, next_values) + } else { + one + }; + let constr = builder.mul_sub_extension(combin, h, f0); + consumer.constraint(builder, constr); + } + + _ => todo!("Allow other constraint degrees"), + } + } + } +} + +/// Given a STARK's trace, and the data associated to one lookup (either CTL or range check), +/// returns the associated helper polynomials. +pub(crate) fn get_helper_cols( + trace: &[PolynomialValues], + degree: usize, + columns_filters: &[ColumnFilter], + challenge: GrandProductChallenge, + constraint_degree: usize, +) -> Vec> { + let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); + + let mut helper_columns = Vec::with_capacity(num_helper_columns); + + let mut filter_index = 0; + for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { + let (first_col, first_filter) = cols_filts.next().unwrap(); + + let mut filter_col = Vec::with_capacity(degree); + let first_combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = first_filter { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = first_col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + let mut acc = F::batch_multiplicative_inverse(&first_combined); + for d in 0..degree { + if filter_col[d].is_zero() { + acc[d] = F::ZERO; + } + } + + for (col, filt) in cols_filts { + let mut filter_col = Vec::with_capacity(degree); + let mut combined = (0..degree) + .map(|d| { + let f = if let Some(filter) = filt { + let f = filter.eval_table(trace, d); + filter_col.push(f); + f + } else { + filter_col.push(F::ONE); + F::ONE + }; + if f.is_one() { + let evals = col + .iter() + .map(|c| c.eval_table(trace, d)) + .collect::>(); + challenge.combine(evals.iter()) + } else { + assert_eq!(f, F::ZERO, "Non-binary filter?"); + // Dummy value. Cannot be zero since it will be batch-inverted. + F::ONE + } + }) + .collect::>(); + + combined = F::batch_multiplicative_inverse(&combined); + + for d in 0..degree { + if filter_col[d].is_zero() { + combined[d] = F::ZERO; + } + } + + batch_add_inplace(&mut acc, &combined); + } + + helper_columns.push(acc.into()); + } + assert_eq!(helper_columns.len(), num_helper_columns); + + helper_columns +} + +pub(crate) struct LookupCheckVars +where + F: Field, + FE: FieldExtension, + P: PackedField, +{ + pub(crate) local_values: Vec

, + pub(crate) next_values: Vec

, + pub(crate) challenges: Vec, +} + +/// Constraints for the logUp lookup argument. +pub(crate) fn eval_packed_lookups_generic( + stark: &S, + lookups: &[Lookup], + vars: &S::EvaluationFrame, + lookup_vars: LookupCheckVars, + yield_constr: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, + S: Stark, +{ + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + let degree = stark.constraint_degree(); + assert!( + degree == 2 || degree == 3, + "TODO: Allow other constraint degrees." + ); + let mut start = 0; + for lookup in lookups { + let num_helper_columns = lookup.num_helper_columns(degree); + for &challenge in &lookup_vars.challenges { + let grand_challenge = GrandProductChallenge { + beta: F::ONE, + gamma: challenge, + }; + let lookup_columns = lookup + .columns + .iter() + .map(|col| vec![col.eval_with_next(local_values, next_values)]) + .collect::>>(); + + // For each chunk, check that `h_i (x+f_2i) (x+f_{2i+1}) = (x+f_2i) * filter_{2i+1} + (x+f_{2i+1}) * filter_2i` + // if the chunk has length 2 or if it has length 1, check that `h_i * (x+f_2i) = filter_2i`, where x is the challenge + eval_helper_columns( + &lookup.filter_columns, + &lookup_columns, + local_values, + next_values, + &lookup_vars.local_values[start..start + num_helper_columns - 1], + degree, + &grand_challenge, + yield_constr, + ); + + let challenge = FE::from_basefield(challenge); + + // Check the `Z` polynomial. + let z = lookup_vars.local_values[start + num_helper_columns - 1]; + let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; + let table_with_challenge = lookup.table_column.eval(local_values) + challenge; + let y = lookup_vars.local_values[start..start + num_helper_columns - 1] + .iter() + .fold(P::ZEROS, |acc, x| acc + *x) + * table_with_challenge + - lookup.frequencies_column.eval(local_values); + // Check that in the first row, z = 0; + yield_constr.constraint_first_row(z); + yield_constr.constraint((next_z - z) * table_with_challenge - y); + start += num_helper_columns; + } + } +} + +pub(crate) struct LookupCheckVarsTarget { + pub(crate) local_values: Vec>, + pub(crate) next_values: Vec>, + pub(crate) challenges: Vec, +} + +pub(crate) fn eval_ext_lookups_circuit< + F: RichField + Extendable, + S: Stark, + const D: usize, +>( + builder: &mut CircuitBuilder, + stark: &S, + vars: &S::EvaluationFrameTarget, + lookup_vars: LookupCheckVarsTarget, + yield_constr: &mut RecursiveConstraintConsumer, +) { + let one = builder.one_extension(); + let degree = stark.constraint_degree(); + let lookups = stark.lookups(); + + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + assert!( + degree == 2 || degree == 3, + "TODO: Allow other constraint degrees." + ); + let mut start = 0; + for lookup in lookups { + let num_helper_columns = lookup.num_helper_columns(degree); + let col_values = lookup + .columns + .iter() + .map(|col| vec![col.eval_with_next_circuit(builder, local_values, next_values)]) + .collect::>(); + + for &challenge in &lookup_vars.challenges { + let grand_challenge = GrandProductChallenge { + beta: builder.one(), + gamma: challenge, + }; + + eval_helper_columns_circuit( + builder, + &lookup.filter_columns, + &col_values, + local_values, + next_values, + &lookup_vars.local_values[start..start + num_helper_columns - 1], + degree, + &grand_challenge, + yield_constr, + ); + let challenge = builder.convert_to_ext(challenge); + + let z = lookup_vars.local_values[start + num_helper_columns - 1]; + let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; + let table_column = lookup + .table_column + .eval_circuit(builder, vars.get_local_values()); + let table_with_challenge = builder.add_extension(table_column, challenge); + let mut y = builder.add_many_extension( + &lookup_vars.local_values[start..start + num_helper_columns - 1], + ); + + let frequencies_column = lookup + .frequencies_column + .eval_circuit(builder, vars.get_local_values()); + y = builder.mul_extension(y, table_with_challenge); + y = builder.sub_extension(y, frequencies_column); + + // Check that in the first row, z = 0; + yield_constr.constraint_first_row(builder, z); + let mut constraint = builder.sub_extension(next_z, z); + constraint = builder.mul_extension(constraint, table_with_challenge); + constraint = builder.sub_extension(constraint, y); + yield_constr.constraint(builder, constraint); + start += num_helper_columns; + } + } +} diff --git a/starky/src/permutation.rs b/starky/src/permutation.rs deleted file mode 100644 index 1059a79b7f..0000000000 --- a/starky/src/permutation.rs +++ /dev/null @@ -1,398 +0,0 @@ -//! Permutation arguments. - -use alloc::vec; -use alloc::vec::Vec; - -use itertools::Itertools; -use plonky2::field::batch_util::batch_multiply_inplace; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::iop::target::Target; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::{AlgebraicHasher, Hasher}; -use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; -use plonky2_maybe_rayon::*; - -use crate::config::StarkConfig; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::stark::Stark; - -/// A pair of lists of columns, `lhs` and `rhs`, that should be permutations of one another. -/// In particular, there should exist some permutation `pi` such that for any `i`, -/// `trace[lhs[i]] = pi(trace[rhs[i]])`. Here `trace` denotes the trace in column-major form, so -/// `trace[col]` is a column vector. -pub struct PermutationPair { - /// Each entry contains two column indices, representing two columns which should be - /// permutations of one another. - pub column_pairs: Vec<(usize, usize)>, -} - -impl PermutationPair { - pub fn singletons(lhs: usize, rhs: usize) -> Self { - Self { - column_pairs: vec![(lhs, rhs)], - } - } -} - -/// A single instance of a permutation check protocol. -pub(crate) struct PermutationInstance<'a, T: Copy> { - pub(crate) pair: &'a PermutationPair, - pub(crate) challenge: PermutationChallenge, -} - -/// Randomness for a single instance of a permutation check protocol. -#[derive(Copy, Clone)] -pub(crate) struct PermutationChallenge { - /// Randomness used to combine multiple columns into one. - pub(crate) beta: T, - /// Random offset that's added to the beta-reduced column values. - pub(crate) gamma: T, -} - -/// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. -#[derive(Clone)] -pub(crate) struct PermutationChallengeSet { - pub(crate) challenges: Vec>, -} - -/// Compute all Z polynomials (for permutation arguments). -pub(crate) fn compute_permutation_z_polys( - stark: &S, - config: &StarkConfig, - trace_poly_values: &[PolynomialValues], - permutation_challenge_sets: &[PermutationChallengeSet], -) -> Vec> -where - F: RichField + Extendable, - S: Stark, -{ - let permutation_pairs = stark.permutation_pairs(); - let permutation_batches = get_permutation_batches( - &permutation_pairs, - permutation_challenge_sets, - config.num_challenges, - stark.permutation_batch_size(), - ); - - permutation_batches - .into_par_iter() - .map(|instances| compute_permutation_z_poly(&instances, trace_poly_values)) - .collect() -} - -/// Compute a single Z polynomial. -fn compute_permutation_z_poly( - instances: &[PermutationInstance], - trace_poly_values: &[PolynomialValues], -) -> PolynomialValues { - let degree = trace_poly_values[0].len(); - let (reduced_lhs_polys, reduced_rhs_polys): (Vec<_>, Vec<_>) = instances - .iter() - .map(|instance| permutation_reduced_polys(instance, trace_poly_values, degree)) - .unzip(); - - let numerator = poly_product_elementwise(reduced_lhs_polys.into_iter()); - let denominator = poly_product_elementwise(reduced_rhs_polys.into_iter()); - - // Compute the quotients. - let denominator_inverses = F::batch_multiplicative_inverse(&denominator.values); - let mut quotients = numerator.values; - batch_multiply_inplace(&mut quotients, &denominator_inverses); - - // Compute Z, which contains partial products of the quotients. - let mut partial_products = Vec::with_capacity(degree); - let mut acc = F::ONE; - for q in quotients { - partial_products.push(acc); - acc *= q; - } - PolynomialValues::new(partial_products) -} - -/// Computes the reduced polynomial, `\sum beta^i f_i(x) + gamma`, for both the "left" and "right" -/// sides of a given `PermutationPair`. -fn permutation_reduced_polys( - instance: &PermutationInstance, - trace_poly_values: &[PolynomialValues], - degree: usize, -) -> (PolynomialValues, PolynomialValues) { - let PermutationInstance { - pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, - } = instance; - - let mut reduced_lhs = PolynomialValues::constant(*gamma, degree); - let mut reduced_rhs = PolynomialValues::constant(*gamma, degree); - for ((lhs, rhs), weight) in column_pairs.iter().zip(beta.powers()) { - reduced_lhs.add_assign_scaled(&trace_poly_values[*lhs], weight); - reduced_rhs.add_assign_scaled(&trace_poly_values[*rhs], weight); - } - (reduced_lhs, reduced_rhs) -} - -/// Computes the elementwise product of a set of polynomials. Assumes that the set is non-empty and -/// that each polynomial has the same length. -fn poly_product_elementwise( - mut polys: impl Iterator>, -) -> PolynomialValues { - let mut product = polys.next().expect("Expected at least one polynomial"); - for poly in polys { - batch_multiply_inplace(&mut product.values, &poly.values) - } - product -} - -fn get_permutation_challenge>( - challenger: &mut Challenger, -) -> PermutationChallenge { - let beta = challenger.get_challenge(); - let gamma = challenger.get_challenge(); - PermutationChallenge { beta, gamma } -} - -fn get_permutation_challenge_set>( - challenger: &mut Challenger, - num_challenges: usize, -) -> PermutationChallengeSet { - let challenges = (0..num_challenges) - .map(|_| get_permutation_challenge(challenger)) - .collect(); - PermutationChallengeSet { challenges } -} - -pub(crate) fn get_n_permutation_challenge_sets>( - challenger: &mut Challenger, - num_challenges: usize, - num_sets: usize, -) -> Vec> { - (0..num_sets) - .map(|_| get_permutation_challenge_set(challenger, num_challenges)) - .collect() -} - -fn get_permutation_challenge_target< - F: RichField + Extendable, - H: AlgebraicHasher, - const D: usize, ->( - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, -) -> PermutationChallenge { - let beta = challenger.get_challenge(builder); - let gamma = challenger.get_challenge(builder); - PermutationChallenge { beta, gamma } -} - -fn get_permutation_challenge_set_target< - F: RichField + Extendable, - H: AlgebraicHasher, - const D: usize, ->( - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, - num_challenges: usize, -) -> PermutationChallengeSet { - let challenges = (0..num_challenges) - .map(|_| get_permutation_challenge_target(builder, challenger)) - .collect(); - PermutationChallengeSet { challenges } -} - -pub(crate) fn get_n_permutation_challenge_sets_target< - F: RichField + Extendable, - H: AlgebraicHasher, - const D: usize, ->( - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, - num_challenges: usize, - num_sets: usize, -) -> Vec> { - (0..num_sets) - .map(|_| get_permutation_challenge_set_target(builder, challenger, num_challenges)) - .collect() -} - -/// Get a list of instances of our batch-permutation argument. These are permutation arguments -/// where the same `Z(x)` polynomial is used to check more than one permutation. -/// Before batching, each permutation pair leads to `num_challenges` permutation arguments, so we -/// start with the cartesian product of `permutation_pairs` and `0..num_challenges`. Then we -/// chunk these arguments based on our batch size. -pub(crate) fn get_permutation_batches<'a, T: Copy>( - permutation_pairs: &'a [PermutationPair], - permutation_challenge_sets: &[PermutationChallengeSet], - num_challenges: usize, - batch_size: usize, -) -> Vec>> { - permutation_pairs - .iter() - .cartesian_product(0..num_challenges) - .chunks(batch_size) - .into_iter() - .map(|batch| { - batch - .enumerate() - .map(|(i, (pair, chal))| { - let challenge = permutation_challenge_sets[i].challenges[chal]; - PermutationInstance { pair, challenge } - }) - .collect_vec() - }) - .collect() -} - -pub struct PermutationCheckVars -where - F: Field, - FE: FieldExtension, - P: PackedField, -{ - pub(crate) local_zs: Vec

, - pub(crate) next_zs: Vec

, - pub(crate) permutation_challenge_sets: Vec>, -} - -pub(crate) fn eval_permutation_checks( - stark: &S, - config: &StarkConfig, - vars: &S::EvaluationFrame, - permutation_data: PermutationCheckVars, - consumer: &mut ConstraintConsumer

, -) where - F: RichField + Extendable, - FE: FieldExtension, - P: PackedField, - S: Stark, -{ - let local_values = vars.get_local_values(); - - let PermutationCheckVars { - local_zs, - next_zs, - permutation_challenge_sets, - } = permutation_data; - - // Check that Z(1) = 1; - for &z in &local_zs { - consumer.constraint_first_row(z - FE::ONE); - } - - let permutation_pairs = stark.permutation_pairs(); - - let permutation_batches = get_permutation_batches( - &permutation_pairs, - &permutation_challenge_sets, - config.num_challenges, - stark.permutation_batch_size(), - ); - - // Each zs value corresponds to a permutation batch. - for (i, instances) in permutation_batches.iter().enumerate() { - // Z(gx) * down = Z x * up - let (reduced_lhs, reduced_rhs): (Vec

, Vec

) = instances - .iter() - .map(|instance| { - let PermutationInstance { - pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, - } = instance; - let mut factor = ReducingFactor::new(*beta); - let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs - .iter() - .map(|&(i, j)| (local_values[i], local_values[j])) - .unzip(); - ( - factor.reduce_ext(lhs.into_iter()) + FE::from_basefield(*gamma), - factor.reduce_ext(rhs.into_iter()) + FE::from_basefield(*gamma), - ) - }) - .unzip(); - let constraint = next_zs[i] * reduced_rhs.into_iter().product::

() - - local_zs[i] * reduced_lhs.into_iter().product::

(); - consumer.constraint(constraint); - } -} - -pub struct PermutationCheckDataTarget { - pub(crate) local_zs: Vec>, - pub(crate) next_zs: Vec>, - pub(crate) permutation_challenge_sets: Vec>, -} - -pub(crate) fn eval_permutation_checks_circuit( - builder: &mut CircuitBuilder, - stark: &S, - config: &StarkConfig, - vars: &S::EvaluationFrameTarget, - permutation_data: PermutationCheckDataTarget, - consumer: &mut RecursiveConstraintConsumer, -) where - F: RichField + Extendable, - S: Stark, -{ - let local_values = vars.get_local_values(); - - let PermutationCheckDataTarget { - local_zs, - next_zs, - permutation_challenge_sets, - } = permutation_data; - - let one = builder.one_extension(); - // Check that Z(1) = 1; - for &z in &local_zs { - let z_1 = builder.sub_extension(z, one); - consumer.constraint_first_row(builder, z_1); - } - - let permutation_pairs = stark.permutation_pairs(); - - let permutation_batches = get_permutation_batches( - &permutation_pairs, - &permutation_challenge_sets, - config.num_challenges, - stark.permutation_batch_size(), - ); - - // Each zs value corresponds to a permutation batch. - for (i, instances) in permutation_batches.iter().enumerate() { - let (reduced_lhs, reduced_rhs): (Vec>, Vec>) = - instances - .iter() - .map(|instance| { - let PermutationInstance { - pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, - } = instance; - let beta_ext = builder.convert_to_ext(*beta); - let gamma_ext = builder.convert_to_ext(*gamma); - let mut factor = ReducingFactorTarget::new(beta_ext); - let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs - .iter() - .map(|&(i, j)| (local_values[i], local_values[j])) - .unzip(); - let reduced_lhs = factor.reduce(&lhs, builder); - let reduced_rhs = factor.reduce(&rhs, builder); - ( - builder.add_extension(reduced_lhs, gamma_ext), - builder.add_extension(reduced_rhs, gamma_ext), - ) - }) - .unzip(); - let reduced_lhs_product = builder.mul_many_extension(reduced_lhs); - let reduced_rhs_product = builder.mul_many_extension(reduced_rhs); - // constraint = next_zs[i] * reduced_rhs_product - local_zs[i] * reduced_lhs_product - let constraint = { - let tmp = builder.mul_extension(local_zs[i], reduced_lhs_product); - builder.mul_sub_extension(next_zs[i], reduced_rhs_product, tmp) - }; - consumer.constraint(builder, constraint) - } -} diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 6bd5f78761..e22399288e 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -18,14 +18,14 @@ use plonky2::plonk::config::GenericConfig; use plonky2_maybe_rayon::*; use crate::config::StarkConfig; -use crate::permutation::PermutationChallengeSet; +use crate::lookup::GrandProductChallengeSet; #[derive(Debug, Clone)] pub struct StarkProof, C: GenericConfig, const D: usize> { /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, /// Merkle cap of LDEs of permutation Z values. - pub permutation_zs_cap: Option>, + pub auxiliary_polys_cap: Option>, /// Merkle cap of LDEs of trace values. pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. @@ -48,7 +48,7 @@ impl, C: GenericConfig, const D: usize> S pub struct StarkProofTarget { pub trace_cap: MerkleCapTarget, - pub permutation_zs_cap: Option, + pub auxiliary_polys_cap: Option, pub quotient_polys_cap: MerkleCapTarget, pub openings: StarkOpeningSetTarget, pub opening_proof: FriProofTarget, @@ -106,7 +106,7 @@ pub struct CompressedStarkProofWithPublicInputs< pub(crate) struct StarkProofChallenges, const D: usize> { /// Randomness used in any permutation arguments. - pub permutation_challenge_sets: Option>>, + pub lookup_challenge_set: Option>, /// Random values used to combine STARK constraints. pub stark_alphas: Vec, @@ -118,7 +118,7 @@ pub(crate) struct StarkProofChallenges, const D: us } pub(crate) struct StarkProofChallengesTarget { - pub permutation_challenge_sets: Option>>, + pub lookup_challenge_set: Option>, pub stark_alphas: Vec, pub stark_zeta: ExtensionTarget, pub fri_challenges: FriChallengesTarget, @@ -129,8 +129,8 @@ pub(crate) struct StarkProofChallengesTarget { pub struct StarkOpeningSet, const D: usize> { pub local_values: Vec, pub next_values: Vec, - pub permutation_zs: Option>, - pub permutation_zs_next: Option>, + pub auxiliary_polys: Option>, + pub auxiliary_polys_next: Option>, pub quotient_polys: Vec, } @@ -139,7 +139,7 @@ impl, const D: usize> StarkOpeningSet { zeta: F::Extension, g: F, trace_commitment: &PolynomialBatch, - permutation_zs_commitment: Option<&PolynomialBatch>, + auxiliary_polys_commitment: Option<&PolynomialBatch>, quotient_commitment: &PolynomialBatch, ) -> Self { let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { @@ -152,8 +152,8 @@ impl, const D: usize> StarkOpeningSet { Self { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta_next, trace_commitment), - permutation_zs: permutation_zs_commitment.map(|c| eval_commitment(zeta, c)), - permutation_zs_next: permutation_zs_commitment.map(|c| eval_commitment(zeta_next, c)), + auxiliary_polys: auxiliary_polys_commitment.map(|c| eval_commitment(zeta, c)), + auxiliary_polys_next: auxiliary_polys_commitment.map(|c| eval_commitment(zeta_next, c)), quotient_polys: eval_commitment(zeta, quotient_commitment), } } @@ -163,7 +163,7 @@ impl, const D: usize> StarkOpeningSet { values: self .local_values .iter() - .chain(self.permutation_zs.iter().flatten()) + .chain(self.auxiliary_polys.iter().flatten()) .chain(&self.quotient_polys) .copied() .collect_vec(), @@ -172,7 +172,7 @@ impl, const D: usize> StarkOpeningSet { values: self .next_values .iter() - .chain(self.permutation_zs_next.iter().flatten()) + .chain(self.auxiliary_polys_next.iter().flatten()) .copied() .collect_vec(), }; @@ -185,8 +185,8 @@ impl, const D: usize> StarkOpeningSet { pub struct StarkOpeningSetTarget { pub local_values: Vec>, pub next_values: Vec>, - pub permutation_zs: Option>>, - pub permutation_zs_next: Option>>, + pub auxiliary_polys: Option>>, + pub auxiliary_polys_next: Option>>, pub quotient_polys: Vec>, } @@ -196,7 +196,7 @@ impl StarkOpeningSetTarget { values: self .local_values .iter() - .chain(self.permutation_zs.iter().flatten()) + .chain(self.auxiliary_polys.iter().flatten()) .chain(&self.quotient_polys) .copied() .collect_vec(), @@ -205,7 +205,7 @@ impl StarkOpeningSetTarget { values: self .next_values .iter() - .chain(self.permutation_zs_next.iter().flatten()) + .chain(self.auxiliary_polys_next.iter().flatten()) .copied() .collect_vec(), }; diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 56154d9105..f9b40217d6 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -21,9 +21,8 @@ use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::permutation::{ - compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, - PermutationCheckVars, +use crate::lookup::{ + get_grand_product_challenge_set, lookup_helper_columns, Lookup, LookupCheckVars, }; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; @@ -69,25 +68,45 @@ where let mut challenger = Challenger::new(); challenger.observe_cap(&trace_cap); - // Permutation arguments. - let permutation_zs_commitment_challenges = stark.uses_permutation_args().then(|| { - let permutation_challenge_sets = get_n_permutation_challenge_sets( - &mut challenger, - config.num_challenges, - stark.permutation_batch_size(), - ); - let permutation_z_polys = compute_permutation_z_polys::( - &stark, - config, - &trace_poly_values, - &permutation_challenge_sets, - ); + // Lookup argument. + let constraint_degree = stark.constraint_degree(); + let lookups = stark.lookups(); + let lookup_challenges = stark.uses_lookups().then(|| { + get_grand_product_challenge_set(&mut challenger, config.num_challenges) + .challenges + .iter() + .map(|ch| ch.beta) + .collect::>() + }); + + let num_lookup_columns = lookups + .iter() + .map(|l| l.num_helper_columns(constraint_degree)) + .sum(); - let permutation_zs_commitment = timed!( + let auxiliary_polys_commitment = stark.uses_lookups().then(|| { + let lookup_helper_columns = timed!(timing, "compute lookup helper columns", { + let challenges = lookup_challenges.as_ref().expect("We do have challenges."); + let mut columns = Vec::with_capacity(num_lookup_columns); + for lookup in &lookups { + for &challenge in challenges { + columns.extend(lookup_helper_columns( + lookup, + &trace_poly_values, + challenge, + constraint_degree, + )); + } + } + columns + }); + + // Get the polynomial commitments for all auxiliary polynomials. + let auxiliary_polys_commitment = timed!( timing, "compute permutation Z commitments", PolynomialBatch::from_values( - permutation_z_polys, + lookup_helper_columns, rate_bits, false, config.fri_config.cap_height, @@ -95,38 +114,68 @@ where None, ) ); - (permutation_zs_commitment, permutation_challenge_sets) + + auxiliary_polys_commitment }); - let permutation_zs_commitment = permutation_zs_commitment_challenges - .as_ref() - .map(|(comm, _)| comm); - let permutation_zs_cap = permutation_zs_commitment + + let auxiliary_polys_cap = auxiliary_polys_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); - if let Some(cap) = &permutation_zs_cap { + if let Some(cap) = &auxiliary_polys_cap { challenger.observe_cap(cap); } let alphas = challenger.get_n_challenges(config.num_challenges); - let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( - &stark, - &trace_commitment, - &permutation_zs_commitment_challenges, - public_inputs, - alphas, - degree_bits, - config, + + #[cfg(test)] + { + check_constraints( + &stark, + &trace_commitment, + public_inputs, + &auxiliary_polys_commitment, + lookup_challenges.as_ref(), + &lookups, + alphas.clone(), + degree_bits, + num_lookup_columns, + ); + } + + let quotient_polys = timed!( + timing, + "compute quotient polys", + compute_quotient_polys::::Packing, C, S, D>( + &stark, + &trace_commitment, + &auxiliary_polys_commitment, + lookup_challenges.as_ref(), + &lookups, + public_inputs, + alphas, + degree_bits, + num_lookup_columns, + config, + ) ); - let all_quotient_chunks = quotient_polys - .into_par_iter() - .flat_map(|mut quotient_poly| { - quotient_poly - .trim_to_len(degree * stark.quotient_degree_factor()) - .expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H"); - // Split quotient into degree-n chunks. - quotient_poly.chunks(degree) - }) - .collect(); + + let all_quotient_chunks = timed!( + timing, + "split quotient polys", + quotient_polys + .into_par_iter() + .flat_map(|mut quotient_poly| { + quotient_poly + .trim_to_len(degree * stark.quotient_degree_factor()) + .expect( + "Quotient has failed, the vanishing polynomial is not divisible by Z_H", + ); + // Split quotient into degree-n chunks. + quotient_poly.chunks(degree) + }) + .collect() + ); + let quotient_commitment = timed!( timing, "compute quotient commitment", @@ -139,6 +188,8 @@ where None, ) ); + + // Observe the quotient polynomials Merkle cap. let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); challenger.observe_cap("ient_polys_cap); @@ -151,17 +202,21 @@ where zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, "Opening point is in the subgroup." ); + + // Compute all openings: evaluate all committed polynomials at `zeta` and, when necessary, at `g * zeta`. let openings = StarkOpeningSet::new( zeta, g, &trace_commitment, - permutation_zs_commitment, + auxiliary_polys_commitment.as_ref(), "ient_commitment, ); + + // Get the FRI openings and observe them. challenger.observe_openings(&openings.to_fri_openings()); let initial_merkle_trees = once(&trace_commitment) - .chain(permutation_zs_commitment) + .chain(&auxiliary_polys_commitment) .chain(once("ient_commitment)) .collect_vec(); @@ -178,7 +233,7 @@ where ); let proof = StarkProof { trace_cap, - permutation_zs_cap, + auxiliary_polys_cap, quotient_polys_cap, openings, opening_proof, @@ -195,13 +250,13 @@ where fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, - permutation_zs_commitment_challenges: &'a Option<( - PolynomialBatch, - Vec>, - )>, + auxiliary_polys_commitment: &'a Option>, + lookup_challenges: Option<&'a Vec>, + lookups: &[Lookup], public_inputs: &[F], alphas: Vec, degree_bits: usize, + num_lookup_columns: usize, config: &StarkConfig, ) -> Vec> where @@ -263,23 +318,35 @@ where lagrange_basis_first, lagrange_basis_last, ); + // Get the local and next row evaluations for the current STARK, + // as well as the public inputs. let vars = S::EvaluationFrame::from_values( &get_trace_values_packed(i_start), &get_trace_values_packed(i_next_start), public_inputs, ); - let permutation_check_data = permutation_zs_commitment_challenges.as_ref().map( - |(permutation_zs_commitment, permutation_challenge_sets)| PermutationCheckVars { - local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step), - next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step), - permutation_challenge_sets: permutation_challenge_sets.to_vec(), - }, - ); + // Get the local and next row evaluations for the permutation argument, + // as well as the associated challenges. + let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars { + local_values: auxiliary_polys_commitment + .as_ref() + .unwrap() + .get_lde_values_packed(i_start, step) + .to_vec(), + next_values: auxiliary_polys_commitment + .as_ref() + .unwrap() + .get_lde_values_packed(i_next_start, step), + challenges: challenges.to_vec(), + }); + + // Evaluate the polynomial combining all constraints, including + // those associated to the permutation arguments. eval_vanishing_poly::( stark, - config, &vars, - permutation_check_data, + lookups, + lookup_vars, &mut consumer, ); @@ -307,3 +374,102 @@ where .map(|values| values.coset_ifft(F::coset_shift())) .collect() } + +#[cfg(test)] +/// Check that all constraints evaluate to zero on `H`. +/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. +fn check_constraints<'a, F, C, S, const D: usize>( + stark: &S, + trace_commitment: &'a PolynomialBatch, + public_inputs: &[F], + auxiliary_commitment: &'a Option>, + lookup_challenges: Option<&'a Vec>, + lookups: &[Lookup], + alphas: Vec, + degree_bits: usize, + num_lookup_columns: usize, +) where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + let degree = 1 << degree_bits; + let rate_bits = 0; // Set this to higher value to check constraint degree. + + let size = degree << rate_bits; + let step = 1 << rate_bits; + + // Evaluation of the first Lagrange polynomial. + let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits); + // Evaluation of the last Lagrange polynomial. + let lagrange_last = PolynomialValues::selector(degree, degree - 1).lde(rate_bits); + + let subgroup = F::two_adic_subgroup(degree_bits + rate_bits); + + // Get the evaluations of a batch of polynomials over our subgroup. + let get_subgroup_evals = |comm: &PolynomialBatch| -> Vec> { + let values = comm + .polynomials + .par_iter() + .map(|coeffs| coeffs.clone().fft().values) + .collect::>(); + transpose(&values) + }; + + // Get batch evaluations of the trace and permutation polynomials over our subgroup. + let trace_subgroup_evals = get_subgroup_evals(trace_commitment); + let auxiliary_subgroup_evals = auxiliary_commitment.as_ref().map(get_subgroup_evals); + + // Last element of the subgroup. + let last = F::primitive_root_of_unity(degree_bits).inverse(); + + let constraint_values = (0..size) + .map(|i| { + let i_next = (i + step) % size; + + let x = subgroup[i]; + let z_last = x - last; + let lagrange_basis_first = lagrange_first.values[i]; + let lagrange_basis_last = lagrange_last.values[i]; + + let mut consumer = ConstraintConsumer::new( + alphas.clone(), + z_last, + lagrange_basis_first, + lagrange_basis_last, + ); + // Get the local and next row evaluations for the current STARK's trace. + let vars = S::EvaluationFrame::from_values( + &trace_subgroup_evals[i], + &trace_subgroup_evals[i_next], + public_inputs, + ); + // Get the local and next row evaluations for the current STARK's permutation argument. + let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars { + local_values: auxiliary_subgroup_evals.as_ref().unwrap()[i].clone(), + next_values: auxiliary_subgroup_evals.as_ref().unwrap()[i_next].clone(), + challenges: challenges.to_vec(), + }); + + // Evaluate the polynomial combining all constraints, including those associated + // to the permutation arguments. + eval_vanishing_poly::( + stark, + &vars, + lookups, + lookup_vars, + &mut consumer, + ); + consumer.accumulators() + }) + .collect::>(); + + // Assert that all constraints evaluate to 0 over our subgroup. + for v in constraint_values { + assert!( + v.iter().all(|x| x.is_zero()), + "Constraint failed in {}", + core::any::type_name::() + ); + } +} diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 18db561be2..e91583f19b 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -1,3 +1,4 @@ +use alloc::vec; use alloc::vec::Vec; use core::iter::once; @@ -17,7 +18,7 @@ use plonky2::with_context; use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::permutation::PermutationCheckDataTarget; +use crate::lookup::LookupCheckVarsTarget; use crate::proof::{ StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, StarkProofTarget, StarkProofWithPublicInputs, StarkProofWithPublicInputsTarget, @@ -43,7 +44,7 @@ pub fn verify_stark_proof_circuit< let challenges = with_context!( builder, "compute challenges", - proof_with_pis.get_challenges::(builder, &stark, inner_config) + proof_with_pis.get_challenges::(builder, inner_config) ); verify_stark_proof_with_challenges_circuit::( @@ -72,7 +73,7 @@ fn verify_stark_proof_with_challenges_circuit< ) where C::Hasher: AlgebraicHasher, { - check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap(); + check_lookup_options(&stark, &proof_with_pis, &challenges).unwrap(); let one = builder.one_extension(); let StarkProofWithPublicInputsTarget { @@ -82,8 +83,8 @@ fn verify_stark_proof_with_challenges_circuit< let StarkOpeningSetTarget { local_values, next_values, - permutation_zs, - permutation_zs_next, + auxiliary_polys, + auxiliary_polys_next, quotient_polys, } = &proof.openings; @@ -112,25 +113,27 @@ fn verify_stark_proof_with_challenges_circuit< l_last, ); - let permutation_data = stark - .uses_permutation_args() - .then(|| PermutationCheckDataTarget { - local_zs: permutation_zs.as_ref().unwrap().clone(), - next_zs: permutation_zs_next.as_ref().unwrap().clone(), - permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), - }); + let num_lookup_columns = stark.num_lookup_helper_columns(inner_config); + let lookup_challenges = stark.uses_lookups().then(|| { + challenges + .lookup_challenge_set + .unwrap() + .challenges + .iter() + .map(|ch| ch.beta) + .collect::>() + }); + + let lookup_vars = stark.uses_lookups().then(|| LookupCheckVarsTarget { + local_values: auxiliary_polys.as_ref().unwrap()[..num_lookup_columns].to_vec(), + next_values: auxiliary_polys_next.as_ref().unwrap()[..num_lookup_columns].to_vec(), + challenges: lookup_challenges.unwrap(), + }); with_context!( builder, "evaluate vanishing polynomial", - eval_vanishing_poly_circuit::( - builder, - &stark, - inner_config, - &vars, - permutation_data, - &mut consumer, - ) + eval_vanishing_poly_circuit::(builder, &stark, &vars, lookup_vars, &mut consumer) ); let vanishing_polys_zeta = consumer.accumulators(); @@ -146,7 +149,7 @@ fn verify_stark_proof_with_challenges_circuit< } let merkle_caps = once(proof.trace_cap) - .chain(proof.permutation_zs_cap) + .chain(proof.auxiliary_polys_cap) .chain(once(proof.quotient_polys_cap)) .collect_vec(); @@ -212,22 +215,19 @@ pub fn add_virtual_stark_proof, S: Stark, con let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; - let num_leaves_per_oracle = once(S::COLUMNS) - .chain( - stark - .uses_permutation_args() - .then(|| stark.num_permutation_batches(config)), - ) - .chain(once(stark.quotient_degree_factor() * config.num_challenges)) - .collect_vec(); + let num_leaves_per_oracle = vec![ + S::COLUMNS, + stark.num_lookup_helper_columns(config), + stark.quotient_degree_factor() * config.num_challenges, + ]; - let permutation_zs_cap = stark - .uses_permutation_args() + let auxiliary_polys_cap = stark + .uses_lookups() .then(|| builder.add_virtual_cap(cap_height)); StarkProofTarget { trace_cap: builder.add_virtual_cap(cap_height), - permutation_zs_cap, + auxiliary_polys_cap, quotient_polys_cap: builder.add_virtual_cap(cap_height), openings: add_stark_opening_set_target::(builder, stark, config), opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), @@ -243,12 +243,12 @@ fn add_stark_opening_set_target, S: Stark, co StarkOpeningSetTarget { local_values: builder.add_virtual_extension_targets(S::COLUMNS), next_values: builder.add_virtual_extension_targets(S::COLUMNS), - permutation_zs: stark - .uses_permutation_args() - .then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))), - permutation_zs_next: stark - .uses_permutation_args() - .then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))), + auxiliary_polys: stark.uses_lookups().then(|| { + builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config)) + }), + auxiliary_polys_next: stark.uses_lookups().then(|| { + builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config)) + }), quotient_polys: builder .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), } @@ -297,33 +297,34 @@ pub fn set_stark_proof_target, W, const D: usize>( &proof.openings.to_fri_openings(), ); - if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = - (&proof_target.permutation_zs_cap, &proof.permutation_zs_cap) - { - witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap); + if let (Some(auxiliary_polys_cap_target), Some(auxiliary_polys_cap)) = ( + &proof_target.auxiliary_polys_cap, + &proof.auxiliary_polys_cap, + ) { + witness.set_cap_target(auxiliary_polys_cap_target, auxiliary_polys_cap); } set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof); } -/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff +/// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff /// the Stark uses a permutation argument. -fn check_permutation_options, S: Stark, const D: usize>( +fn check_lookup_options, S: Stark, const D: usize>( stark: &S, proof_with_pis: &StarkProofWithPublicInputsTarget, challenges: &StarkProofChallengesTarget, ) -> Result<()> { let options_is_some = [ - proof_with_pis.proof.permutation_zs_cap.is_some(), - proof_with_pis.proof.openings.permutation_zs.is_some(), - proof_with_pis.proof.openings.permutation_zs_next.is_some(), - challenges.permutation_challenge_sets.is_some(), + proof_with_pis.proof.auxiliary_polys_cap.is_some(), + proof_with_pis.proof.openings.auxiliary_polys.is_some(), + proof_with_pis.proof.openings.auxiliary_polys_next.is_some(), + challenges.lookup_challenge_set.is_some(), ]; ensure!( options_is_some .into_iter() - .all(|b| b == stark.uses_permutation_args()), - "Permutation data doesn't match with Stark configuration." + .all(|b| b == stark.uses_lookups()), + "Lookups data doesn't match with Stark configuration." ); Ok(()) } diff --git a/starky/src/stark.rs b/starky/src/stark.rs index 1e7b07117b..a9f2b2602f 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; +use plonky2::field::types::Field; use plonky2::fri::structure::{ FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo, FriPolynomialInfo, @@ -10,12 +11,15 @@ use plonky2::fri::structure::{ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::util::ceil_div_usize; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::permutation::PermutationPair; +use crate::lookup::Lookup; + +const TRACE_ORACLE_INDEX: usize = 0; +const AUXILIARY_ORACLE_INDEX: usize = 1; +const QUOTIENT_ORACLE_INDEX: usize = 2; /// Represents a STARK system. pub trait Stark, const D: usize>: Sync { @@ -94,49 +98,47 @@ pub trait Stark, const D: usize>: Sync { g: F, config: &StarkConfig, ) -> FriInstanceInfo { - let mut oracles = vec![]; - - let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS); - oracles.push(FriOracleInfo { + let trace_oracle = FriOracleInfo { num_polys: Self::COLUMNS, blinding: false, - }); - - let permutation_zs_info = if self.uses_permutation_args() { - let num_z_polys = self.num_permutation_batches(config); - let polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_z_polys); - oracles.push(FriOracleInfo { - num_polys: num_z_polys, - blinding: false, - }); - polys - } else { - vec![] }; + let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); + + let num_lookup_columns = self.num_lookup_helper_columns(config); + let num_auxiliary_polys = num_lookup_columns; + let auxiliary_oracle = FriOracleInfo { + num_polys: num_auxiliary_polys, + blinding: false, + }; + let auxiliary_polys_info = + FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); - let num_quotient_polys = self.quotient_degree_factor() * config.num_challenges; - let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); - oracles.push(FriOracleInfo { + let num_quotient_polys = self.num_quotient_polys(config); + let quotient_oracle = FriOracleInfo { num_polys: num_quotient_polys, blinding: false, - }); + }; + let quotient_info = + FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); let zeta_batch = FriBatchInfo { point: zeta, polynomials: [ trace_info.clone(), - permutation_zs_info.clone(), + auxiliary_polys_info.clone(), quotient_info, ] .concat(), }; let zeta_next_batch = FriBatchInfo { point: zeta.scalar_mul(g), - polynomials: [trace_info, permutation_zs_info].concat(), + polynomials: [trace_info, auxiliary_polys_info].concat(), }; - let batches = vec![zeta_batch, zeta_next_batch]; - FriInstanceInfo { oracles, batches } + FriInstanceInfo { + oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], + batches: vec![zeta_batch, zeta_next_batch], + } } /// Computes the FRI instance used to prove this Stark. @@ -147,38 +149,34 @@ pub trait Stark, const D: usize>: Sync { g: F, config: &StarkConfig, ) -> FriInstanceInfoTarget { - let mut oracles = vec![]; - - let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS); - oracles.push(FriOracleInfo { + let trace_oracle = FriOracleInfo { num_polys: Self::COLUMNS, blinding: false, - }); - - let permutation_zs_info = if self.uses_permutation_args() { - let num_z_polys = self.num_permutation_batches(config); - let polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_z_polys); - oracles.push(FriOracleInfo { - num_polys: num_z_polys, - blinding: false, - }); - polys - } else { - vec![] }; + let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); - let num_quotient_polys = self.quotient_degree_factor() * config.num_challenges; - let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); - oracles.push(FriOracleInfo { + let num_lookup_columns = self.num_lookup_helper_columns(config); + let num_auxiliary_polys = num_lookup_columns; + let auxiliary_oracle = FriOracleInfo { + num_polys: num_auxiliary_polys, + blinding: false, + }; + let auxiliary_polys_info = + FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); + + let num_quotient_polys = self.num_quotient_polys(config); + let quotient_oracle = FriOracleInfo { num_polys: num_quotient_polys, blinding: false, - }); + }; + let quotient_info = + FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); let zeta_batch = FriBatchInfoTarget { point: zeta, polynomials: [ trace_info.clone(), - permutation_zs_info.clone(), + auxiliary_polys_info.clone(), quotient_info, ] .concat(), @@ -186,40 +184,28 @@ pub trait Stark, const D: usize>: Sync { let zeta_next = builder.mul_const_extension(g, zeta); let zeta_next_batch = FriBatchInfoTarget { point: zeta_next, - polynomials: [trace_info, permutation_zs_info].concat(), + polynomials: [trace_info, auxiliary_polys_info].concat(), }; - let batches = vec![zeta_batch, zeta_next_batch]; - FriInstanceInfoTarget { oracles, batches } + FriInstanceInfoTarget { + oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], + batches: vec![zeta_batch, zeta_next_batch], + } } - /// Pairs of lists of columns that should be permutations of one another. A permutation argument - /// will be used for each such pair. Empty by default. - fn permutation_pairs(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![] } - fn uses_permutation_args(&self) -> bool { - !self.permutation_pairs().is_empty() - } - - /// The number of permutation argument instances that can be combined into a single constraint. - fn permutation_batch_size(&self) -> usize { - // The permutation argument constraints look like - // Z(x) \prod(...) = Z(g x) \prod(...) - // where each product has a number of terms equal to the batch size. So our batch size - // should be one less than our constraint degree, which happens to be our quotient degree. - self.quotient_degree_factor() - } - - fn num_permutation_instances(&self, config: &StarkConfig) -> usize { - self.permutation_pairs().len() * config.num_challenges + fn num_lookup_helper_columns(&self, config: &StarkConfig) -> usize { + self.lookups() + .iter() + .map(|lookup| lookup.num_helper_columns(self.constraint_degree())) + .sum::() + * config.num_challenges } - fn num_permutation_batches(&self, config: &StarkConfig) -> usize { - ceil_div_usize( - self.num_permutation_instances(config), - self.permutation_batch_size(), - ) + fn uses_lookups(&self) -> bool { + !self.lookups().is_empty() } } diff --git a/starky/src/vanishing_poly.rs b/starky/src/vanishing_poly.rs index 0a399dce53..6a179fe27a 100644 --- a/starky/src/vanishing_poly.rs +++ b/starky/src/vanishing_poly.rs @@ -3,19 +3,18 @@ use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; -use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::permutation::{ - eval_permutation_checks, eval_permutation_checks_circuit, PermutationCheckDataTarget, - PermutationCheckVars, +use crate::lookup::{ + eval_ext_lookups_circuit, eval_packed_lookups_generic, Lookup, LookupCheckVars, + LookupCheckVarsTarget, }; use crate::stark::Stark; pub(crate) fn eval_vanishing_poly( stark: &S, - config: &StarkConfig, vars: &S::EvaluationFrame, - permutation_data: Option>, + lookups: &[Lookup], + lookup_vars: Option>, consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -24,12 +23,13 @@ pub(crate) fn eval_vanishing_poly( S: Stark, { stark.eval_packed_generic(vars, consumer); - if let Some(permutation_data) = permutation_data { - eval_permutation_checks::( + if let Some(lookup_vars) = lookup_vars { + // Evaluate the STARK constraints related to the permutation arguments. + eval_packed_lookups_generic::( stark, - config, + lookups, vars, - permutation_data, + lookup_vars, consumer, ); } @@ -38,23 +38,16 @@ pub(crate) fn eval_vanishing_poly( pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, - config: &StarkConfig, vars: &S::EvaluationFrameTarget, - permutation_data: Option>, + lookup_vars: Option>, consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, S: Stark, { stark.eval_ext_circuit(builder, vars, consumer); - if let Some(permutation_data) = permutation_data { - eval_permutation_checks_circuit::( - builder, - stark, - config, - vars, - permutation_data, - consumer, - ); + if let Some(lookup_vars) = lookup_vars { + // Evaluate all of the STARK's constraints related to the permutation argument. + eval_ext_lookups_circuit::(builder, stark, vars, lookup_vars, consumer); } } diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 28b9a3e2b3..577405ef4f 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -7,13 +7,14 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::types::Field; use plonky2::fri::verifier::verify_fri_proof; use plonky2::hash::hash_types::RichField; +use plonky2::hash::merkle_tree::MerkleCap; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::permutation::PermutationCheckVars; +use crate::lookup::LookupCheckVars; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; @@ -30,7 +31,7 @@ pub fn verify_stark_proof< ) -> Result<()> { ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); let degree_bits = proof_with_pis.proof.recover_degree_bits(config); - let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits); + let challenges = proof_with_pis.get_challenges(config, degree_bits); verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) } @@ -47,7 +48,7 @@ pub(crate) fn verify_stark_proof_with_challenges< config: &StarkConfig, ) -> Result<()> { validate_proof_shape(&stark, &proof_with_pis, config)?; - check_permutation_options(&stark, &proof_with_pis, &challenges)?; + let StarkProofWithPublicInputs { proof, public_inputs, @@ -55,8 +56,8 @@ pub(crate) fn verify_stark_proof_with_challenges< let StarkOpeningSet { local_values, next_values, - permutation_zs, - permutation_zs_next, + auxiliary_polys, + auxiliary_polys_next, quotient_polys, } = &proof.openings; let vars = S::EvaluationFrame::from_values( @@ -81,16 +82,30 @@ pub(crate) fn verify_stark_proof_with_challenges< l_0, l_last, ); - let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { - local_zs: permutation_zs.as_ref().unwrap().clone(), - next_zs: permutation_zs_next.as_ref().unwrap().clone(), - permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), + + let num_lookup_columns = stark.num_lookup_helper_columns(config); + let lookup_challenges = (num_lookup_columns > 0).then(|| { + challenges + .lookup_challenge_set + .unwrap() + .challenges + .iter() + .map(|ch| ch.beta) + .collect::>() }); + + let lookup_vars = stark.uses_lookups().then(|| LookupCheckVars { + local_values: auxiliary_polys.as_ref().unwrap().clone(), + next_values: auxiliary_polys_next.as_ref().unwrap().clone(), + challenges: lookup_challenges.unwrap(), + }); + let lookups = stark.lookups(); + eval_vanishing_poly::( &stark, - config, &vars, - permutation_data, + &lookups, + lookup_vars, &mut consumer, ); let vanishing_polys_zeta = consumer.accumulators(); @@ -114,7 +129,7 @@ pub(crate) fn verify_stark_proof_with_challenges< } let merkle_caps = once(proof.trace_cap) - .chain(proof.permutation_zs_cap) + .chain(proof.auxiliary_polys_cap) .chain(once(proof.quotient_polys_cap)) .collect_vec(); @@ -152,7 +167,7 @@ where let StarkProof { trace_cap, - permutation_zs_cap, + auxiliary_polys_cap, quotient_polys_cap, openings, // The shape of the opening proof will be checked in the FRI verifier (see @@ -163,8 +178,8 @@ where let StarkOpeningSet { local_values, next_values, - permutation_zs, - permutation_zs_next, + auxiliary_polys, + auxiliary_polys_next, quotient_polys, } = openings; @@ -172,7 +187,8 @@ where let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; - let num_zs = stark.num_permutation_batches(config); + + let num_auxiliary = stark.num_lookup_helper_columns(config); ensure!(trace_cap.height() == cap_height); ensure!(quotient_polys_cap.height() == cap_height); @@ -181,25 +197,13 @@ where ensure!(next_values.len() == S::COLUMNS); ensure!(quotient_polys.len() == stark.num_quotient_polys(config)); - if stark.uses_permutation_args() { - let permutation_zs_cap = permutation_zs_cap - .as_ref() - .ok_or_else(|| anyhow!("Missing Zs cap"))?; - let permutation_zs = permutation_zs - .as_ref() - .ok_or_else(|| anyhow!("Missing permutation_zs"))?; - let permutation_zs_next = permutation_zs_next - .as_ref() - .ok_or_else(|| anyhow!("Missing permutation_zs_next"))?; - - ensure!(permutation_zs_cap.height() == cap_height); - ensure!(permutation_zs.len() == num_zs); - ensure!(permutation_zs_next.len() == num_zs); - } else { - ensure!(permutation_zs_cap.is_none()); - ensure!(permutation_zs.is_none()); - ensure!(permutation_zs_next.is_none()); - } + check_lookup_options::( + stark, + auxiliary_polys_cap, + auxiliary_polys, + auxiliary_polys_next, + config, + )?; Ok(()) } @@ -216,30 +220,43 @@ fn eval_l_0_and_l_last(log_n: usize, x: F) -> (F, F) { (z_x * invs[0], z_x * invs[1]) } -/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff +/// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff /// the Stark uses a permutation argument. -fn check_permutation_options< +fn check_lookup_options< F: RichField + Extendable, C: GenericConfig, S: Stark, const D: usize, >( stark: &S, - proof_with_pis: &StarkProofWithPublicInputs, - challenges: &StarkProofChallenges, + auxiliary_polys_cap: &Option>::Hasher>>, + auxiliary_polys: &Option>::Extension>>, + auxiliary_polys_next: &Option>::Extension>>, + config: &StarkConfig, ) -> Result<()> { - let options_is_some = [ - proof_with_pis.proof.permutation_zs_cap.is_some(), - proof_with_pis.proof.openings.permutation_zs.is_some(), - proof_with_pis.proof.openings.permutation_zs_next.is_some(), - challenges.permutation_challenge_sets.is_some(), - ]; - ensure!( - options_is_some - .into_iter() - .all(|b| b == stark.uses_permutation_args()), - "Permutation data doesn't match with Stark configuration." - ); + if stark.uses_lookups() { + let num_auxiliary = stark.num_lookup_helper_columns(config); + let cap_height = config.fri_config.cap_height; + + let auxiliary_polys_cap = auxiliary_polys_cap + .as_ref() + .ok_or_else(|| anyhow!("Missing auxiliary_polys_cap"))?; + let auxiliary_polys = auxiliary_polys + .as_ref() + .ok_or_else(|| anyhow!("Missing auxiliary_polys"))?; + let auxiliary_polys_next = auxiliary_polys_next + .as_ref() + .ok_or_else(|| anyhow!("Missing auxiliary_polys_next"))?; + + ensure!(auxiliary_polys_cap.height() == cap_height); + ensure!(auxiliary_polys.len() == num_auxiliary); + ensure!(auxiliary_polys_next.len() == num_auxiliary); + } else { + ensure!(auxiliary_polys_cap.is_none()); + ensure!(auxiliary_polys.is_none()); + ensure!(auxiliary_polys_next.is_none()); + } + Ok(()) } From 1a08e783da3d3cedcc7ae3dc9c06e018ab90da71 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:59:34 -0500 Subject: [PATCH 137/175] Fix no-std tests and add corresponding jobs in the CI (#1501) --- .../continuous-integration-workflow.yml | 43 ++++- plonky2/src/gadgets/interpolation.rs | 3 + plonky2/src/gates/poseidon.rs | 3 + plonky2/src/hash/path_compression.rs | 17 +- plonky2/src/hash/poseidon.rs | 3 + plonky2/src/hash/poseidon_goldilocks.rs | 3 + plonky2/src/iop/challenger.rs | 3 + plonky2/src/lookup_test.rs | 161 +++++------------- plonky2/src/plonk/proof.rs | 5 +- .../conditional_recursive_verifier.rs | 3 + plonky2/src/recursion/cyclic_recursion.rs | 3 + plonky2/src/recursion/recursive_verifier.rs | 10 +- plonky2/src/util/mod.rs | 5 + plonky2/src/util/partial_products.rs | 3 + 14 files changed, 138 insertions(+), 127 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 29f471380a..1af066714e 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -69,8 +69,8 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 - wasm32: - name: wasm32 compatibility + wasm: + name: Check wasm32 compatibility runs-on: ubuntu-latest timeout-minutes: 30 if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" @@ -89,7 +89,7 @@ jobs: with: cache-on-failure: true - - name: Check in plonky2 subdirectory + - name: Check in plonky2 subdirectory for wasm targets run: cargo check --manifest-path plonky2/Cargo.toml --target wasm32-unknown-unknown --no-default-features env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 @@ -97,7 +97,7 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 - - name: Check in starky subdirectory + - name: Check in starky subdirectory for wasm targets run: cargo check --manifest-path starky/Cargo.toml --target wasm32-unknown-unknown --no-default-features env: RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 @@ -105,6 +105,41 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 + no_std: + name: Test Suite in no-std + runs-on: ubuntu-latest + timeout-minutes: 30 + if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install nightly toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-02-01 + + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Run cargo test in plonky2 subdirectory (no-std) + run: cargo test --manifest-path plonky2/Cargo.toml --no-default-features --lib + env: + RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 + RUST_LOG: 1 + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + + - name: Run cargo test in starky subdirectory (no-std) + run: cargo test --manifest-path starky/Cargo.toml --no-default-features --lib + env: + RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 + RUST_LOG: 1 + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + lints: name: Formatting and Clippy runs-on: ubuntu-latest diff --git a/plonky2/src/gadgets/interpolation.rs b/plonky2/src/gadgets/interpolation.rs index daf51d2103..6adbc42779 100644 --- a/plonky2/src/gadgets/interpolation.rs +++ b/plonky2/src/gadgets/interpolation.rs @@ -38,6 +38,9 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use anyhow::Result; use crate::field::extension::FieldExtension; diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index 5f353cd625..3ba1b67b4e 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -532,6 +532,9 @@ impl + Poseidon, const D: usize> SimpleGenerator AlgebraicHasher for PoseidonHash { #[cfg(test)] pub(crate) mod test_helpers { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use crate::field::types::Field; use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH}; diff --git a/plonky2/src/hash/poseidon_goldilocks.rs b/plonky2/src/hash/poseidon_goldilocks.rs index 1fd5af1354..12d061265e 100644 --- a/plonky2/src/hash/poseidon_goldilocks.rs +++ b/plonky2/src/hash/poseidon_goldilocks.rs @@ -444,6 +444,9 @@ mod poseidon12_mds { #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::{vec, vec::Vec}; + use crate::field::goldilocks_field::GoldilocksField as F; use crate::field::types::{Field, PrimeField64}; use crate::hash::poseidon::test_helpers::{check_consistency, check_test_vectors}; diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index d5de2831a3..d7b3c23795 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -293,6 +293,9 @@ impl, H: AlgebraicHasher, const D: usize> #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use crate::field::types::Sample; use crate::iop::challenger::{Challenger, RecursiveChallenger}; use crate::iop::generator::generate_partial_witness; diff --git a/plonky2/src/lookup_test.rs b/plonky2/src/lookup_test.rs index af85decaeb..cb6b53f86b 100644 --- a/plonky2/src/lookup_test.rs +++ b/plonky2/src/lookup_test.rs @@ -1,28 +1,34 @@ -static LOGGER_INITIALIZED: Once = Once::new(); - -use alloc::sync::Arc; -use std::sync::Once; +#[cfg(not(feature = "std"))] +use alloc::{sync::Arc, vec, vec::Vec}; +#[cfg(feature = "std")] +use std::sync::{Arc, Once}; use itertools::Itertools; -use log::{Level, LevelFilter}; +use log::Level; +use crate::field::types::Field; use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE}; use crate::gates::lookup_table::LookupTable; use crate::gates::noop::NoopGate; +use crate::iop::witness::{PartialWitness, WitnessWrite}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::circuit_data::CircuitConfig; +use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::prover::prove; use crate::util::timing::TimingTree; +const D: usize = 2; +type C = PoseidonGoldilocksConfig; +type F = >::F; + +const LUT_SIZE: usize = u16::MAX as usize + 1; + +#[cfg(feature = "std")] +static LOGGER_INITIALIZED: Once = Once::new(); + #[test] fn test_no_lookup() -> anyhow::Result<()> { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::iop::witness::PartialWitness; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; + init_logger(); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -41,14 +47,7 @@ fn test_no_lookup() -> anyhow::Result<()> { #[should_panic] #[test] fn test_lookup_table_not_used() { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; + init_logger(); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -63,14 +62,7 @@ fn test_lookup_table_not_used() { #[should_panic] #[test] fn test_lookup_without_table() { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; + init_logger(); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -84,17 +76,8 @@ fn test_lookup_without_table() { // Tests two lookups in one lookup table. #[test] fn test_one_lookup() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); let tip5_table = TIP5_TABLE.to_vec(); let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); let config = CircuitConfig::standard_recursion_config(); @@ -145,18 +128,9 @@ fn test_one_lookup() -> anyhow::Result<()> { // Tests one lookup in two different lookup tables. #[test] -pub fn test_two_luts() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); +fn test_two_luts() -> anyhow::Result<()> { + init_logger(); + let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -229,17 +203,9 @@ pub fn test_two_luts() -> anyhow::Result<()> { } #[test] -pub fn test_different_inputs() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); +fn test_different_inputs() -> anyhow::Result<()> { + init_logger(); + let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -314,17 +280,9 @@ pub fn test_different_inputs() -> anyhow::Result<()> { // This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created. #[test] -pub fn test_many_lookups() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); +fn test_many_lookups() -> anyhow::Result<()> { + init_logger(); + let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -404,18 +362,9 @@ pub fn test_many_lookups() -> anyhow::Result<()> { // Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index. #[test] -pub fn test_same_luts() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); +fn test_same_luts() -> anyhow::Result<()> { + init_logger(); + let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -469,21 +418,11 @@ pub fn test_same_luts() -> anyhow::Result<()> { #[test] fn test_big_lut() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - const LUT_SIZE: usize = u16::MAX as usize + 1; let inputs: [u16; LUT_SIZE] = core::array::from_fn(|i| i as u16); let lut_fn = |inp: u16| inp / 10; let lut_index = builder.add_lookup_table_from_fn(lut_fn, &inputs); @@ -522,21 +461,11 @@ fn test_big_lut() -> anyhow::Result<()> { #[test] fn test_many_lookups_on_big_lut() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; + init_logger(); - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - const LUT_SIZE: usize = u16::MAX as usize + 1; let inputs: [u16; LUT_SIZE] = core::array::from_fn(|i| i as u16); let lut_fn = |inp: u16| inp / 10; let lut_index = builder.add_lookup_table_from_fn(lut_fn, &inputs); @@ -581,11 +510,15 @@ fn test_many_lookups_on_big_lut() -> anyhow::Result<()> { data.verify(proof) } -fn init_logger() -> anyhow::Result<()> { - let mut builder = env_logger::Builder::from_default_env(); - builder.format_timestamp(None); - builder.filter_level(LevelFilter::Debug); +fn init_logger() { + #[cfg(feature = "std")] + { + LOGGER_INITIALIZED.call_once(|| { + let mut builder = env_logger::Builder::from_default_env(); + builder.format_timestamp(None); + builder.filter_level(log::LevelFilter::Debug); - builder.try_init()?; - Ok(()) + builder.try_init().unwrap(); + }); + } } diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index c010414e25..de82746af1 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -451,7 +451,10 @@ impl OpeningSetTarget { #[cfg(test)] mod tests { - use alloc::sync::Arc; + #[cfg(not(feature = "std"))] + use alloc::{sync::Arc, vec}; + #[cfg(feature = "std")] + use std::sync::Arc; use anyhow::Result; use itertools::Itertools; diff --git a/plonky2/src/recursion/conditional_recursive_verifier.rs b/plonky2/src/recursion/conditional_recursive_verifier.rs index 3f3b626751..a35b46ea03 100644 --- a/plonky2/src/recursion/conditional_recursive_verifier.rs +++ b/plonky2/src/recursion/conditional_recursive_verifier.rs @@ -336,6 +336,9 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec; + use anyhow::Result; use hashbrown::HashMap; diff --git a/plonky2/src/recursion/cyclic_recursion.rs b/plonky2/src/recursion/cyclic_recursion.rs index 60d5342a90..172c0826bc 100644 --- a/plonky2/src/recursion/cyclic_recursion.rs +++ b/plonky2/src/recursion/cyclic_recursion.rs @@ -198,6 +198,9 @@ where #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec; + use anyhow::Result; use crate::field::extension::Extendable; diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index ada2b00242..2da2440844 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -191,7 +191,10 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { - use alloc::sync::Arc; + #[cfg(not(feature = "std"))] + use alloc::{sync::Arc, vec}; + #[cfg(feature = "std")] + use std::sync::Arc; use anyhow::Result; use itertools::Itertools; @@ -690,12 +693,17 @@ mod tests { let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, common_data)?; assert_eq!(proof, &proof_from_bytes); + #[cfg(feature = "std")] let now = std::time::Instant::now(); + let compressed_proof = proof.clone().compress(&vd.circuit_digest, common_data)?; let decompressed_compressed_proof = compressed_proof .clone() .decompress(&vd.circuit_digest, common_data)?; + + #[cfg(feature = "std")] info!("{:.4}s to compress proof", now.elapsed().as_secs_f64()); + assert_eq!(proof, &decompressed_compressed_proof); let compressed_proof_bytes = compressed_proof.to_bytes(); diff --git a/plonky2/src/util/mod.rs b/plonky2/src/util/mod.rs index e0f71f1272..8f9960034d 100644 --- a/plonky2/src/util/mod.rs +++ b/plonky2/src/util/mod.rs @@ -1,5 +1,6 @@ //! Utility module for helper methods and plonky2 serialization logic. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use plonky2_maybe_rayon::*; @@ -41,6 +42,10 @@ pub(crate) const fn reverse_bits(n: usize, num_bits: usize) -> usize { #[cfg(test)] mod tests { + + #[cfg(not(feature = "std"))] + use alloc::vec; + use super::*; #[test] diff --git a/plonky2/src/util/partial_products.rs b/plonky2/src/util/partial_products.rs index 89be0fea86..e195af1a73 100644 --- a/plonky2/src/util/partial_products.rs +++ b/plonky2/src/util/partial_products.rs @@ -108,6 +108,9 @@ pub(crate) fn check_partial_products_circuit, const #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec; + use super::*; use crate::field::goldilocks_field::GoldilocksField; From d2598bded0cb24d36b0a9900e8ffd104c470831b Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:43:04 -0500 Subject: [PATCH 138/175] Revert "Remove StarkProofWithMetadata (#1497)" (#1502) This reverts commit af0259c5eb010304cfc04a5e2a77e88cf8cc2378. --- evm/src/cross_table_lookup.rs | 8 ++++---- evm/src/fixed_recursive_verifier.rs | 10 ++++++---- evm/src/get_challenges.rs | 6 ++++-- evm/src/proof.rs | 18 ++++++++++++++++-- evm/src/prover.rs | 14 ++++++++++---- evm/src/recursive_verifier.rs | 12 +++++++++--- evm/src/verifier.rs | 16 ++++++++-------- 7 files changed, 57 insertions(+), 27 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index df5bfbfc65..359b5309e8 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -52,7 +52,7 @@ use crate::lookup::{ eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, ColumnFilter, Filter, GrandProductChallenge, }; -use crate::proof::{StarkProof, StarkProofTarget}; +use crate::proof::{StarkProofTarget, StarkProofWithMetadata}; use crate::stark::Stark; /// An alias for `usize`, to represent the index of a STARK table in a multi-STARK setting. @@ -494,7 +494,7 @@ impl<'a, F: RichField + Extendable, const D: usize> { /// Extracts the `CtlCheckVars` for each STARK. pub(crate) fn from_proofs, const N: usize>( - proofs: &[StarkProof; N], + proofs: &[StarkProofWithMetadata; N], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_lookup_columns: &[usize; N], @@ -511,8 +511,8 @@ impl<'a, F: RichField + Extendable, const D: usize> let ctl_zs = proofs .iter() .zip(num_lookup_columns) - .map(|(proof, &num_lookup)| { - let openings = &proof.openings; + .map(|(p, &num_lookup)| { + let openings = &p.proof.openings; let ctl_zs = &openings.auxiliary_polys[num_lookup..]; let ctl_zs_next = &openings.auxiliary_polys_next[num_lookup..]; diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index b8844f5c00..2df85b03de 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -40,7 +40,7 @@ use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, - PublicValues, PublicValuesTarget, StarkProof, TrieRoots, TrieRootsTarget, + PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, }; use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ @@ -1003,7 +1003,7 @@ where for table in 0..NUM_TABLES { let stark_proof = &all_proof.stark_proofs[table]; - let original_degree_bits = stark_proof.recover_degree_bits(config); + let original_degree_bits = stark_proof.proof.recover_degree_bits(config); let table_circuits = &self.by_table[table]; let shrunk_proof = table_circuits .by_stark_size @@ -1629,10 +1629,12 @@ where pub fn shrink( &self, - stark_proof: &StarkProof, + stark_proof_with_metadata: &StarkProofWithMetadata, ctl_challenges: &GrandProductChallengeSet, ) -> anyhow::Result> { - let mut proof = self.initial_wrapper.prove(stark_proof, ctl_challenges)?; + let mut proof = self + .initial_wrapper + .prove(stark_proof_with_metadata, ctl_challenges)?; for wrapper_circuit in &self.shrinking_wrappers { proof = wrapper_circuit.prove(&proof)?; } diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index a9ea705a33..756b0650da 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -199,7 +199,7 @@ impl, C: GenericConfig, const D: usize> A let mut challenger = Challenger::::new(); for proof in &self.stark_proofs { - challenger.observe_cap(&proof.trace_cap); + challenger.observe_cap(&proof.proof.trace_cap); } observe_public_values::(&mut challenger, &self.public_values)?; @@ -210,7 +210,9 @@ impl, C: GenericConfig, const D: usize> A Ok(AllProofChallenges { stark_challenges: core::array::from_fn(|i| { challenger.compact(); - self.stark_proofs[i].get_challenges(&mut challenger, config) + self.stark_proofs[i] + .proof + .get_challenges(&mut challenger, config) }), ctl_challenges, }) diff --git a/evm/src/proof.rs b/evm/src/proof.rs index ef63431b98..33640458d6 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -25,7 +25,7 @@ use crate::util::{get_h160, get_h256, h2u}; #[derive(Debug, Clone)] pub struct AllProof, C: GenericConfig, const D: usize> { /// Proofs for all the different STARK modules. - pub stark_proofs: [StarkProof; NUM_TABLES], + pub stark_proofs: [StarkProofWithMetadata; NUM_TABLES], /// Cross-table lookup challenges. pub(crate) ctl_challenges: GrandProductChallengeSet, /// Public memory values used for the recursive proofs. @@ -35,7 +35,7 @@ pub struct AllProof, C: GenericConfig, co impl, C: GenericConfig, const D: usize> AllProof { /// Returns the degree (i.e. the trace length) of each STARK. pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - core::array::from_fn(|i| self.stark_proofs[i].recover_degree_bits(config)) + core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) } } @@ -837,6 +837,20 @@ pub struct StarkProof, C: GenericConfig, pub opening_proof: FriProof, } +/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when +/// creating a recursive wrapper proof around a STARK proof. +#[derive(Debug, Clone)] +pub struct StarkProofWithMetadata +where + F: RichField + Extendable, + C: GenericConfig, +{ + /// Initial Fiat-Shamir state. + pub(crate) init_challenger_state: >::Permutation, + /// Proof for a single STARK. + pub(crate) proof: StarkProof, +} + impl, C: GenericConfig, const D: usize> StarkProof { /// Recover the length of the trace from a STARK proof and a STARK config. pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 0b7858d3b9..f376b8cd28 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -32,7 +32,7 @@ use crate::evaluation_frame::StarkEvaluationFrame; use crate::generation::{generate_traces, GenerationInputs}; use crate::get_challenges::observe_public_values; use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; -use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof}; +use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; #[cfg(test)] @@ -187,7 +187,7 @@ fn prove_with_commitments( ctl_challenges: &GrandProductChallengeSet, timing: &mut TimingTree, abort_signal: Option>, -) -> Result<[StarkProof; NUM_TABLES]> +) -> Result<[StarkProofWithMetadata; NUM_TABLES]> where F: RichField + Extendable, C: GenericConfig, @@ -323,7 +323,7 @@ pub(crate) fn prove_single_table( challenger: &mut Challenger, timing: &mut TimingTree, abort_signal: Option>, -) -> Result> +) -> Result> where F: RichField + Extendable, C: GenericConfig, @@ -341,6 +341,8 @@ where "FRI total reduction arity is too large.", ); + let init_challenger_state = challenger.compact(); + let constraint_degree = stark.constraint_degree(); let lookup_challenges = stark.uses_lookups().then(|| { ctl_challenges @@ -518,12 +520,16 @@ where ) ); - Ok(StarkProof { + let proof = StarkProof { trace_cap: trace_commitment.merkle_tree.cap.clone(), auxiliary_polys_cap, quotient_polys_cap, openings, opening_proof, + }; + Ok(StarkProofWithMetadata { + init_challenger_state, + proof, }) } diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 2c3827a50f..5220ba32a7 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -39,7 +39,8 @@ use crate::memory::VALUE_LIMBS; use crate::proof::{ BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, StarkProof, - StarkProofChallengesTarget, StarkProofTarget, TrieRoots, TrieRootsTarget, + StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots, + TrieRootsTarget, }; use crate::stark::Stark; use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; @@ -146,7 +147,7 @@ where pub(crate) fn prove( &self, - proof: &StarkProof, + proof_with_metadata: &StarkProofWithMetadata, ctl_challenges: &GrandProductChallengeSet, ) -> Result> { let mut inputs = PartialWitness::new(); @@ -154,7 +155,7 @@ where set_stark_proof_target( &mut inputs, &self.stark_proof_target, - proof, + &proof_with_metadata.proof, self.zero_target, ); @@ -168,6 +169,11 @@ where inputs.set_target(challenge_target.gamma, challenge.gamma); } + inputs.set_target_arr( + self.init_challenger_state_target.as_ref(), + proof_with_metadata.init_challenger_state.as_ref(), + ); + self.circuit.prove(inputs) } } diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index ae4fbf4aff..3e284c7fc4 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -72,7 +72,7 @@ where verify_stark_proof_with_challenges( arithmetic_stark, - &all_proof.stark_proofs[Table::Arithmetic as usize], + &all_proof.stark_proofs[Table::Arithmetic as usize].proof, &stark_challenges[Table::Arithmetic as usize], &ctl_vars_per_table[Table::Arithmetic as usize], &ctl_challenges, @@ -80,7 +80,7 @@ where )?; verify_stark_proof_with_challenges( byte_packing_stark, - &all_proof.stark_proofs[Table::BytePacking as usize], + &all_proof.stark_proofs[Table::BytePacking as usize].proof, &stark_challenges[Table::BytePacking as usize], &ctl_vars_per_table[Table::BytePacking as usize], &ctl_challenges, @@ -88,7 +88,7 @@ where )?; verify_stark_proof_with_challenges( cpu_stark, - &all_proof.stark_proofs[Table::Cpu as usize], + &all_proof.stark_proofs[Table::Cpu as usize].proof, &stark_challenges[Table::Cpu as usize], &ctl_vars_per_table[Table::Cpu as usize], &ctl_challenges, @@ -96,7 +96,7 @@ where )?; verify_stark_proof_with_challenges( keccak_stark, - &all_proof.stark_proofs[Table::Keccak as usize], + &all_proof.stark_proofs[Table::Keccak as usize].proof, &stark_challenges[Table::Keccak as usize], &ctl_vars_per_table[Table::Keccak as usize], &ctl_challenges, @@ -104,7 +104,7 @@ where )?; verify_stark_proof_with_challenges( keccak_sponge_stark, - &all_proof.stark_proofs[Table::KeccakSponge as usize], + &all_proof.stark_proofs[Table::KeccakSponge as usize].proof, &stark_challenges[Table::KeccakSponge as usize], &ctl_vars_per_table[Table::KeccakSponge as usize], &ctl_challenges, @@ -112,7 +112,7 @@ where )?; verify_stark_proof_with_challenges( logic_stark, - &all_proof.stark_proofs[Table::Logic as usize], + &all_proof.stark_proofs[Table::Logic as usize].proof, &stark_challenges[Table::Logic as usize], &ctl_vars_per_table[Table::Logic as usize], &ctl_challenges, @@ -120,7 +120,7 @@ where )?; verify_stark_proof_with_challenges( memory_stark, - &all_proof.stark_proofs[Table::Memory as usize], + &all_proof.stark_proofs[Table::Memory as usize].proof, &stark_challenges[Table::Memory as usize], &ctl_vars_per_table[Table::Memory as usize], &ctl_challenges, @@ -142,7 +142,7 @@ where cross_table_lookups, all_proof .stark_proofs - .map(|proof| proof.openings.ctl_zs_first), + .map(|p| p.proof.openings.ctl_zs_first), extra_looking_sums, config, ) From 6b39fc9006c107f5937879c9ff3b58b1c6582e43 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:11:52 -0500 Subject: [PATCH 139/175] Remove risk of panics in interpreter (#1519) --- evm/src/cpu/kernel/interpreter.rs | 37 ++++++++++++++++++------------- evm/src/witness/operation.rs | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5a65ace3d5..cdd2e99f37 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -4,7 +4,7 @@ use core::cmp::Ordering; use core::ops::Range; use std::collections::{BTreeSet, HashMap}; -use anyhow::bail; +use anyhow::{anyhow, bail}; use eth_trie_utils::partial_trie::PartialTrie; use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; use keccak_hash::keccak; @@ -298,7 +298,7 @@ impl<'a> Interpreter<'a> { (Segment::GlobalBlockBloom.unscale()).into(), i.into(), ) - .unwrap(), + .expect("This cannot panic as `virt` fits in a `u32`"), metadata.block_bloom[i], ) }) @@ -315,7 +315,7 @@ impl<'a> Interpreter<'a> { (Segment::BlockHashes.unscale()).into(), i.into(), ) - .unwrap(), + .expect("This cannot panic as `virt` fits in a `u32`"), h2u(inputs.block_hashes.prev_hashes[i]), ) }) @@ -336,7 +336,7 @@ impl<'a> Interpreter<'a> { } } - fn roll_memory_back(&mut self, len: usize) { + fn roll_memory_back(&mut self, len: usize) -> Result<(), ProgramError> { // We roll the memory back until `memops` reaches length `len`. debug_assert!(self.memops.len() >= len); while self.memops.len() > len { @@ -346,25 +346,28 @@ impl<'a> Interpreter<'a> { self.generation_state.memory.contexts[context].segments [Segment::Stack.unscale()] .content - .pop(); + .pop() + .ok_or(ProgramError::StackUnderflow)?; } InterpreterMemOpKind::Pop(value, context) => { self.generation_state.memory.contexts[context].segments [Segment::Stack.unscale()] .content - .push(value) + .push(value); } InterpreterMemOpKind::Write(value, context, segment, offset) => { self.generation_state.memory.contexts[context].segments [segment >> SEGMENT_SCALING_FACTOR] // we need to unscale the segment value - .content[offset] = value + .content[offset] = value; } } } } + + Ok(()) } - fn rollback(&mut self, checkpoint: InterpreterCheckpoint) { + fn rollback(&mut self, checkpoint: InterpreterCheckpoint) -> anyhow::Result<()> { let InterpreterRegistersState { kernel_mode, context, @@ -373,7 +376,8 @@ impl<'a> Interpreter<'a> { self.set_is_kernel(kernel_mode); self.set_context(context); self.generation_state.registers = registers; - self.roll_memory_back(checkpoint.mem_len); + self.roll_memory_back(checkpoint.mem_len) + .map_err(|_| anyhow!("Memory rollback failed unexpectedly.")) } fn handle_error(&mut self, err: ProgramError) -> anyhow::Result<()> { @@ -417,7 +421,7 @@ impl<'a> Interpreter<'a> { .content, ); } - self.rollback(checkpoint); + self.rollback(checkpoint)?; self.handle_error(e) } }?; @@ -630,7 +634,7 @@ impl<'a> Interpreter<'a> { } pub(crate) fn extract_kernel_memory(self, segment: Segment, range: Range) -> Vec { - let mut output: Vec = vec![]; + let mut output: Vec = Vec::with_capacity(range.end); for i in range { let term = self .generation_state @@ -672,7 +676,7 @@ impl<'a> Interpreter<'a> { .push(InterpreterMemOpKind::Pop(val, self.context())); } if self.stack_len() > 1 { - let top = stack_peek(&self.generation_state, 1).unwrap(); + let top = stack_peek(&self.generation_state, 1)?; self.generation_state.registers.stack_top = top; } self.generation_state.registers.stack_len -= 1; @@ -986,6 +990,7 @@ impl<'a> Interpreter<'a> { let i = self.pop()?; let x = self.pop()?; let result = if i < 32.into() { + // Calling `as_usize()` here is safe. x.byte(31 - i.as_usize()) } else { 0 @@ -1013,7 +1018,7 @@ impl<'a> Interpreter<'a> { let addr = self.pop()?; let (context, segment, offset) = unpack_address!(addr); - let size = self.pop()?.as_usize(); + let size = u256_to_usize(self.pop()?)?; let bytes = (offset..offset + size) .map(|i| { self.generation_state @@ -1211,7 +1216,7 @@ impl<'a> Interpreter<'a> { fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { let x = self.pop()?; - let new_ctx = (x >> CONTEXT_SCALING_FACTOR).as_usize(); + let new_ctx = u256_to_usize(x >> CONTEXT_SCALING_FACTOR)?; let sp_to_save = self.stack_len().into(); let old_ctx = self.context(); @@ -1222,7 +1227,7 @@ impl<'a> Interpreter<'a> { let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); self.generation_state.memory.set(old_sp_addr, sp_to_save); - let new_sp = self.generation_state.memory.get(new_sp_addr).as_usize(); + let new_sp = u256_to_usize(self.generation_state.memory.get(new_sp_addr))?; if new_sp > 0 { let new_stack_top = self.generation_state.memory.contexts[new_ctx].segments @@ -1249,7 +1254,7 @@ impl<'a> Interpreter<'a> { fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { let addr = self.pop()?; let (context, segment, offset) = unpack_address!(addr); - let len = self.pop()?.as_usize(); + let len = u256_to_usize(self.pop()?)?; if len > 32 { return Err(ProgramError::IntegerTooLarge); } diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 8c09fa00a2..4e6271d3b3 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -398,7 +398,7 @@ pub(crate) fn generate_set_context( }; // If the new stack isn't empty, read stack_top from memory. - let new_sp = new_sp.as_usize(); + let new_sp = u256_to_usize(new_sp)?; if new_sp > 0 { // Set up columns to disable the channel if it *is* empty. let new_sp_field = F::from_canonical_usize(new_sp); From b600142cd454b95eba403fa1f86f582ff8688c79 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:48:52 -0500 Subject: [PATCH 140/175] Cleanup `alloc` / `std` imports for plonky2 (#1518) * Cleanup alloc/std versions for plonky2 * Fix import for macro --- plonky2/examples/bench_recursion.rs | 5 ++++ plonky2/src/fri/mod.rs | 1 + plonky2/src/fri/oracle.rs | 4 ++-- plonky2/src/fri/proof.rs | 4 ++-- plonky2/src/fri/prover.rs | 1 + plonky2/src/fri/recursive_verifier.rs | 4 ++-- plonky2/src/fri/reduction_strategies.rs | 4 ++-- plonky2/src/fri/structure.rs | 1 + plonky2/src/fri/verifier.rs | 1 + plonky2/src/gadgets/arithmetic.rs | 9 +++++--- plonky2/src/gadgets/arithmetic_extension.rs | 9 +++++--- plonky2/src/gadgets/interpolation.rs | 1 + plonky2/src/gadgets/lookup.rs | 4 ++-- plonky2/src/gadgets/polynomial.rs | 1 + plonky2/src/gadgets/random_access.rs | 1 + plonky2/src/gadgets/range_check.rs | 9 +++++--- plonky2/src/gadgets/split_base.rs | 5 ++-- plonky2/src/gadgets/split_join.rs | 9 +++++--- plonky2/src/gates/arithmetic_base.rs | 9 +++++--- plonky2/src/gates/arithmetic_extension.rs | 9 +++++--- plonky2/src/gates/base_sum.rs | 5 ++-- plonky2/src/gates/constant.rs | 5 ++-- plonky2/src/gates/coset_interpolation.rs | 10 +++++--- plonky2/src/gates/exponentiation.rs | 10 +++++--- plonky2/src/gates/gate.rs | 8 +++---- plonky2/src/gates/gate_testing.rs | 4 ++-- plonky2/src/gates/lookup.rs | 10 +++++--- plonky2/src/gates/lookup_table.rs | 14 +++++++---- plonky2/src/gates/multiplication_extension.rs | 9 +++++--- plonky2/src/gates/noop.rs | 4 ++-- plonky2/src/gates/packed_util.rs | 4 ++-- plonky2/src/gates/poseidon.rs | 23 ++++++++----------- plonky2/src/gates/poseidon_mds.rs | 10 +++++--- plonky2/src/gates/public_input.rs | 4 ++-- plonky2/src/gates/random_access.rs | 10 +++++--- plonky2/src/gates/reducing.rs | 10 +++++--- plonky2/src/gates/reducing_extension.rs | 10 +++++--- plonky2/src/gates/selectors.rs | 4 ++-- plonky2/src/hash/hash_types.rs | 1 + plonky2/src/hash/hashing.rs | 2 +- plonky2/src/hash/keccak.rs | 4 ++-- plonky2/src/hash/merkle_proofs.rs | 4 ++-- plonky2/src/hash/merkle_tree.rs | 1 + plonky2/src/hash/path_compression.rs | 4 ++-- plonky2/src/hash/poseidon.rs | 10 +++----- plonky2/src/iop/challenger.rs | 4 ++-- plonky2/src/iop/ext_target.rs | 1 + plonky2/src/iop/generator.rs | 11 +++++---- plonky2/src/iop/target.rs | 1 + plonky2/src/iop/wire.rs | 1 + plonky2/src/iop/witness.rs | 4 ++-- plonky2/src/lib.rs | 1 + plonky2/src/plonk/circuit_builder.rs | 8 +++---- plonky2/src/plonk/circuit_data.rs | 7 +++--- plonky2/src/plonk/config.rs | 4 ++-- plonky2/src/plonk/copy_constraint.rs | 1 + plonky2/src/plonk/get_challenges.rs | 4 ++-- plonky2/src/plonk/permutation_argument.rs | 1 + plonky2/src/plonk/plonk_common.rs | 4 ++-- plonky2/src/plonk/proof.rs | 11 +++++---- plonky2/src/plonk/prover.rs | 4 ++-- plonky2/src/plonk/vanishing_poly.rs | 4 ++-- .../conditional_recursive_verifier.rs | 1 + plonky2/src/recursion/cyclic_recursion.rs | 1 + plonky2/src/recursion/dummy_circuit.rs | 9 +++++--- plonky2/src/util/context_tree.rs | 9 +++++--- plonky2/src/util/partial_products.rs | 1 + plonky2/src/util/reducing.rs | 4 ++-- .../util/serialization/gate_serialization.rs | 5 +++- .../serialization/generator_serialization.rs | 7 ++++-- plonky2/src/util/serialization/mod.rs | 8 +++---- 71 files changed, 235 insertions(+), 152 deletions(-) diff --git a/plonky2/examples/bench_recursion.rs b/plonky2/examples/bench_recursion.rs index 2e4c1ca3a1..8201c96de0 100644 --- a/plonky2/examples/bench_recursion.rs +++ b/plonky2/examples/bench_recursion.rs @@ -3,11 +3,16 @@ // put it in `src/bin/`, but then we wouldn't have access to // `[dev-dependencies]`. +#[cfg(not(feature = "std"))] extern crate alloc; + +#[cfg(not(feature = "std"))] use alloc::sync::Arc; use core::num::ParseIntError; use core::ops::RangeInclusive; use core::str::FromStr; +#[cfg(feature = "std")] +use std::sync::Arc; use anyhow::{anyhow, Context as _, Result}; use itertools::Itertools; diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 207a2ea82c..3445ada8f4 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -3,6 +3,7 @@ //! It provides both a native implementation and an in-circuit version //! of the FRI verifier for recursive proof composition. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use serde::Serialize; diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index 8642a6c566..64dcbc6095 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -1,5 +1,5 @@ -use alloc::format; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{format, vec::Vec}; use itertools::Itertools; use plonky2_field::types::Field; diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index 71b62c7142..edff1bea4a 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use hashbrown::HashMap; use itertools::izip; diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 378f1daebb..4fb15614eb 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use plonky2_maybe_rayon::*; diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index da6082426a..47ae08f2c9 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -1,5 +1,5 @@ -use alloc::format; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{format, vec::Vec}; use itertools::Itertools; diff --git a/plonky2/src/fri/reduction_strategies.rs b/plonky2/src/fri/reduction_strategies.rs index 6e5752296e..e7f5d799ff 100644 --- a/plonky2/src/fri/reduction_strategies.rs +++ b/plonky2/src/fri/reduction_strategies.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use log::debug; use serde::Serialize; diff --git a/plonky2/src/fri/structure.rs b/plonky2/src/fri/structure.rs index 7d7436d5e5..81e462da5c 100644 --- a/plonky2/src/fri/structure.rs +++ b/plonky2/src/fri/structure.rs @@ -1,6 +1,7 @@ //! Information about the structure of a FRI instance, in terms of the oracles and polynomials //! involved, and the points they are opened at. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index f860ba3000..89faa0f6e7 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use anyhow::{ensure, Result}; diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index 9982628e02..0e6806ffb0 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use core::borrow::Borrow; use crate::field::extension::Extendable; diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 3c1deac381..649f4082ec 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use core::borrow::Borrow; use crate::field::extension::{Extendable, FieldExtension, OEF}; diff --git a/plonky2/src/gadgets/interpolation.rs b/plonky2/src/gadgets/interpolation.rs index 6adbc42779..39b048af48 100644 --- a/plonky2/src/gadgets/interpolation.rs +++ b/plonky2/src/gadgets/interpolation.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec; use plonky2_field::extension::Extendable; diff --git a/plonky2/src/gadgets/lookup.rs b/plonky2/src/gadgets/lookup.rs index 4ab765ba03..0d9963a84f 100644 --- a/plonky2/src/gadgets/lookup.rs +++ b/plonky2/src/gadgets/lookup.rs @@ -1,5 +1,5 @@ -use alloc::borrow::ToOwned; -use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::{borrow::ToOwned, vec}; use crate::field::extension::Extendable; use crate::gates::lookup::LookupGate; diff --git a/plonky2/src/gadgets/polynomial.rs b/plonky2/src/gadgets/polynomial.rs index d43d99c2ea..94fbe3b1c6 100644 --- a/plonky2/src/gadgets/polynomial.rs +++ b/plonky2/src/gadgets/polynomial.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::field::extension::Extendable; diff --git a/plonky2/src/gadgets/random_access.rs b/plonky2/src/gadgets/random_access.rs index 85d2c7141c..0d99a3e918 100644 --- a/plonky2/src/gadgets/random_access.rs +++ b/plonky2/src/gadgets/random_access.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::field::extension::Extendable; diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index 41af064aa6..9a66a6a6c6 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use crate::field::extension::Extendable; use crate::hash::hash_types::RichField; diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index a2c98ac707..1cdec86203 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -1,6 +1,5 @@ -use alloc::string::String; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec, vec::Vec}; use core::borrow::Borrow; use itertools::Itertools; diff --git a/plonky2/src/gadgets/split_join.rs b/plonky2/src/gadgets/split_join.rs index 6901c8caf2..2f35b94c77 100644 --- a/plonky2/src/gadgets/split_join.rs +++ b/plonky2/src/gadgets/split_join.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use crate::field::extension::Extendable; use crate::gates::base_sum::BaseSumGate; diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index dfdd87e8c0..754895790a 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -1,6 +1,9 @@ -use alloc::format; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; use crate::field::extension::Extendable; use crate::field::packed::PackedField; diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index a19c6b4a4b..60eb912b61 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -1,6 +1,9 @@ -use alloc::format; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; use core::ops::Range; use crate::field::extension::{Extendable, FieldExtension}; diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 1d0f8f809e..0f38415d4e 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -1,6 +1,5 @@ -use alloc::string::String; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec, vec::Vec}; use core::ops::Range; use crate::field::extension::Extendable; diff --git a/plonky2/src/gates/constant.rs b/plonky2/src/gates/constant.rs index 144e1ca352..cc62de7fe4 100644 --- a/plonky2/src/gates/constant.rs +++ b/plonky2/src/gates/constant.rs @@ -1,6 +1,5 @@ -use alloc::string::String; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec, vec::Vec}; use serde::{Deserialize, Serialize}; diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index ab69f698be..9911e92793 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -1,6 +1,10 @@ -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::marker::PhantomData; use core::ops::Range; diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 0011f01143..2b7164e1d2 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -1,6 +1,10 @@ -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::marker::PhantomData; use crate::field::extension::Extendable; diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index cc8f7513c4..07de4fa33b 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -1,11 +1,11 @@ -use alloc::string::String; -use alloc::sync::Arc; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{string::String, sync::Arc, vec, vec::Vec}; use core::any::Any; use core::fmt::{Debug, Error, Formatter}; use core::hash::{Hash, Hasher}; use core::ops::Range; +#[cfg(feature = "std")] +use std::sync::Arc; use hashbrown::HashMap; use serde::{Serialize, Serializer}; diff --git a/plonky2/src/gates/gate_testing.rs b/plonky2/src/gates/gate_testing.rs index 9a1f0f4949..c71e96dff7 100644 --- a/plonky2/src/gates/gate_testing.rs +++ b/plonky2/src/gates/gate_testing.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use anyhow::{ensure, Result}; diff --git a/plonky2/src/gates/lookup.rs b/plonky2/src/gates/lookup.rs index 42b3bb92fb..23a0fd8742 100644 --- a/plonky2/src/gates/lookup.rs +++ b/plonky2/src/gates/lookup.rs @@ -1,6 +1,10 @@ -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::usize; use itertools::Itertools; diff --git a/plonky2/src/gates/lookup_table.rs b/plonky2/src/gates/lookup_table.rs index ad01e09209..9a4d08c83b 100644 --- a/plonky2/src/gates/lookup_table.rs +++ b/plonky2/src/gates/lookup_table.rs @@ -1,8 +1,14 @@ -use alloc::string::{String, ToString}; -use alloc::sync::Arc; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + sync::Arc, + vec, + vec::Vec, +}; use core::usize; +#[cfg(feature = "std")] +use std::sync::Arc; use itertools::Itertools; use keccak_hash::keccak; diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 3f9fd8fe53..143c854c66 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -1,6 +1,9 @@ -use alloc::format; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; use core::ops::Range; use crate::field::extension::{Extendable, FieldExtension}; diff --git a/plonky2/src/gates/noop.rs b/plonky2/src/gates/noop.rs index 8752f380b2..54cb6422ef 100644 --- a/plonky2/src/gates/noop.rs +++ b/plonky2/src/gates/noop.rs @@ -1,5 +1,5 @@ -use alloc::string::String; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; use crate::field::extension::Extendable; use crate::gates::gate::Gate; diff --git a/plonky2/src/gates/packed_util.rs b/plonky2/src/gates/packed_util.rs index 361eb3a24b..32f1c37a7f 100644 --- a/plonky2/src/gates/packed_util.rs +++ b/plonky2/src/gates/packed_util.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use crate::field::extension::Extendable; use crate::field::packable::Packable; diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index 3ba1b67b4e..be9a064e43 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -1,6 +1,10 @@ -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{ + format, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::marker::PhantomData; use crate::field::extension::Extendable; @@ -532,20 +536,13 @@ impl + Poseidon, const D: usize> SimpleGenerator -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::fmt::Debug; use unroll::unroll_for_loops; @@ -753,11 +753,7 @@ impl AlgebraicHasher for PoseidonHash { #[cfg(test)] pub(crate) mod test_helpers { - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - - use crate::field::types::Field; - use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH}; + use super::*; pub(crate) fn check_test_vectors( test_vectors: Vec<([u64; SPONGE_WIDTH], [u64; SPONGE_WIDTH])>, diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index d7b3c23795..2daa7fdc4f 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::marker::PhantomData; use crate::field::extension::{Extendable, FieldExtension}; diff --git a/plonky2/src/iop/ext_target.rs b/plonky2/src/iop/ext_target.rs index c64d96e872..cc90355732 100644 --- a/plonky2/src/iop/ext_target.rs +++ b/plonky2/src/iop/ext_target.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 1704b34795..6cdd75dcf6 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,7 +1,10 @@ -use alloc::boxed::Box; -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + boxed::Box, + string::{String, ToString}, + vec, + vec::Vec, +}; use core::fmt::Debug; use core::marker::PhantomData; diff --git a/plonky2/src/iop/target.rs b/plonky2/src/iop/target.rs index 705941e023..f70d4c3dc2 100644 --- a/plonky2/src/iop/target.rs +++ b/plonky2/src/iop/target.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/iop/wire.rs b/plonky2/src/iop/wire.rs index 435479ce7b..cfa69755d5 100644 --- a/plonky2/src/iop/wire.rs +++ b/plonky2/src/iop/wire.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index cf74be512c..40377aa3e4 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index 44bc2cf638..b0b6bfb4d9 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -2,6 +2,7 @@ #![allow(clippy::needless_range_loop)] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] pub extern crate alloc; /// Re-export of `plonky2_field`. diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 4c2a536905..6df692fbe4 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -1,12 +1,10 @@ //! Logic for building plonky2 circuits. -use alloc::collections::BTreeMap; -use alloc::sync::Arc; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{collections::BTreeMap, sync::Arc, vec, vec::Vec}; use core::cmp::max; #[cfg(feature = "std")] -use std::time::Instant; +use std::{collections::BTreeMap, sync::Arc, time::Instant}; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index d9847f4be8..e4afb5680d 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -12,10 +12,11 @@ //! The verifier data can similarly be extracted by calling [`CircuitData::verifier_data`]. //! This is useful to allow even small devices to verify plonky2 proofs. -use alloc::collections::BTreeMap; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{collections::BTreeMap, vec, vec::Vec}; use core::ops::{Range, RangeFrom}; +#[cfg(feature = "std")] +use std::collections::BTreeMap; use anyhow::Result; use serde::Serialize; diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index 1ed40c40ce..ee5b69ffa8 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -6,8 +6,8 @@ //! the Poseidon hash function both internally and natively, and one //! mixing Poseidon internally and truncated Keccak externally. -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::fmt::Debug; use serde::de::DeserializeOwned; diff --git a/plonky2/src/plonk/copy_constraint.rs b/plonky2/src/plonk/copy_constraint.rs index ea92ec1c9e..cf7a6a19ac 100644 --- a/plonky2/src/plonk/copy_constraint.rs +++ b/plonky2/src/plonk/copy_constraint.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::string::String; use crate::iop::target::Target; diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index ee6167b90b..45d79f99aa 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use hashbrown::HashSet; diff --git a/plonky2/src/plonk/permutation_argument.rs b/plonky2/src/plonk/permutation_argument.rs index a0dd57707f..312f3e991b 100644 --- a/plonky2/src/plonk/permutation_argument.rs +++ b/plonky2/src/plonk/permutation_argument.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use hashbrown::HashMap; diff --git a/plonky2/src/plonk/plonk_common.rs b/plonky2/src/plonk/plonk_common.rs index ca8ea9196a..170bfa170a 100644 --- a/plonky2/src/plonk/plonk_common.rs +++ b/plonky2/src/plonk/plonk_common.rs @@ -1,7 +1,7 @@ //! Utility methods and constants for Plonk. -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use crate::field::extension::Extendable; use crate::field::packed::PackedField; diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index de82746af1..a9151a9fc1 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -4,8 +4,8 @@ //! [`CompressedProof`] or [`CompressedProofWithPublicInputs`] formats. //! The latter can be directly passed to a verifier to assert its correctness. -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use anyhow::ensure; use plonky2_maybe_rayon::*; @@ -452,21 +452,22 @@ impl OpeningSetTarget { #[cfg(test)] mod tests { #[cfg(not(feature = "std"))] - use alloc::{sync::Arc, vec}; + use alloc::sync::Arc; #[cfg(feature = "std")] use std::sync::Arc; use anyhow::Result; use itertools::Itertools; + use plonky2_field::types::Sample; - use crate::field::types::Sample; + use super::*; use crate::fri::reduction_strategies::FriReductionStrategy; use crate::gates::lookup_table::LookupTable; use crate::gates::noop::NoopGate; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use crate::plonk::config::PoseidonGoldilocksConfig; use crate::plonk::verifier::verify; #[test] diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 153610dcb6..400845bc6f 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -1,7 +1,7 @@ //! plonky2 prover implementation. -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{format, vec, vec::Vec}; use core::cmp::min; use core::mem::swap; diff --git a/plonky2/src/plonk/vanishing_poly.rs b/plonky2/src/plonk/vanishing_poly.rs index e3ddcf5b88..9eb7252e1a 100644 --- a/plonky2/src/plonk/vanishing_poly.rs +++ b/plonky2/src/plonk/vanishing_poly.rs @@ -1,5 +1,5 @@ -use alloc::vec::Vec; -use alloc::{format, vec}; +#[cfg(not(feature = "std"))] +use alloc::{format, vec, vec::Vec}; use core::cmp::min; use plonky2_field::polynomial::PolynomialCoeffs; diff --git a/plonky2/src/recursion/conditional_recursive_verifier.rs b/plonky2/src/recursion/conditional_recursive_verifier.rs index a35b46ea03..43bef5892b 100644 --- a/plonky2/src/recursion/conditional_recursive_verifier.rs +++ b/plonky2/src/recursion/conditional_recursive_verifier.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use itertools::Itertools; diff --git a/plonky2/src/recursion/cyclic_recursion.rs b/plonky2/src/recursion/cyclic_recursion.rs index 172c0826bc..7be554176c 100644 --- a/plonky2/src/recursion/cyclic_recursion.rs +++ b/plonky2/src/recursion/cyclic_recursion.rs @@ -1,5 +1,6 @@ #![allow(clippy::int_plus_one)] // Makes more sense for some inequalities below. +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use anyhow::{ensure, Result}; diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index ee73105acc..501a475f1b 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use hashbrown::HashMap; use plonky2_field::extension::Extendable; diff --git a/plonky2/src/util/context_tree.rs b/plonky2/src/util/context_tree.rs index a0a699710d..2e70fd61ca 100644 --- a/plonky2/src/util/context_tree.rs +++ b/plonky2/src/util/context_tree.rs @@ -1,6 +1,9 @@ -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; use log::{log, Level}; diff --git a/plonky2/src/util/partial_products.rs b/plonky2/src/util/partial_products.rs index e195af1a73..1ceea7cab1 100644 --- a/plonky2/src/util/partial_products.rs +++ b/plonky2/src/util/partial_products.rs @@ -1,3 +1,4 @@ +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::iter; diff --git a/plonky2/src/util/reducing.rs b/plonky2/src/util/reducing.rs index e1ba397b1c..b99da32e6a 100644 --- a/plonky2/src/util/reducing.rs +++ b/plonky2/src/util/reducing.rs @@ -1,5 +1,5 @@ -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::borrow::Borrow; use crate::field::extension::{Extendable, FieldExtension}; diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index c5763fb0bf..8b10da07b0 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -1,6 +1,9 @@ //! A module to help with GateRef serialization +#[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "std")] +use std::vec::Vec; // For macros below use plonky2_field::extension::Extendable; @@ -76,7 +79,7 @@ macro_rules! impl_gate_serializer { fn write_gate( &self, - buf: &mut $crate::alloc::vec::Vec, + buf: &mut $crate::util::serialization::gate_serialization::Vec, gate: &$crate::gates::gate::GateRef, common: &$crate::plonk::circuit_data::CommonCircuitData, ) -> $crate::util::serialization::IoResult<()> { diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index bad24cebf2..6ede002007 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -1,6 +1,9 @@ //! A module to help with WitnessGeneratorRef serialization -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +pub use alloc::vec::Vec; +#[cfg(feature = "std")] +pub use std::vec::Vec; // For macros below use plonky2_field::extension::Extendable; @@ -80,7 +83,7 @@ macro_rules! impl_generator_serializer { fn write_generator( &self, - buf: &mut $crate::alloc::vec::Vec, + buf: &mut $crate::util::serialization::generator_serialization::Vec, generator: &$crate::iop::generator::WitnessGeneratorRef, common: &$crate::plonk::circuit_data::CommonCircuitData, ) -> $crate::util::serialization::IoResult<()> { diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index 94551bdfc6..393db6c699 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -4,14 +4,14 @@ pub mod generator_serialization; #[macro_use] pub mod gate_serialization; -use alloc::collections::BTreeMap; -use alloc::sync::Arc; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::{collections::BTreeMap, sync::Arc, vec, vec::Vec}; use core::convert::Infallible; use core::fmt::{Debug, Display, Formatter}; use core::mem::size_of; use core::ops::Range; +#[cfg(feature = "std")] +use std::{collections::BTreeMap, sync::Arc}; pub use gate_serialization::default::DefaultGateSerializer; pub use gate_serialization::GateSerializer; From b6fec06c38ab85b12adfe46884f53c43a67440fe Mon Sep 17 00:00:00 2001 From: David Date: Mon, 12 Feb 2024 15:42:07 +0100 Subject: [PATCH 141/175] Fix nightly build (ahash issue) (#1524) * Revert "Fix workflow" This reverts commit 246c2b6263f71313192801b1c27a3c08e241f545. * Revert "Fix nightly version" This reverts commit 8f919133379213698ba43fda5a39a153a17324b7. * chore: remove stdsimd feature req (stabilized) --- .github/workflows/continuous-integration-workflow.yml | 10 +++------- field/src/lib.rs | 1 - rust-toolchain | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 1af066714e..9da841bca7 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -28,9 +28,7 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly-2024-02-01 + uses: dtolnay/rust-toolchain@nightly - name: Set up rust cache uses: Swatinem/rust-cache@v2 @@ -79,9 +77,8 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@master + uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly-2024-02-01 targets: wasm32-unknown-unknown - name: Set up rust cache @@ -150,9 +147,8 @@ jobs: uses: actions/checkout@v4 - name: Install nightly toolchain - uses: dtolnay/rust-toolchain@master + uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly-2024-02-01 components: rustfmt, clippy - name: Set up rust cache diff --git a/field/src/lib.rs b/field/src/lib.rs index d0806bc8fd..c35441bdb7 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -3,7 +3,6 @@ #![allow(clippy::type_complexity)] #![allow(clippy::len_without_is_empty)] #![allow(clippy::needless_range_loop)] -#![feature(stdsimd)] #![feature(specialization)] #![cfg_attr(not(test), no_std)] diff --git a/rust-toolchain b/rust-toolchain index 471d867dd0..07ade694b1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-02-01 \ No newline at end of file +nightly \ No newline at end of file From 3ec1bfddb32d51494bb8013d4bd3ad332d30bf87 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 13 Feb 2024 11:47:54 -0500 Subject: [PATCH 142/175] Update `starky` and leverage it as dependency for `plonky2_evm` (#1503) * Update prover logic * Add helper method for CTL data * Some cleanup * Update some methods * Fix * Some more fixes * More tweaks * Final * Leverage starky crate * Additional tweaks * Cleanup * More cleanup * Fix * Cleanup imports * Fix * Final tweaks * Cleanup and hide behind debug_assertions attribute * Clippy * Fix no-std * Make wasm compatible * Doc and remove todo * API cleanup and remove TODO * Add Debug impls * Add documentation for public items * Feature-gate alloc imports * Import method from starky instead * Add simple crate and module documentation * Apply comments * Add lib level documentation * Add test without lookups * Fix starks without logup * Cleanup * Some more cleanup * Fix get_challenges for non-lookup STARKs * Add additional config methods and tests * Apply comments * More comments --- evm/Cargo.toml | 9 +- evm/src/all_stark.rs | 9 +- evm/src/arithmetic/addcy.rs | 16 +- evm/src/arithmetic/arithmetic_stark.rs | 23 +- evm/src/arithmetic/byte.rs | 8 +- evm/src/arithmetic/divmod.rs | 103 ++- evm/src/arithmetic/modular.rs | 107 +-- evm/src/arithmetic/mul.rs | 8 +- evm/src/arithmetic/shift.rs | 10 +- evm/src/byte_packing/byte_packing_stark.rs | 19 +- evm/src/config.rs | 43 - evm/src/constraint_consumer.rs | 162 ---- evm/src/cpu/byte_unpacking.rs | 2 +- evm/src/cpu/clock.rs | 2 +- evm/src/cpu/contextops.rs | 2 +- evm/src/cpu/control_flow.rs | 2 +- evm/src/cpu/cpu_stark.rs | 23 +- evm/src/cpu/decode.rs | 2 +- evm/src/cpu/dup_swap.rs | 2 +- evm/src/cpu/gas.rs | 2 +- evm/src/cpu/halt.rs | 2 +- evm/src/cpu/jumps.rs | 2 +- evm/src/cpu/membus.rs | 2 +- evm/src/cpu/memio.rs | 2 +- evm/src/cpu/modfp254.rs | 2 +- evm/src/cpu/pc.rs | 2 +- evm/src/cpu/push0.rs | 2 +- evm/src/cpu/shift.rs | 2 +- evm/src/cpu/simple_logic/eq_iszero.rs | 2 +- evm/src/cpu/simple_logic/mod.rs | 2 +- evm/src/cpu/simple_logic/not.rs | 2 +- evm/src/cpu/stack.rs | 2 +- evm/src/cpu/syscalls_exceptions.rs | 2 +- evm/src/evaluation_frame.rs | 47 - evm/src/fixed_recursive_verifier.rs | 24 +- evm/src/generation/mod.rs | 2 +- evm/src/get_challenges.rs | 120 +-- evm/src/keccak/columns.rs | 2 +- evm/src/keccak/keccak_stark.rs | 41 +- evm/src/keccak/round_flags.rs | 9 +- evm/src/keccak_sponge/keccak_sponge_stark.rs | 20 +- evm/src/lib.rs | 31 +- evm/src/logic.rs | 22 +- evm/src/lookup.rs | 895 ------------------- evm/src/memory/memory_stark.rs | 19 +- evm/src/proof.rs | 335 +------ evm/src/prover.rs | 563 +----------- evm/src/recursive_verifier.rs | 275 +----- evm/src/stark.rs | 228 ----- evm/src/stark_testing.rs | 157 ---- evm/src/util.rs | 12 - evm/src/vanishing_poly.rs | 81 -- evm/src/verifier.rs | 294 +----- evm/src/witness/traces.rs | 4 +- evm/tests/add11_yml.rs | 4 +- evm/tests/basic_smart_contract.rs | 4 +- evm/tests/empty_txn_list.rs | 5 +- evm/tests/erc20.rs | 4 +- evm/tests/erc721.rs | 4 +- evm/tests/log_opcode.rs | 5 +- evm/tests/self_balance_gas_cost.rs | 4 +- evm/tests/selfdestruct.rs | 4 +- evm/tests/simple_transfer.rs | 4 +- evm/tests/withdrawals.rs | 4 +- plonky2/src/fri/proof.rs | 2 + starky/Cargo.toml | 2 + starky/src/config.rs | 115 ++- starky/src/constraint_consumer.rs | 24 +- {evm => starky}/src/cross_table_lookup.rs | 319 ++++--- starky/src/evaluation_frame.rs | 7 + starky/src/fibonacci_stark.rs | 216 ++++- starky/src/get_challenges.rs | 180 +++- starky/src/lib.rs | 322 ++++++- starky/src/lookup.rs | 70 +- starky/src/proof.rs | 277 +++++- starky/src/prover.rs | 272 +++++- starky/src/recursive_verifier.rs | 163 ++-- starky/src/stark.rs | 145 ++- starky/src/stark_testing.rs | 6 +- starky/src/util.rs | 3 + starky/src/vanishing_poly.rs | 32 + starky/src/verifier.rs | 160 +++- 82 files changed, 2295 insertions(+), 3823 deletions(-) delete mode 100644 evm/src/config.rs delete mode 100644 evm/src/constraint_consumer.rs delete mode 100644 evm/src/evaluation_frame.rs delete mode 100644 evm/src/lookup.rs delete mode 100644 evm/src/stark.rs delete mode 100644 evm/src/stark_testing.rs delete mode 100644 evm/src/vanishing_poly.rs rename {evm => starky}/src/cross_table_lookup.rs (84%) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 24c560a0ed..df8401b059 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -27,8 +27,9 @@ num-bigint = "0.4.3" once_cell = "1.13.0" pest = "2.1.3" pest_derive = "2.1.0" -plonky2 = { path = "../plonky2", default-features = false, features = ["timing"] } +plonky2 = { path = "../plonky2", features = ["timing"] } plonky2_util = { path = "../util" } +starky = { path = "../starky" } rand = "0.8.5" rand_chacha = "0.3.1" rlp = "0.5.1" @@ -51,7 +52,11 @@ sha2 = "0.10.6" [features] default = ["parallel"] asmtools = ["hex"] -parallel = ["plonky2/parallel", "plonky2_maybe_rayon/parallel"] +parallel = [ + "plonky2/parallel", + "plonky2_maybe_rayon/parallel", + "starky/parallel" +] [[bin]] name = "assemble" diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index cd7a2d3c38..ec218ef8e7 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -3,15 +3,17 @@ use core::ops::Deref; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use starky::config::StarkConfig; +use starky::cross_table_lookup::{CrossTableLookup, TableIdx, TableWithColumns}; +use starky::evaluation_frame::StarkFrame; +use starky::stark::Stark; use crate::arithmetic::arithmetic_stark; use crate::arithmetic::arithmetic_stark::ArithmeticStark; use crate::byte_packing::byte_packing_stark::{self, BytePackingStark}; -use crate::config::StarkConfig; use crate::cpu::cpu_stark; use crate::cpu::cpu_stark::CpuStark; use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::cross_table_lookup::{CrossTableLookup, TableIdx, TableWithColumns}; use crate::keccak::keccak_stark; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; @@ -21,7 +23,6 @@ use crate::logic; use crate::logic::LogicStark; use crate::memory::memory_stark; use crate::memory::memory_stark::MemoryStark; -use crate::stark::Stark; /// Structure containing all STARKs and the cross-table lookups. #[derive(Clone)] @@ -66,6 +67,8 @@ impl, const D: usize> AllStark { } } +pub type EvmStarkFrame = StarkFrame; + /// Associates STARK tables with a unique index. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Table { diff --git a/evm/src/arithmetic/addcy.rs b/evm/src/arithmetic/addcy.rs index 4f343b45d5..94d2bd1697 100644 --- a/evm/src/arithmetic/addcy.rs +++ b/evm/src/arithmetic/addcy.rs @@ -22,10 +22,10 @@ use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::arithmetic::columns::*; use crate::arithmetic::utils::u256_to_array; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; /// Generate row for ADD, SUB, GT and LT operations. pub(crate) fn generate( @@ -263,10 +263,10 @@ mod tests { use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::constraint_consumer::ConstraintConsumer; use super::*; use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::constraint_consumer::ConstraintConsumer; // TODO: Should be able to refactor this test to apply to all operations. #[test] @@ -284,14 +284,14 @@ mod tests { lv[IS_LT] = F::ZERO; lv[IS_GT] = F::ZERO; - let mut constrant_consumer = ConstraintConsumer::new( + let mut constraint_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], F::ONE, F::ONE, F::ONE, ); - eval_packed_generic(&lv, &mut constrant_consumer); - for &acc in &constrant_consumer.constraint_accs { + eval_packed_generic(&lv, &mut constraint_consumer); + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, F::ZERO); } } @@ -324,14 +324,14 @@ mod tests { generate(&mut lv, op_filter, left_in, right_in); - let mut constrant_consumer = ConstraintConsumer::new( + let mut constraint_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], F::ONE, F::ONE, F::ONE, ); - eval_packed_generic(&lv, &mut constrant_consumer); - for &acc in &constrant_consumer.constraint_accs { + eval_packed_generic(&lv, &mut constraint_consumer); + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, F::ZERO); } diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 5e3f039cdf..75fd9fe2a2 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -9,18 +9,18 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::util::transpose; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::cross_table_lookup::TableWithColumns; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter, Lookup}; +use starky::stark::Stark; use static_assertions::const_assert; use super::columns::{op_flags, NUM_ARITH_COLUMNS}; use super::shift; -use crate::all_stark::Table; +use crate::all_stark::{EvmStarkFrame, Table}; use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::TableWithColumns; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Filter, Lookup}; -use crate::stark::Stark; /// Creates a vector of `Columns` to link the 16-bit columns of the arithmetic table, /// split into groups of N_LIMBS at a time in `regs`, with the corresponding 32-bit @@ -190,12 +190,13 @@ impl ArithmeticStark { } impl, const D: usize> Stark for ArithmeticStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_ARITH_COLUMNS>; + type EvaluationFrameTarget = + EvmStarkFrame, ExtensionTarget, NUM_ARITH_COLUMNS>; fn eval_packed_generic( &self, @@ -320,6 +321,10 @@ impl, const D: usize> Stark for ArithmeticSta filter_columns: vec![None; NUM_SHARED_COLS], }] } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] @@ -330,11 +335,11 @@ mod tests { use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use super::{columns, ArithmeticStark}; use crate::arithmetic::columns::OUTPUT_REGISTER; use crate::arithmetic::*; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn degree() -> Result<()> { diff --git a/evm/src/arithmetic/byte.rs b/evm/src/arithmetic/byte.rs index f7581efa77..272a78431b 100644 --- a/evm/src/arithmetic/byte.rs +++ b/evm/src/arithmetic/byte.rs @@ -69,11 +69,11 @@ use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use static_assertions::const_assert; use crate::arithmetic::columns::*; use crate::arithmetic::utils::u256_to_array; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; // Give meaningful names to the columns of AUX_INPUT_REGISTER_0 that // we're using @@ -480,14 +480,14 @@ mod tests { let out_byte = val.byte(31 - i) as u64; verify_output(&lv, out_byte); - let mut constrant_consumer = ConstraintConsumer::new( + let mut constraint_consumer = ConstraintConsumer::new( vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], F::ONE, F::ONE, F::ONE, ); - eval_packed(&lv, &mut constrant_consumer); - for &acc in &constrant_consumer.constraint_accs { + eval_packed(&lv, &mut constraint_consumer); + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, F::ZERO); } } diff --git a/evm/src/arithmetic/divmod.rs b/evm/src/arithmetic/divmod.rs index a4599dc721..d27fbc2e35 100644 --- a/evm/src/arithmetic/divmod.rs +++ b/evm/src/arithmetic/divmod.rs @@ -11,13 +11,13 @@ use plonky2::field::types::PrimeField64; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::arithmetic::columns::*; use crate::arithmetic::modular::{ generate_modular_op, modular_constr_poly, modular_constr_poly_ext_circuit, }; use crate::arithmetic::utils::*; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; /// Generates the output and auxiliary values for modular operations, /// assuming the input, modular and output limbs are already set. @@ -215,10 +215,10 @@ mod tests { use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::constraint_consumer::ConstraintConsumer; use super::*; use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::constraint_consumer::ConstraintConsumer; const N_RND_TESTS: usize = 1000; const MODULAR_OPS: [usize; 2] = [IS_MOD, IS_DIV]; @@ -247,7 +247,7 @@ mod tests { GoldilocksField::ONE, ); eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -306,7 +306,7 @@ mod tests { GoldilocksField::ZERO, ); eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -321,52 +321,57 @@ mod tests { for op_filter in MODULAR_OPS { for _i in 0..N_RND_TESTS { - // set inputs to random values and the modulus to zero; - // the output is defined to be zero when modulus is zero. - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; + for corrupt_constraints in [false, true] { + // set inputs to random values and the modulus to zero; + // the output is defined to be zero when modulus is zero. + let mut lv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + let mut nv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + + // Reset operation columns, then select one + for op in MODULAR_OPS { + lv[op] = F::ZERO; + } + // Since SHR uses the logic for DIV, `IS_SHR` should also be set to 0 here. + lv[IS_SHR] = F::ZERO; + lv[op_filter] = F::ONE; + + let input0 = U256::from(rng.gen::<[u8; 32]>()); + let input1 = U256::zero(); + + generate(&mut lv, &mut nv, op_filter, input0, input1, U256::zero()); + + // check that the correct output was generated + assert!(lv[OUTPUT_REGISTER].iter().all(|&c| c == F::ZERO)); + + let mut constraint_consumer = ConstraintConsumer::new( + vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], + GoldilocksField::ONE, + GoldilocksField::ZERO, + GoldilocksField::ZERO, + ); + eval_packed(&lv, &nv, &mut constraint_consumer); + + if corrupt_constraints { + // Corrupt one output limb by setting it to a non-zero value. + let random_oi = OUTPUT_REGISTER.start + rng.gen::() % N_LIMBS; + lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); + + eval_packed(&lv, &nv, &mut constraint_consumer); + + // Check that at least one of the constraints was non-zero. + assert!(constraint_consumer + .accumulators() + .iter() + .any(|&acc| acc != F::ZERO)); + } else { + assert!(constraint_consumer + .accumulators() + .iter() + .all(|&acc| acc == F::ZERO)); + } } - // Since SHR uses the logic for DIV, `IS_SHR` should also be set to 0 here. - lv[IS_SHR] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = U256::zero(); - - generate(&mut lv, &mut nv, op_filter, input0, input1, U256::zero()); - - // check that the correct output was generated - assert!(lv[OUTPUT_REGISTER].iter().all(|&c| c == F::ZERO)); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - assert!(constraint_consumer - .constraint_accs - .iter() - .all(|&acc| acc == F::ZERO)); - - // Corrupt one output limb by setting it to a non-zero value - let random_oi = OUTPUT_REGISTER.start + rng.gen::() % N_LIMBS; - lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); - - eval_packed(&lv, &nv, &mut constraint_consumer); - - // Check that at least one of the constraints was non-zero - assert!(constraint_consumer - .constraint_accs - .iter() - .any(|&acc| acc != F::ZERO)); } } } diff --git a/evm/src/arithmetic/modular.rs b/evm/src/arithmetic/modular.rs index 5a1df5c733..a3806862ad 100644 --- a/evm/src/arithmetic/modular.rs +++ b/evm/src/arithmetic/modular.rs @@ -119,13 +119,13 @@ use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use static_assertions::const_assert; use super::columns; use crate::arithmetic::addcy::{eval_ext_circuit_addcy, eval_packed_generic_addcy}; use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::extension_tower::BN_BASE; const fn bn254_modulus_limbs() -> [u16; N_LIMBS] { @@ -832,10 +832,10 @@ mod tests { use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::constraint_consumer::ConstraintConsumer; use super::*; use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::constraint_consumer::ConstraintConsumer; use crate::extension_tower::BN_BASE; const N_RND_TESTS: usize = 1000; @@ -873,7 +873,7 @@ mod tests { GoldilocksField::ONE, ); eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -930,7 +930,7 @@ mod tests { GoldilocksField::ZERO, ); eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -945,54 +945,59 @@ mod tests { for op_filter in [IS_ADDMOD, IS_SUBMOD, IS_MULMOD] { for _i in 0..N_RND_TESTS { - // set inputs to random values and the modulus to zero; - // the output is defined to be zero when modulus is zero. - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; + for corrupt_constraints in [false, true] { + // set inputs to random values and the modulus to zero; + // the output is defined to be zero when modulus is zero. + let mut lv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + let mut nv = [F::default(); NUM_ARITH_COLUMNS] + .map(|_| F::from_canonical_u16(rng.gen::())); + + // Reset operation columns, then select one + for op in MODULAR_OPS { + lv[op] = F::ZERO; + } + lv[IS_SHR] = F::ZERO; + lv[IS_DIV] = F::ZERO; + lv[IS_MOD] = F::ZERO; + lv[op_filter] = F::ONE; + + let input0 = U256::from(rng.gen::<[u8; 32]>()); + let input1 = U256::from(rng.gen::<[u8; 32]>()); + let modulus = U256::zero(); + + generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); + + // check that the correct output was generated + assert!(lv[MODULAR_OUTPUT].iter().all(|&c| c == F::ZERO)); + + let mut constraint_consumer = ConstraintConsumer::new( + vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], + GoldilocksField::ONE, + GoldilocksField::ZERO, + GoldilocksField::ZERO, + ); + eval_packed(&lv, &nv, &mut constraint_consumer); + + if corrupt_constraints { + // Corrupt one output limb by setting it to a non-zero value. + let random_oi = MODULAR_OUTPUT.start + rng.gen::() % N_LIMBS; + lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); + + eval_packed(&lv, &nv, &mut constraint_consumer); + + // Check that at least one of the constraints was non-zero. + assert!(constraint_consumer + .accumulators() + .iter() + .any(|&acc| acc != F::ZERO)); + } else { + assert!(constraint_consumer + .accumulators() + .iter() + .all(|&acc| acc == F::ZERO)); + } } - lv[IS_SHR] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[IS_MOD] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = U256::from(rng.gen::<[u8; 32]>()); - let modulus = U256::zero(); - - generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); - - // check that the correct output was generated - assert!(lv[MODULAR_OUTPUT].iter().all(|&c| c == F::ZERO)); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - assert!(constraint_consumer - .constraint_accs - .iter() - .all(|&acc| acc == F::ZERO)); - - // Corrupt one output limb by setting it to a non-zero value - let random_oi = MODULAR_OUTPUT.start + rng.gen::() % N_LIMBS; - lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); - - eval_packed(&lv, &nv, &mut constraint_consumer); - - // Check that at least one of the constraints was non-zero - assert!(constraint_consumer - .constraint_accs - .iter() - .any(|&acc| acc != F::ZERO)); } } } diff --git a/evm/src/arithmetic/mul.rs b/evm/src/arithmetic/mul.rs index 01c9d5c1c0..112ef7ebb5 100644 --- a/evm/src/arithmetic/mul.rs +++ b/evm/src/arithmetic/mul.rs @@ -62,10 +62,10 @@ use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; /// Given the two limbs of `left_in` and `right_in`, computes `left_in * right_in`. pub(crate) fn generate_mul(lv: &mut [F], left_in: [i64; 16], right_in: [i64; 16]) { @@ -253,10 +253,10 @@ mod tests { use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::constraint_consumer::ConstraintConsumer; use super::*; use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::constraint_consumer::ConstraintConsumer; const N_RND_TESTS: usize = 1000; @@ -279,7 +279,7 @@ mod tests { GoldilocksField::ONE, ); eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -312,7 +312,7 @@ mod tests { GoldilocksField::ONE, ); eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } diff --git a/evm/src/arithmetic/shift.rs b/evm/src/arithmetic/shift.rs index bb83798495..bc6276b1b2 100644 --- a/evm/src/arithmetic/shift.rs +++ b/evm/src/arithmetic/shift.rs @@ -27,11 +27,11 @@ use plonky2::field::types::PrimeField64; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use super::{divmod, mul}; use crate::arithmetic::columns::*; use crate::arithmetic::utils::*; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; /// Generates a shift operation (either SHL or SHR). /// The inputs are stored in the form `(shift, input, 1 << shift)`. @@ -184,10 +184,10 @@ mod tests { use plonky2::field::types::{Field, Sample}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; + use starky::constraint_consumer::ConstraintConsumer; use super::*; use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::constraint_consumer::ConstraintConsumer; const N_RND_TESTS: usize = 1000; @@ -212,7 +212,7 @@ mod tests { GoldilocksField::ONE, ); eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -261,7 +261,7 @@ mod tests { GoldilocksField::ZERO, ); eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } @@ -320,7 +320,7 @@ mod tests { GoldilocksField::ZERO, ); eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.constraint_accs { + for &acc in &constraint_consumer.accumulators() { assert_eq!(acc, GoldilocksField::ZERO); } } diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index ff7a18c06d..14cf61d5e4 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -37,16 +37,17 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter, Lookup}; +use starky::stark::Stark; use super::NUM_BYTES; +use crate::all_stark::EvmStarkFrame; use crate::byte_packing::columns::{ index_len, value_bytes, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, IS_READ, LEN_INDICES_COLS, NUM_COLUMNS, RANGE_COUNTER, RC_FREQUENCIES, TIMESTAMP, }; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Filter, Lookup}; -use crate::stark::Stark; use crate::witness::memory::MemoryAddress; /// Strict upper bound for the individual bytes range-check. @@ -258,12 +259,12 @@ impl, const D: usize> BytePackingStark { } impl, const D: usize> Stark for BytePackingStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; + type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; fn eval_packed_generic( &self, @@ -397,15 +398,19 @@ impl, const D: usize> Stark for BytePackingSt filter_columns: vec![None; NUM_BYTES], }] } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] pub(crate) mod tests { use anyhow::Result; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use crate::byte_packing::byte_packing_stark::BytePackingStark; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/config.rs b/evm/src/config.rs deleted file mode 100644 index 3f88d99f5d..0000000000 --- a/evm/src/config.rs +++ /dev/null @@ -1,43 +0,0 @@ -use plonky2::fri::reduction_strategies::FriReductionStrategy; -use plonky2::fri::{FriConfig, FriParams}; - -/// A configuration containing the different parameters to be used by the STARK prover. -pub struct StarkConfig { - /// The targeted security level for the proofs generated with this configuration. - pub security_bits: usize, - - /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly) - /// `degree / |F|`. - pub num_challenges: usize, - - /// The configuration of the FRI sub-protocol. - pub fri_config: FriConfig, -} - -impl Default for StarkConfig { - fn default() -> Self { - Self::standard_fast_config() - } -} - -impl StarkConfig { - /// A typical configuration with a rate of 2, resulting in fast but large proofs. - /// Targets ~100 bit conjectured security. - pub const fn standard_fast_config() -> Self { - Self { - security_bits: 100, - num_challenges: 2, - fri_config: FriConfig { - rate_bits: 1, - cap_height: 4, - proof_of_work_bits: 16, - reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5), - num_query_rounds: 84, - }, - } - } - - pub(crate) fn fri_params(&self, degree_bits: usize) -> FriParams { - self.fri_config.fri_params(degree_bits, false) - } -} diff --git a/evm/src/constraint_consumer.rs b/evm/src/constraint_consumer.rs deleted file mode 100644 index 919b51638a..0000000000 --- a/evm/src/constraint_consumer.rs +++ /dev/null @@ -1,162 +0,0 @@ -use core::marker::PhantomData; - -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::iop::target::Target; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -pub struct ConstraintConsumer { - /// Random values used to combine multiple constraints into one. - pub alphas: Vec, - - /// Running sums of constraints that have been emitted so far, scaled by powers of alpha. - // TODO(JN): This is pub so it can be used in a test. Once we have an API for accessing this - // result, it should be made private. - pub constraint_accs: Vec

, - - /// The evaluation of `X - g^(n-1)`. - z_last: P, - - /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated - /// with the first trace row, and zero at other points in the subgroup. - lagrange_basis_first: P, - - /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated - /// with the last trace row, and zero at other points in the subgroup. - lagrange_basis_last: P, -} - -impl ConstraintConsumer

{ - pub(crate) fn new( - alphas: Vec, - z_last: P, - lagrange_basis_first: P, - lagrange_basis_last: P, - ) -> Self { - Self { - constraint_accs: vec![P::ZEROS; alphas.len()], - alphas, - z_last, - lagrange_basis_first, - lagrange_basis_last, - } - } - - pub(crate) fn accumulators(self) -> Vec

{ - self.constraint_accs - } - - /// Add one constraint valid on all rows except the last. - pub(crate) fn constraint_transition(&mut self, constraint: P) { - self.constraint(constraint * self.z_last); - } - - /// Add one constraint on all rows. - pub(crate) fn constraint(&mut self, constraint: P) { - for (&alpha, acc) in self.alphas.iter().zip(&mut self.constraint_accs) { - *acc *= alpha; - *acc += constraint; - } - } - - /// Add one constraint, but first multiply it by a filter such that it will only apply to the - /// first row of the trace. - pub(crate) fn constraint_first_row(&mut self, constraint: P) { - self.constraint(constraint * self.lagrange_basis_first); - } - - /// Add one constraint, but first multiply it by a filter such that it will only apply to the - /// last row of the trace. - pub(crate) fn constraint_last_row(&mut self, constraint: P) { - self.constraint(constraint * self.lagrange_basis_last); - } -} - -pub struct RecursiveConstraintConsumer, const D: usize> { - /// A random value used to combine multiple constraints into one. - alphas: Vec, - - /// A running sum of constraints that have been emitted so far, scaled by powers of alpha. - constraint_accs: Vec>, - - /// The evaluation of `X - g^(n-1)`. - z_last: ExtensionTarget, - - /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated - /// with the first trace row, and zero at other points in the subgroup. - lagrange_basis_first: ExtensionTarget, - - /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated - /// with the last trace row, and zero at other points in the subgroup. - lagrange_basis_last: ExtensionTarget, - - _phantom: PhantomData, -} - -impl, const D: usize> RecursiveConstraintConsumer { - pub(crate) fn new( - zero: ExtensionTarget, - alphas: Vec, - z_last: ExtensionTarget, - lagrange_basis_first: ExtensionTarget, - lagrange_basis_last: ExtensionTarget, - ) -> Self { - Self { - constraint_accs: vec![zero; alphas.len()], - alphas, - z_last, - lagrange_basis_first, - lagrange_basis_last, - _phantom: Default::default(), - } - } - - pub(crate) fn accumulators(self) -> Vec> { - self.constraint_accs - } - - /// Add one constraint valid on all rows except the last. - pub(crate) fn constraint_transition( - &mut self, - builder: &mut CircuitBuilder, - constraint: ExtensionTarget, - ) { - let filtered_constraint = builder.mul_extension(constraint, self.z_last); - self.constraint(builder, filtered_constraint); - } - - /// Add one constraint valid on all rows. - pub(crate) fn constraint( - &mut self, - builder: &mut CircuitBuilder, - constraint: ExtensionTarget, - ) { - for (&alpha, acc) in self.alphas.iter().zip(&mut self.constraint_accs) { - *acc = builder.scalar_mul_add_extension(alpha, *acc, constraint); - } - } - - /// Add one constraint, but first multiply it by a filter such that it will only apply to the - /// first row of the trace. - pub(crate) fn constraint_first_row( - &mut self, - builder: &mut CircuitBuilder, - constraint: ExtensionTarget, - ) { - let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_first); - self.constraint(builder, filtered_constraint); - } - - /// Add one constraint, but first multiply it by a filter such that it will only apply to the - /// last row of the trace. - pub(crate) fn constraint_last_row( - &mut self, - builder: &mut CircuitBuilder, - constraint: ExtensionTarget, - ) { - let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_last); - self.constraint(builder, filtered_constraint); - } -} diff --git a/evm/src/cpu/byte_unpacking.rs b/evm/src/cpu/byte_unpacking.rs index 39053141d6..4de1855dae 100644 --- a/evm/src/cpu/byte_unpacking.rs +++ b/evm/src/cpu/byte_unpacking.rs @@ -4,8 +4,8 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; pub(crate) fn eval_packed( diff --git a/evm/src/cpu/clock.rs b/evm/src/cpu/clock.rs index cd7b17d8ed..4fa917a213 100644 --- a/evm/src/cpu/clock.rs +++ b/evm/src/cpu/clock.rs @@ -2,8 +2,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; /// Check the correct updating of `clock`. diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index ec4e5e5e6e..9a0bb7483f 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -5,10 +5,10 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use super::columns::ops::OpsColumnsView; use super::cpu_stark::{disable_unused_channels, disable_unused_channels_circuit}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs index bde5930572..a288746241 100644 --- a/evm/src/cpu/control_flow.rs +++ b/evm/src/cpu/control_flow.rs @@ -3,8 +3,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::kernel::aggregator::KERNEL; diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 8bcada2f3b..340eede508 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -8,24 +8,24 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::cross_table_lookup::TableWithColumns; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter}; +use starky::stark::Stark; use super::columns::CpuColumnsView; use super::halt; use super::kernel::constants::context_metadata::ContextMetadata; use super::membus::NUM_GP_CHANNELS; -use crate::all_stark::Table; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::all_stark::{EvmStarkFrame, Table}; use crate::cpu::columns::{COL_MAP, NUM_CPU_COLUMNS}; use crate::cpu::{ byte_unpacking, clock, contextops, control_flow, decode, dup_swap, gas, jumps, membus, memio, modfp254, pc, push0, shift, simple_logic, stack, syscalls_exceptions, }; -use crate::cross_table_lookup::TableWithColumns; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Filter}; use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; -use crate::stark::Stark; /// Creates the vector of `Columns` corresponding to the General Purpose channels when calling the Keccak sponge: /// the CPU reads the output of the sponge directly from the `KeccakSpongeStark` table. @@ -452,12 +452,13 @@ pub(crate) struct CpuStark { } impl, const D: usize> Stark for CpuStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_CPU_COLUMNS>; + type EvaluationFrameTarget = + EvmStarkFrame, ExtensionTarget, NUM_CPU_COLUMNS>; /// Evaluates all CPU constraints. fn eval_packed_generic( @@ -531,15 +532,19 @@ impl, const D: usize> Stark for CpuStark usize { 3 } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use crate::cpu::cpu_stark::CpuStark; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs index 4c2c43221e..83980239ac 100644 --- a/evm/src/cpu/decode.rs +++ b/evm/src/cpu/decode.rs @@ -3,8 +3,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP}; /// List of opcode blocks diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 1abec5fc61..e67eaa6253 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -5,8 +5,8 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, MemoryChannelView}; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/gas.rs b/evm/src/cpu/gas.rs index be033c3c43..37097adcea 100644 --- a/evm/src/cpu/gas.rs +++ b/evm/src/cpu/gas.rs @@ -4,9 +4,9 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use super::columns::COL_MAP; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::ops::OpsColumnsView; use crate::cpu::columns::CpuColumnsView; diff --git a/evm/src/cpu/halt.rs b/evm/src/cpu/halt.rs index 80ac32853c..a04128608c 100644 --- a/evm/src/cpu/halt.rs +++ b/evm/src/cpu/halt.rs @@ -5,9 +5,9 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use super::control_flow::get_halt_pc; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::membus::NUM_GP_CHANNELS; diff --git a/evm/src/cpu/jumps.rs b/evm/src/cpu/jumps.rs index fd7fcfd962..f3413b0f0a 100644 --- a/evm/src/cpu/jumps.rs +++ b/evm/src/cpu/jumps.rs @@ -3,8 +3,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::membus::NUM_GP_CHANNELS; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/membus.rs b/evm/src/cpu/membus.rs index 6ce845613d..b50ab5cce3 100644 --- a/evm/src/cpu/membus.rs +++ b/evm/src/cpu/membus.rs @@ -2,8 +2,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; /// General-purpose memory channels; they can read and write to all contexts/segments/addresses. diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 924f030f5f..ac32253da1 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -4,9 +4,9 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use super::cpu_stark::get_addr; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::stack; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/modfp254.rs b/evm/src/cpu/modfp254.rs index 95bab8d655..a3b40f5929 100644 --- a/evm/src/cpu/modfp254.rs +++ b/evm/src/cpu/modfp254.rs @@ -4,8 +4,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; // Python: diff --git a/evm/src/cpu/pc.rs b/evm/src/cpu/pc.rs index 9635534e50..4294dbaf61 100644 --- a/evm/src/cpu/pc.rs +++ b/evm/src/cpu/pc.rs @@ -2,8 +2,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; /// Evaluates constraints to check that we are storing the correct PC. diff --git a/evm/src/cpu/push0.rs b/evm/src/cpu/push0.rs index ed9f6c10f2..4f37a55e0b 100644 --- a/evm/src/cpu/push0.rs +++ b/evm/src/cpu/push0.rs @@ -2,8 +2,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; /// Evaluates constraints to check that we are not pushing anything. diff --git a/evm/src/cpu/shift.rs b/evm/src/cpu/shift.rs index 9e751421ff..12ed18b9ed 100644 --- a/evm/src/cpu/shift.rs +++ b/evm/src/cpu/shift.rs @@ -3,8 +3,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::membus::NUM_GP_CHANNELS; use crate::memory::segments::Segment; diff --git a/evm/src/cpu/simple_logic/eq_iszero.rs b/evm/src/cpu/simple_logic/eq_iszero.rs index fd811ae7f7..43333fd9ed 100644 --- a/evm/src/cpu/simple_logic/eq_iszero.rs +++ b/evm/src/cpu/simple_logic/eq_iszero.rs @@ -5,8 +5,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::stack::{self, EQ_STACK_BEHAVIOR, IS_ZERO_STACK_BEHAVIOR}; diff --git a/evm/src/cpu/simple_logic/mod.rs b/evm/src/cpu/simple_logic/mod.rs index 04f8bcc2da..748930f2ee 100644 --- a/evm/src/cpu/simple_logic/mod.rs +++ b/evm/src/cpu/simple_logic/mod.rs @@ -5,8 +5,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; /// Evaluates constraints for NOT, EQ and ISZERO. diff --git a/evm/src/cpu/simple_logic/not.rs b/evm/src/cpu/simple_logic/not.rs index 3798606de3..92b1156807 100644 --- a/evm/src/cpu/simple_logic/not.rs +++ b/evm/src/cpu/simple_logic/not.rs @@ -3,8 +3,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::stack; diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 87ca7ee1c4..e135e39175 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -6,8 +6,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::ops::OpsColumnsView; use crate::cpu::columns::CpuColumnsView; use crate::cpu::membus::NUM_GP_CHANNELS; diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index 1dfdb8fa2c..cf7aa72e0f 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -7,8 +7,8 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::membus::NUM_GP_CHANNELS; diff --git a/evm/src/evaluation_frame.rs b/evm/src/evaluation_frame.rs deleted file mode 100644 index 0f6bbe2ceb..0000000000 --- a/evm/src/evaluation_frame.rs +++ /dev/null @@ -1,47 +0,0 @@ -/// A trait for viewing an evaluation frame of a STARK table. -/// -/// It allows to access the current and next rows at a given step -/// and can be used to implement constraint evaluation both natively -/// and recursively. -pub trait StarkEvaluationFrame: Sized { - /// The number of columns for the STARK table this evaluation frame views. - const COLUMNS: usize; - - /// Returns the local values (i.e. current row) for this evaluation frame. - fn get_local_values(&self) -> &[T]; - /// Returns the next values (i.e. next row) for this evaluation frame. - fn get_next_values(&self) -> &[T]; - - /// Outputs a new evaluation frame from the provided local and next values. - /// - /// **NOTE**: Concrete implementations of this method SHOULD ensure that - /// the provided slices lengths match the `Self::COLUMNS` value. - fn from_values(lv: &[T], nv: &[T]) -> Self; -} - -pub struct StarkFrame { - local_values: [T; N], - next_values: [T; N], -} - -impl StarkEvaluationFrame for StarkFrame { - const COLUMNS: usize = N; - - fn get_local_values(&self) -> &[T] { - &self.local_values - } - - fn get_next_values(&self) -> &[T] { - &self.next_values - } - - fn from_values(lv: &[T], nv: &[T]) -> Self { - assert_eq!(lv.len(), Self::COLUMNS); - assert_eq!(nv.len(), Self::COLUMNS); - - Self { - local_values: lv.try_into().unwrap(), - next_values: nv.try_into().unwrap(), - } - } -} diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 2df85b03de..f12a485e8e 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -29,18 +29,18 @@ use plonky2::util::serialization::{ }; use plonky2::util::timing::TimingTree; use plonky2_util::log2_ceil; +use starky::config::StarkConfig; +use starky::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; +use starky::lookup::{get_grand_product_challenge_set_target, GrandProductChallengeSet}; +use starky::proof::StarkProofWithMetadata; +use starky::stark::Stark; use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; -use crate::config::StarkConfig; -use crate::cross_table_lookup::{ - get_grand_product_challenge_set_target, verify_cross_table_lookups_circuit, CrossTableLookup, - GrandProductChallengeSet, -}; use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; use crate::proof::{ AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, - PublicValues, PublicValuesTarget, StarkProofWithMetadata, TrieRoots, TrieRootsTarget, + PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, }; use crate::prover::{check_abort_signal, prove}; use crate::recursive_verifier::{ @@ -48,7 +48,6 @@ use crate::recursive_verifier::{ recursive_stark_circuit, set_public_value_targets, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; -use crate::stark::Stark; use crate::util::h256_limbs; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. @@ -587,7 +586,7 @@ where &mut builder, all_cross_table_lookups(), pis.map(|p| p.ctl_zs_first), - extra_looking_sums, + Some(&extra_looking_sums), stark_config, ); @@ -1002,7 +1001,7 @@ where let mut root_inputs = PartialWitness::new(); for table in 0..NUM_TABLES { - let stark_proof = &all_proof.stark_proofs[table]; + let stark_proof = &all_proof.multi_proof.stark_proofs[table]; let original_degree_bits = stark_proof.proof.recover_degree_bits(config); let table_circuits = &self.by_table[table]; let shrunk_proof = table_circuits @@ -1015,7 +1014,7 @@ where original_degree_bits, )) })? - .shrink(stark_proof, &all_proof.ctl_challenges)?; + .shrink(stark_proof, &all_proof.multi_proof.ctl_challenges)?; let index_verifier_data = table_circuits .by_stark_size .keys() @@ -1107,9 +1106,10 @@ where for table in 0..NUM_TABLES { let (table_circuit, index_verifier_data) = &table_circuits[table]; - let stark_proof = &all_proof.stark_proofs[table]; + let stark_proof = &all_proof.multi_proof.stark_proofs[table]; - let shrunk_proof = table_circuit.shrink(stark_proof, &all_proof.ctl_challenges)?; + let shrunk_proof = + table_circuit.shrink(stark_proof, &all_proof.multi_proof.ctl_challenges)?; root_inputs.set_target( self.root.index_verifier_data[table], F::from_canonical_u8(*index_verifier_data), diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index b63f48a1c7..6da0e38b7b 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -10,13 +10,13 @@ use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; use serde::{Deserialize, Serialize}; +use starky::config::StarkConfig; use GlobalMetadata::{ ReceiptTrieRootDigestAfter, ReceiptTrieRootDigestBefore, StateTrieRootDigestAfter, StateTrieRootDigestBefore, TransactionTrieRootDigestAfter, TransactionTrieRootDigestBefore, }; use crate::all_stark::{AllStark, NUM_TABLES}; -use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 756b0650da..2a783b940b 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -1,13 +1,11 @@ use ethereum_types::{BigEndianHash, H256, U256}; use plonky2::field::extension::Extendable; -use plonky2::fri::proof::{FriProof, FriProofTarget}; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; -use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use starky::config::StarkConfig; +use starky::lookup::get_grand_product_challenge_set; -use crate::config::StarkConfig; -use crate::cross_table_lookup::get_grand_product_challenge_set; use crate::proof::*; use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; use crate::witness::errors::ProgramError; @@ -198,7 +196,9 @@ impl, C: GenericConfig, const D: usize> A ) -> Result, ProgramError> { let mut challenger = Challenger::::new(); - for proof in &self.stark_proofs { + let stark_proofs = &self.multi_proof.stark_proofs; + + for proof in stark_proofs { challenger.observe_cap(&proof.proof.trace_cap); } @@ -210,112 +210,14 @@ impl, C: GenericConfig, const D: usize> A Ok(AllProofChallenges { stark_challenges: core::array::from_fn(|i| { challenger.compact(); - self.stark_proofs[i] - .proof - .get_challenges(&mut challenger, config) + stark_proofs[i].proof.get_challenges( + &mut challenger, + Some(&ctl_challenges), + true, + config, + ) }), ctl_challenges, }) } } - -impl StarkProof -where - F: RichField + Extendable, - C: GenericConfig, -{ - /// Computes all Fiat-Shamir challenges used in the STARK proof. - pub(crate) fn get_challenges( - &self, - challenger: &mut Challenger, - config: &StarkConfig, - ) -> StarkProofChallenges { - let degree_bits = self.recover_degree_bits(config); - - let StarkProof { - auxiliary_polys_cap, - quotient_polys_cap, - openings, - opening_proof: - FriProof { - commit_phase_merkle_caps, - final_poly, - pow_witness, - .. - }, - .. - } = &self; - - let num_challenges = config.num_challenges; - - challenger.observe_cap(auxiliary_polys_cap); - - let stark_alphas = challenger.get_n_challenges(num_challenges); - - challenger.observe_cap(quotient_polys_cap); - let stark_zeta = challenger.get_extension_challenge::(); - - challenger.observe_openings(&openings.to_fri_openings()); - - StarkProofChallenges { - stark_alphas, - stark_zeta, - fri_challenges: challenger.fri_challenges::( - commit_phase_merkle_caps, - final_poly, - *pow_witness, - degree_bits, - &config.fri_config, - ), - } - } -} - -impl StarkProofTarget { - pub(crate) fn get_challenges, C: GenericConfig>( - &self, - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, - config: &StarkConfig, - ) -> StarkProofChallengesTarget - where - C::Hasher: AlgebraicHasher, - { - let StarkProofTarget { - auxiliary_polys_cap: auxiliary_polys, - quotient_polys_cap, - openings, - opening_proof: - FriProofTarget { - commit_phase_merkle_caps, - final_poly, - pow_witness, - .. - }, - .. - } = &self; - - let num_challenges = config.num_challenges; - - challenger.observe_cap(auxiliary_polys); - - let stark_alphas = challenger.get_n_challenges(builder, num_challenges); - - challenger.observe_cap(quotient_polys_cap); - let stark_zeta = challenger.get_extension_challenge(builder); - - challenger.observe_openings(&openings.to_fri_openings(builder.zero())); - - StarkProofChallengesTarget { - stark_alphas, - stark_zeta, - fri_challenges: challenger.fri_challenges( - builder, - commit_phase_merkle_caps, - final_poly, - *pow_witness, - &config.fri_config, - ), - } - } -} diff --git a/evm/src/keccak/columns.rs b/evm/src/keccak/columns.rs index eedba41c0f..bbd96a7426 100644 --- a/evm/src/keccak/columns.rs +++ b/evm/src/keccak/columns.rs @@ -1,7 +1,7 @@ use plonky2::field::types::Field; +use starky::lookup::Column; use crate::keccak::keccak_stark::{NUM_INPUTS, NUM_ROUNDS}; -use crate::lookup::Column; /// A register which is set to 1 if we are in the `i`th round, otherwise 0. pub(crate) const fn reg_step(i: usize) -> usize { diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 771c9b4371..fc27086ae5 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -10,10 +10,14 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::plonk_common::reduce_with_powers_ext_circuit; use plonky2::timed; use plonky2::util::timing::TimingTree; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter}; +use starky::stark::Stark; +use starky::util::trace_rows_to_poly_values; use super::columns::reg_input_limb; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +use crate::all_stark::EvmStarkFrame; use crate::keccak::columns::{ reg_a, reg_a_prime, reg_a_prime_prime, reg_a_prime_prime_0_0_bit, reg_a_prime_prime_prime, reg_b, reg_c, reg_c_prime, reg_output_limb, reg_step, NUM_COLUMNS, TIMESTAMP, @@ -23,9 +27,6 @@ use crate::keccak::logic::{ andn, andn_gen, andn_gen_circuit, xor, xor3_gen, xor3_gen_circuit, xor_gen, xor_gen_circuit, }; use crate::keccak::round_flags::{eval_round_flags, eval_round_flags_recursively}; -use crate::lookup::{Column, Filter}; -use crate::stark::Stark; -use crate::util::trace_rows_to_poly_values; /// Number of rounds in a Keccak permutation. pub(crate) const NUM_ROUNDS: usize = 24; @@ -253,12 +254,12 @@ impl, const D: usize> KeccakStark { } impl, const D: usize> Stark for KeccakStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; + type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; fn eval_packed_generic( &self, @@ -616,6 +617,10 @@ impl, const D: usize> Stark for KeccakStark usize { 3 } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] @@ -626,14 +631,14 @@ mod tests { use plonky2::fri::oracle::PolynomialBatch; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::config::StarkConfig; + use starky::cross_table_lookup::{CtlData, CtlZData}; + use starky::lookup::{GrandProductChallenge, GrandProductChallengeSet}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use tiny_keccak::keccakf; use super::*; - use crate::config::StarkConfig; - use crate::cross_table_lookup::{CtlData, CtlZData, GrandProductChallengeSet}; - use crate::lookup::GrandProductChallenge; use crate::prover::prove_single_table; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { @@ -734,16 +739,16 @@ mod tests { let degree = 1 << trace_commitments.degree_log; // Fake CTL data. - let ctl_z_data = CtlZData { - helper_columns: vec![PolynomialValues::zero(degree)], - z: PolynomialValues::zero(degree), - challenge: GrandProductChallenge { + let ctl_z_data = CtlZData::new( + vec![PolynomialValues::zero(degree)], + PolynomialValues::zero(degree), + GrandProductChallenge { beta: F::ZERO, gamma: F::ZERO, }, - columns: vec![], - filter: vec![Some(Filter::new_simple(Column::constant(F::ZERO)))], - }; + vec![], + vec![Some(Filter::new_simple(Column::constant(F::ZERO)))], + ); let ctl_data = CtlData { zs_columns: vec![ctl_z_data.clone(); config.num_challenges], }; diff --git a/evm/src/keccak/round_flags.rs b/evm/src/keccak/round_flags.rs index 9ad144f7ef..5e76b2ec9c 100644 --- a/evm/src/keccak/round_flags.rs +++ b/evm/src/keccak/round_flags.rs @@ -4,14 +4,15 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +use crate::all_stark::EvmStarkFrame; use crate::keccak::columns::{reg_step, NUM_COLUMNS}; use crate::keccak::keccak_stark::NUM_ROUNDS; pub(crate) fn eval_round_flags>( - vars: &StarkFrame, + vars: &EvmStarkFrame, yield_constr: &mut ConstraintConsumer

, ) { let local_values = vars.get_local_values(); @@ -40,7 +41,7 @@ pub(crate) fn eval_round_flags>( pub(crate) fn eval_round_flags_recursively, const D: usize>( builder: &mut CircuitBuilder, - vars: &StarkFrame, NUM_COLUMNS>, + vars: &EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>, yield_constr: &mut RecursiveConstraintConsumer, ) { let one = builder.one_extension(); diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index ddf2bca00e..04b1bca63b 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -14,13 +14,14 @@ use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; use plonky2_util::ceil_div_usize; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter, Lookup}; +use starky::stark::Stark; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::all_stark::EvmStarkFrame; use crate::cpu::kernel::keccak_util::keccakf_u32s; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::keccak_sponge::columns::*; -use crate::lookup::{Column, Filter, Lookup}; -use crate::stark::Stark; use crate::witness::memory::MemoryAddress; /// Strict upper bound for the individual bytes range-check. @@ -520,12 +521,13 @@ impl, const D: usize> KeccakSpongeStark { } impl, const D: usize> Stark for KeccakSpongeStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_KECCAK_SPONGE_COLUMNS>; + type EvaluationFrameTarget = + EvmStarkFrame, ExtensionTarget, NUM_KECCAK_SPONGE_COLUMNS>; fn eval_packed_generic( &self, @@ -807,6 +809,10 @@ impl, const D: usize> Stark for KeccakSpongeS filter_columns: vec![None; KECCAK_RATE_BYTES], }] } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] @@ -816,10 +822,10 @@ mod tests { use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::field::types::PrimeField64; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use super::*; use crate::memory::segments::Segment; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 025fc8e63d..5741c4419e 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -165,35 +165,32 @@ #![allow(unused)] #![feature(let_chains)] -pub mod all_stark; +// Individual STARK processing units pub mod arithmetic; pub mod byte_packing; -pub mod config; -pub mod constraint_consumer; pub mod cpu; -pub mod cross_table_lookup; -pub mod curve_pairings; -pub mod evaluation_frame; -pub mod extension_tower; -pub mod fixed_recursive_verifier; -pub mod generation; -mod get_challenges; pub mod keccak; pub mod keccak_sponge; pub mod logic; -pub mod lookup; pub mod memory; + +// Proving system components +pub mod all_stark; +pub mod fixed_recursive_verifier; +mod get_challenges; pub mod proof; pub mod prover; pub mod recursive_verifier; -pub mod stark; -pub mod util; -pub mod vanishing_poly; pub mod verifier; + +// Witness generation +pub mod generation; pub mod witness; -#[cfg(test)] -mod stark_testing; +// Utility modules +pub mod curve_pairings; +pub mod extension_tower; +pub mod util; use eth_trie_utils::partial_trie::HashedPartialTrie; // Set up Jemalloc @@ -209,6 +206,6 @@ static GLOBAL: Jemalloc = Jemalloc; pub type Node = eth_trie_utils::partial_trie::Node; pub use all_stark::AllStark; -pub use config::StarkConfig; pub use fixed_recursive_verifier::AllRecursiveCircuits; pub use generation::GenerationInputs; +pub use starky::config::StarkConfig; diff --git a/evm/src/logic.rs b/evm/src/logic.rs index 7300c6af65..d07a6e3d18 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -11,13 +11,15 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2_util::ceil_div_usize; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter}; +use starky::stark::Stark; +use starky::util::trace_rows_to_poly_values; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +use crate::all_stark::EvmStarkFrame; use crate::logic::columns::NUM_COLUMNS; -use crate::lookup::{Column, Filter}; -use crate::stark::Stark; -use crate::util::{limb_from_bits_le, limb_from_bits_le_recursive, trace_rows_to_poly_values}; +use crate::util::{limb_from_bits_le, limb_from_bits_le_recursive}; /// Total number of bits per input/output. const VAL_BITS: usize = 256; @@ -210,12 +212,12 @@ impl LogicStark { } impl, const D: usize> Stark for LogicStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; + type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; fn eval_packed_generic( &self, @@ -354,15 +356,19 @@ impl, const D: usize> Stark for LogicStark usize { 3 } + + fn requires_ctls(&self) -> bool { + true + } } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use crate::logic::LogicStark; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs deleted file mode 100644 index f98814f9a1..0000000000 --- a/evm/src/lookup.rs +++ /dev/null @@ -1,895 +0,0 @@ -use core::borrow::Borrow; -use core::fmt::Debug; -use core::iter::repeat; - -use itertools::Itertools; -use num_bigint::BigUint; -use plonky2::field::batch_util::batch_add_inplace; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::iop::target::Target; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::plonk_common::{ - reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, -}; -use plonky2_util::ceil_div_usize; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::stark::Stark; - -/// Represents a filter, which evaluates to 1 if the row must be considered and 0 if it should be ignored. -/// It's an arbitrary degree 2 combination of columns: `products` are the degree 2 terms, and `constants` are -/// the degree 1 terms. -#[derive(Clone, Debug)] -pub(crate) struct Filter { - products: Vec<(Column, Column)>, - constants: Vec>, -} - -impl Filter { - pub(crate) fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { - Self { - products, - constants, - } - } - - /// Returns a filter made of a single column. - pub(crate) fn new_simple(col: Column) -> Self { - Self { - products: vec![], - constants: vec![col], - } - } - - /// Given the column values for the current and next rows, evaluates the filter. - pub(crate) fn eval_filter(&self, v: &[P], next_v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.products - .iter() - .map(|(col1, col2)| col1.eval_with_next(v, next_v) * col2.eval_with_next(v, next_v)) - .sum::

() - + self - .constants - .iter() - .map(|col| col.eval_with_next(v, next_v)) - .sum::

() - } - - /// Circuit version of `eval_filter`: - /// Given the column values for the current and next rows, evaluates the filter. - pub(crate) fn eval_filter_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - next_v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let prods = self - .products - .iter() - .map(|(col1, col2)| { - let col1_eval = col1.eval_with_next_circuit(builder, v, next_v); - let col2_eval = col2.eval_with_next_circuit(builder, v, next_v); - builder.mul_extension(col1_eval, col2_eval) - }) - .collect::>(); - - let consts = self - .constants - .iter() - .map(|col| col.eval_with_next_circuit(builder, v, next_v)) - .collect::>(); - - let prods = builder.add_many_extension(prods); - let consts = builder.add_many_extension(consts); - builder.add_extension(prods, consts) - } - - /// Evaluate on a row of a table given in column-major form. - pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - self.products - .iter() - .map(|(col1, col2)| col1.eval_table(table, row) * col2.eval_table(table, row)) - .sum::() - + self - .constants - .iter() - .map(|col| col.eval_table(table, row)) - .sum() - } -} - -/// Represent two linear combination of columns, corresponding to the current and next row values. -/// Each linear combination is represented as: -/// - a vector of `(usize, F)` corresponding to the column number and the associated multiplicand -/// - the constant of the linear combination. -#[derive(Clone, Debug)] -pub(crate) struct Column { - linear_combination: Vec<(usize, F)>, - next_row_linear_combination: Vec<(usize, F)>, - constant: F, -} - -impl Column { - /// Returns the representation of a single column in the current row. - pub(crate) fn single(c: usize) -> Self { - Self { - linear_combination: vec![(c, F::ONE)], - next_row_linear_combination: vec![], - constant: F::ZERO, - } - } - - /// Returns multiple single columns in the current row. - pub(crate) fn singles>>( - cs: I, - ) -> impl Iterator { - cs.into_iter().map(|c| Self::single(*c.borrow())) - } - - /// Returns the representation of a single column in the next row. - pub(crate) fn single_next_row(c: usize) -> Self { - Self { - linear_combination: vec![], - next_row_linear_combination: vec![(c, F::ONE)], - constant: F::ZERO, - } - } - - /// Returns multiple single columns for the next row. - pub(crate) fn singles_next_row>>( - cs: I, - ) -> impl Iterator { - cs.into_iter().map(|c| Self::single_next_row(*c.borrow())) - } - - /// Returns a linear combination corresponding to a constant. - pub(crate) fn constant(constant: F) -> Self { - Self { - linear_combination: vec![], - next_row_linear_combination: vec![], - constant, - } - } - - /// Returns a linear combination corresponding to 0. - pub(crate) fn zero() -> Self { - Self::constant(F::ZERO) - } - - /// Returns a linear combination corresponding to 1. - pub(crate) fn one() -> Self { - Self::constant(F::ONE) - } - - /// Given an iterator of `(usize, F)` and a constant, returns the association linear combination of columns for the current row. - pub(crate) fn linear_combination_with_constant>( - iter: I, - constant: F, - ) -> Self { - let v = iter.into_iter().collect::>(); - assert!(!v.is_empty()); - debug_assert_eq!( - v.iter().map(|(c, _)| c).unique().count(), - v.len(), - "Duplicate columns." - ); - Self { - linear_combination: v, - next_row_linear_combination: vec![], - constant, - } - } - - /// Given an iterator of `(usize, F)` and a constant, returns the associated linear combination of columns for the current and the next rows. - pub(crate) fn linear_combination_and_next_row_with_constant< - I: IntoIterator, - >( - iter: I, - next_row_iter: I, - constant: F, - ) -> Self { - let v = iter.into_iter().collect::>(); - let next_row_v = next_row_iter.into_iter().collect::>(); - - assert!(!v.is_empty() || !next_row_v.is_empty()); - debug_assert_eq!( - v.iter().map(|(c, _)| c).unique().count(), - v.len(), - "Duplicate columns." - ); - debug_assert_eq!( - next_row_v.iter().map(|(c, _)| c).unique().count(), - next_row_v.len(), - "Duplicate columns." - ); - - Self { - linear_combination: v, - next_row_linear_combination: next_row_v, - constant, - } - } - - /// Returns a linear combination of columns, with no additional constant. - pub(crate) fn linear_combination>(iter: I) -> Self { - Self::linear_combination_with_constant(iter, F::ZERO) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: - /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n. - pub(crate) fn le_bits>>(cs: I) -> Self { - Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers())) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bits in little endian order: - /// returns the representation of c_0 + 2 * c_1 + ... + 2^n * c_n + k where `k` is an - /// additional constant. - pub(crate) fn le_bits_with_constant>>( - cs: I, - constant: F, - ) -> Self { - Self::linear_combination_with_constant( - cs.into_iter().map(|c| *c.borrow()).zip(F::TWO.powers()), - constant, - ) - } - - /// Given an iterator of columns (c_0, ..., c_n) containing bytes in little endian order: - /// returns the representation of c_0 + 256 * c_1 + ... + 256^n * c_n. - pub(crate) fn le_bytes>>(cs: I) -> Self { - Self::linear_combination( - cs.into_iter() - .map(|c| *c.borrow()) - .zip(F::from_canonical_u16(256).powers()), - ) - } - - /// Given an iterator of columns, returns the representation of their sum. - pub(crate) fn sum>>(cs: I) -> Self { - Self::linear_combination(cs.into_iter().map(|c| *c.borrow()).zip(repeat(F::ONE))) - } - - /// Given the column values for the current row, returns the evaluation of the linear combination. - pub(crate) fn eval(&self, v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.linear_combination - .iter() - .map(|&(c, f)| v[c] * FE::from_basefield(f)) - .sum::

() - + FE::from_basefield(self.constant) - } - - /// Given the column values for the current and next rows, evaluates the current and next linear combinations and returns their sum. - pub(crate) fn eval_with_next(&self, v: &[P], next_v: &[P]) -> P - where - FE: FieldExtension, - P: PackedField, - { - self.linear_combination - .iter() - .map(|&(c, f)| v[c] * FE::from_basefield(f)) - .sum::

() - + self - .next_row_linear_combination - .iter() - .map(|&(c, f)| next_v[c] * FE::from_basefield(f)) - .sum::

() - + FE::from_basefield(self.constant) - } - - /// Evaluate on a row of a table given in column-major form. - pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - let mut res = self - .linear_combination - .iter() - .map(|&(c, f)| table[c].values[row] * f) - .sum::() - + self.constant; - - // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. - // If the lookups are correctly written, the filter should be 0 in that case anyway. - if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { - res += self - .next_row_linear_combination - .iter() - .map(|&(c, f)| table[c].values[row + 1] * f) - .sum::(); - } - - res - } - - /// Evaluates the column on all rows. - pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { - let length = table[0].len(); - (0..length) - .map(|row| self.eval_table(table, row)) - .collect::>() - } - - /// Circuit version of `eval`: Given a row's targets, returns their linear combination. - pub(crate) fn eval_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let pairs = self - .linear_combination - .iter() - .map(|&(c, f)| { - ( - v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }) - .collect::>(); - let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); - builder.inner_product_extension(F::ONE, constant, pairs) - } - - /// Circuit version of `eval_with_next`: - /// Given the targets of the current and next row, returns the sum of their linear combinations. - pub(crate) fn eval_with_next_circuit( - &self, - builder: &mut CircuitBuilder, - v: &[ExtensionTarget], - next_v: &[ExtensionTarget], - ) -> ExtensionTarget - where - F: RichField + Extendable, - { - let mut pairs = self - .linear_combination - .iter() - .map(|&(c, f)| { - ( - v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }) - .collect::>(); - let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| { - ( - next_v[c], - builder.constant_extension(F::Extension::from_basefield(f)), - ) - }); - pairs.extend(next_row_pairs); - let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); - builder.inner_product_extension(F::ONE, constant, pairs) - } -} - -pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); - -pub struct Lookup { - /// Columns whose values should be contained in the lookup table. - /// These are the f_i(x) polynomials in the logUp paper. - pub(crate) columns: Vec>, - /// Column containing the lookup table. - /// This is the t(x) polynomial in the paper. - pub(crate) table_column: Column, - /// Column containing the frequencies of `columns` in `table_column`. - /// This is the m(x) polynomial in the paper. - pub(crate) frequencies_column: Column, - - /// Columns to filter some elements. There is at most one filter - /// column per column to range-check. - pub(crate) filter_columns: Vec>>, -} - -impl Lookup { - pub(crate) fn num_helper_columns(&self, constraint_degree: usize) -> usize { - // One helper column for each column batch of size `constraint_degree-1`, - // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. - ceil_div_usize(self.columns.len(), constraint_degree - 1) + 1 - } -} - -/// Randomness for a single instance of a permutation check protocol. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) struct GrandProductChallenge { - /// Randomness used to combine multiple columns into one. - pub(crate) beta: T, - /// Random offset that's added to the beta-reduced column values. - pub(crate) gamma: T, -} - -impl GrandProductChallenge { - pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( - &self, - terms: T, - ) -> P - where - FE: FieldExtension, - P: PackedField, - T::IntoIter: DoubleEndedIterator, - { - reduce_with_powers(terms, FE::from_basefield(self.beta)) + FE::from_basefield(self.gamma) - } -} - -impl GrandProductChallenge { - pub(crate) fn combine_circuit, const D: usize>( - &self, - builder: &mut CircuitBuilder, - terms: &[ExtensionTarget], - ) -> ExtensionTarget { - let reduced = reduce_with_powers_ext_circuit(builder, terms, self.beta); - let gamma = builder.convert_to_ext(self.gamma); - builder.add_extension(reduced, gamma) - } -} - -impl GrandProductChallenge { - pub(crate) fn combine_base_circuit, const D: usize>( - &self, - builder: &mut CircuitBuilder, - terms: &[Target], - ) -> Target { - let reduced = reduce_with_powers_circuit(builder, terms, self.beta); - builder.add(reduced, self.gamma) - } -} - -/// logUp protocol from -/// Compute the helper columns for the lookup argument. -/// Given columns `f0,...,fk` and a column `t`, such that `∪fi ⊆ t`, and challenges `x`, -/// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`, -/// and `Z(gx) = Z(x) + sum h_i(x) - m(x)g(x)` where `m` is the frequencies column. -pub(crate) fn lookup_helper_columns( - lookup: &Lookup, - trace_poly_values: &[PolynomialValues], - challenge: F, - constraint_degree: usize, -) -> Vec> { - assert_eq!( - constraint_degree, 3, - "TODO: Allow other constraint degrees." - ); - - assert_eq!(lookup.columns.len(), lookup.filter_columns.len()); - - let num_total_logup_entries = trace_poly_values[0].values.len() * lookup.columns.len(); - assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); - - let num_helper_columns = lookup.num_helper_columns(constraint_degree); - - let looking_cols = lookup - .columns - .iter() - .map(|col| vec![col.clone()]) - .collect::>>>(); - - let grand_challenge = GrandProductChallenge { - beta: F::ONE, - gamma: challenge, - }; - - let columns_filters = looking_cols - .iter() - .zip(lookup.filter_columns.iter()) - .map(|(col, filter)| (&col[..], filter)) - .collect::>(); - // For each batch of `constraint_degree-1` columns `fi`, compute `sum 1/(f_i+challenge)` and - // add it to the helper columns. - // Note: these are the h_k(x) polynomials in the paper, with a few differences: - // * Here, the first ratio m_0(x)/phi_0(x) is not included with the columns batched up to create the - // h_k polynomials; instead there's a separate helper column for it (see below). - // * Here, we use 1 instead of -1 as the numerator (and subtract later). - // * Here, for now, the batch size (l) is always constraint_degree - 1 = 2. - // * Here, there are filters for the columns, to only select some rows - // in a given column. - let mut helper_columns = get_helper_cols( - trace_poly_values, - trace_poly_values[0].len(), - &columns_filters, - grand_challenge, - constraint_degree, - ); - - // Add `1/(table+challenge)` to the helper columns. - // This is 1/phi_0(x) = 1/(x + t(x)) from the paper. - // Here, we don't include m(x) in the numerator, instead multiplying it with this column later. - let mut table = lookup.table_column.eval_all_rows(trace_poly_values); - for x in table.iter_mut() { - *x = challenge + *x; - } - let table_inverse: Vec = F::batch_multiplicative_inverse(&table); - - // Compute the `Z` polynomial with `Z(1)=0` and `Z(gx) = Z(x) + sum h_i(x) - frequencies(x)g(x)`. - // This enforces the check from the paper, that the sum of the h_k(x) polynomials is 0 over H. - // In the paper, that sum includes m(x)/(x + t(x)) = frequencies(x)/g(x), because that was bundled - // into the h_k(x) polynomials. - let frequencies = &lookup.frequencies_column.eval_all_rows(trace_poly_values); - let mut z = Vec::with_capacity(frequencies.len()); - z.push(F::ZERO); - for i in 0..frequencies.len() - 1 { - let x = helper_columns[..num_helper_columns - 1] - .iter() - .map(|col| col.values[i]) - .sum::() - - frequencies[i] * table_inverse[i]; - z.push(z[i] + x); - } - helper_columns.push(z.into()); - - helper_columns -} - -/// Given data associated to a lookup, check the associated helper polynomials. -pub(crate) fn eval_helper_columns( - filter: &[Option>], - columns: &[Vec

], - local_values: &[P], - next_values: &[P], - helper_columns: &[P], - constraint_degree: usize, - challenges: &GrandProductChallenge, - consumer: &mut ConstraintConsumer

, -) where - F: RichField + Extendable, - FE: FieldExtension, - P: PackedField, -{ - if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - - match chunk.len() { - 2 => { - let combin0 = challenges.combine(&chunk[0]); - let combin1 = challenges.combine(chunk[1].iter()); - - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter(local_values, next_values) - } else { - P::ONES - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; - - consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); - } - 1 => { - let combin = challenges.combine(&chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; - consumer.constraint(combin * h - f0); - } - - _ => todo!("Allow other constraint degrees"), - } - } - } -} - -/// Circuit version of `eval_helper_columns`. -/// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. -pub(crate) fn eval_helper_columns_circuit, const D: usize>( - builder: &mut CircuitBuilder, - filter: &[Option>], - columns: &[Vec>], - local_values: &[ExtensionTarget], - next_values: &[ExtensionTarget], - helper_columns: &[ExtensionTarget], - constraint_degree: usize, - challenges: &GrandProductChallenge, - consumer: &mut RecursiveConstraintConsumer, -) { - if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - - let one = builder.one_extension(); - match chunk.len() { - 2 => { - let combin0 = challenges.combine_circuit(builder, &chunk[0]); - let combin1 = challenges.combine_circuit(builder, &chunk[1]); - - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - - let constr = builder.mul_sub_extension(combin0, h, f0); - let constr = builder.mul_extension(constr, combin1); - let f1_constr = builder.mul_extension(f1, combin0); - let constr = builder.sub_extension(constr, f1_constr); - - consumer.constraint(builder, constr); - } - 1 => { - let combin = challenges.combine_circuit(builder, &chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let constr = builder.mul_sub_extension(combin, h, f0); - consumer.constraint(builder, constr); - } - - _ => todo!("Allow other constraint degrees"), - } - } - } -} - -/// Given a STARK's trace, and the data associated to one lookup (either CTL or range check), -/// returns the associated helper polynomials. -pub(crate) fn get_helper_cols( - trace: &[PolynomialValues], - degree: usize, - columns_filters: &[ColumnFilter], - challenge: GrandProductChallenge, - constraint_degree: usize, -) -> Vec> { - let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); - - let mut helper_columns = Vec::with_capacity(num_helper_columns); - - for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { - let (first_col, first_filter) = cols_filts.next().unwrap(); - - let mut filter_col = Vec::with_capacity(degree); - let first_combined = (0..degree) - .map(|d| { - let f = if let Some(filter) = first_filter { - let f = filter.eval_table(trace, d); - filter_col.push(f); - f - } else { - filter_col.push(F::ONE); - F::ONE - }; - if f.is_one() { - let evals = first_col - .iter() - .map(|c| c.eval_table(trace, d)) - .collect::>(); - challenge.combine(evals.iter()) - } else { - assert_eq!(f, F::ZERO, "Non-binary filter?"); - // Dummy value. Cannot be zero since it will be batch-inverted. - F::ONE - } - }) - .collect::>(); - - let mut acc = F::batch_multiplicative_inverse(&first_combined); - for d in 0..degree { - if filter_col[d].is_zero() { - acc[d] = F::ZERO; - } - } - - for (col, filt) in cols_filts { - let mut filter_col = Vec::with_capacity(degree); - let mut combined = (0..degree) - .map(|d| { - let f = if let Some(filter) = filt { - let f = filter.eval_table(trace, d); - filter_col.push(f); - f - } else { - filter_col.push(F::ONE); - F::ONE - }; - if f.is_one() { - let evals = col - .iter() - .map(|c| c.eval_table(trace, d)) - .collect::>(); - challenge.combine(evals.iter()) - } else { - assert_eq!(f, F::ZERO, "Non-binary filter?"); - // Dummy value. Cannot be zero since it will be batch-inverted. - F::ONE - } - }) - .collect::>(); - - combined = F::batch_multiplicative_inverse(&combined); - - for d in 0..degree { - if filter_col[d].is_zero() { - combined[d] = F::ZERO; - } - } - - batch_add_inplace(&mut acc, &combined); - } - - helper_columns.push(acc.into()); - } - assert_eq!(helper_columns.len(), num_helper_columns); - - helper_columns -} - -pub(crate) struct LookupCheckVars -where - F: Field, - FE: FieldExtension, - P: PackedField, -{ - pub(crate) local_values: Vec

, - pub(crate) next_values: Vec

, - pub(crate) challenges: Vec, -} - -/// Constraints for the logUp lookup argument. -pub(crate) fn eval_packed_lookups_generic( - stark: &S, - lookups: &[Lookup], - vars: &S::EvaluationFrame, - lookup_vars: LookupCheckVars, - yield_constr: &mut ConstraintConsumer

, -) where - F: RichField + Extendable, - FE: FieldExtension, - P: PackedField, - S: Stark, -{ - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - let degree = stark.constraint_degree(); - assert_eq!(degree, 3, "TODO: Allow other constraint degrees."); - let mut start = 0; - for lookup in lookups { - let num_helper_columns = lookup.num_helper_columns(degree); - for &challenge in &lookup_vars.challenges { - let grand_challenge = GrandProductChallenge { - beta: F::ONE, - gamma: challenge, - }; - let lookup_columns = lookup - .columns - .iter() - .map(|col| vec![col.eval_with_next(local_values, next_values)]) - .collect::>>(); - - // For each chunk, check that `h_i (x+f_2i) (x+f_{2i+1}) = (x+f_2i) * filter_{2i+1} + (x+f_{2i+1}) * filter_2i` if the chunk has length 2 - // or if it has length 1, check that `h_i * (x+f_2i) = filter_2i`, where x is the challenge - eval_helper_columns( - &lookup.filter_columns, - &lookup_columns, - local_values, - next_values, - &lookup_vars.local_values[start..start + num_helper_columns - 1], - degree, - &grand_challenge, - yield_constr, - ); - - let challenge = FE::from_basefield(challenge); - - // Check the `Z` polynomial. - let z = lookup_vars.local_values[start + num_helper_columns - 1]; - let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; - let table_with_challenge = lookup.table_column.eval(local_values) + challenge; - let y = lookup_vars.local_values[start..start + num_helper_columns - 1] - .iter() - .fold(P::ZEROS, |acc, x| acc + *x) - * table_with_challenge - - lookup.frequencies_column.eval(local_values); - // Check that in the first row, z = 0; - yield_constr.constraint_first_row(z); - yield_constr.constraint((next_z - z) * table_with_challenge - y); - start += num_helper_columns; - } - } -} - -pub(crate) struct LookupCheckVarsTarget { - pub(crate) local_values: Vec>, - pub(crate) next_values: Vec>, - pub(crate) challenges: Vec, -} - -pub(crate) fn eval_ext_lookups_circuit< - F: RichField + Extendable, - S: Stark, - const D: usize, ->( - builder: &mut CircuitBuilder, - stark: &S, - vars: &S::EvaluationFrameTarget, - lookup_vars: LookupCheckVarsTarget, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let degree = stark.constraint_degree(); - let lookups = stark.lookups(); - - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - assert_eq!(degree, 3, "TODO: Allow other constraint degrees."); - let mut start = 0; - for lookup in lookups { - let num_helper_columns = lookup.num_helper_columns(degree); - let col_values = lookup - .columns - .iter() - .map(|col| vec![col.eval_with_next_circuit(builder, local_values, next_values)]) - .collect::>(); - - for &challenge in &lookup_vars.challenges { - let grand_challenge = GrandProductChallenge { - beta: builder.one(), - gamma: challenge, - }; - - eval_helper_columns_circuit( - builder, - &lookup.filter_columns, - &col_values, - local_values, - next_values, - &lookup_vars.local_values[start..start + num_helper_columns - 1], - degree, - &grand_challenge, - yield_constr, - ); - let challenge = builder.convert_to_ext(challenge); - - let z = lookup_vars.local_values[start + num_helper_columns - 1]; - let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; - let table_column = lookup - .table_column - .eval_circuit(builder, vars.get_local_values()); - let table_with_challenge = builder.add_extension(table_column, challenge); - let mut y = builder.add_many_extension( - &lookup_vars.local_values[start..start + num_helper_columns - 1], - ); - - let frequencies_column = lookup - .frequencies_column - .eval_circuit(builder, vars.get_local_values()); - y = builder.mul_extension(y, table_with_challenge); - y = builder.sub_extension(y, frequencies_column); - - // Check that in the first row, z = 0; - yield_constr.constraint_first_row(builder, z); - let mut constraint = builder.sub_extension(next_z, z); - constraint = builder.mul_extension(constraint, table_with_challenge); - constraint = builder.sub_extension(constraint, y); - yield_constr.constraint(builder, constraint); - start += num_helper_columns; - } - } -} diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 44d2af6ae2..d8a818ff83 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -12,18 +12,19 @@ use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; use plonky2_maybe_rayon::*; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter, Lookup}; +use starky::stark::Stark; use super::segments::Segment; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Filter, Lookup}; +use crate::all_stark::EvmStarkFrame; use crate::memory::columns::{ value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, FILTER, FREQUENCIES, INITIALIZE_AUX, IS_READ, NUM_COLUMNS, RANGE_CHECK, SEGMENT_FIRST_CHANGE, TIMESTAMP, VIRTUAL_FIRST_CHANGE, }; use crate::memory::VALUE_LIMBS; -use crate::stark::Stark; use crate::witness::memory::MemoryOpKind::Read; use crate::witness::memory::{MemoryAddress, MemoryOp}; @@ -268,12 +269,12 @@ impl, const D: usize> MemoryStark { } impl, const D: usize> Stark for MemoryStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame = EvmStarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; + type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; fn eval_packed_generic( &self, @@ -569,15 +570,19 @@ impl, const D: usize> Stark for MemoryStark bool { + true + } } #[cfg(test)] pub(crate) mod tests { use anyhow::Result; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use crate::memory::memory_stark::MemoryStark; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 33640458d6..bc70dbb857 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -1,33 +1,24 @@ use ethereum_types::{Address, H256, U256}; -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::fri::oracle::PolynomialBatch; -use plonky2::fri::proof::{FriChallenges, FriChallengesTarget, FriProof, FriProofTarget}; -use plonky2::fri::structure::{ - FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget, -}; -use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; -use plonky2::hash::merkle_tree::MerkleCap; -use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::field::extension::Extendable; +use plonky2::hash::hash_types::RichField; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::plonk::config::GenericConfig; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; -use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; +use starky::config::StarkConfig; +use starky::lookup::GrandProductChallengeSet; +use starky::proof::{MultiProof, StarkProofChallenges}; use crate::all_stark::NUM_TABLES; -use crate::config::StarkConfig; -use crate::cross_table_lookup::GrandProductChallengeSet; use crate::util::{get_h160, get_h256, h2u}; /// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs. #[derive(Debug, Clone)] pub struct AllProof, C: GenericConfig, const D: usize> { - /// Proofs for all the different STARK modules. - pub stark_proofs: [StarkProofWithMetadata; NUM_TABLES], - /// Cross-table lookup challenges. - pub(crate) ctl_challenges: GrandProductChallengeSet, + /// A multi-proof containing all proofs for the different STARK modules and their + /// cross-table lookup challenges. + pub multi_proof: MultiProof, /// Public memory values used for the recursive proofs. pub public_values: PublicValues, } @@ -35,7 +26,7 @@ pub struct AllProof, C: GenericConfig, co impl, C: GenericConfig, const D: usize> AllProof { /// Returns the degree (i.e. the trace length) of each STARK. pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) + self.multi_proof.recover_degree_bits(config) } } @@ -821,309 +812,3 @@ impl ExtraBlockDataTarget { builder.connect(ed0.gas_used_after, ed1.gas_used_after); } } - -/// Merkle caps and openings that form the proof of a single STARK. -#[derive(Debug, Clone)] -pub struct StarkProof, C: GenericConfig, const D: usize> { - /// Merkle cap of LDEs of trace values. - pub trace_cap: MerkleCap, - /// Merkle cap of LDEs of lookup helper and CTL columns. - pub auxiliary_polys_cap: MerkleCap, - /// Merkle cap of LDEs of quotient polynomial evaluations. - pub quotient_polys_cap: MerkleCap, - /// Purported values of each polynomial at the challenge point. - pub openings: StarkOpeningSet, - /// A batch FRI argument for all openings. - pub opening_proof: FriProof, -} - -/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when -/// creating a recursive wrapper proof around a STARK proof. -#[derive(Debug, Clone)] -pub struct StarkProofWithMetadata -where - F: RichField + Extendable, - C: GenericConfig, -{ - /// Initial Fiat-Shamir state. - pub(crate) init_challenger_state: >::Permutation, - /// Proof for a single STARK. - pub(crate) proof: StarkProof, -} - -impl, C: GenericConfig, const D: usize> StarkProof { - /// Recover the length of the trace from a STARK proof and a STARK config. - pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { - let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] - .initial_trees_proof - .evals_proofs[0] - .1; - let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); - lde_bits - config.fri_config.rate_bits - } - - /// Returns the number of cross-table lookup polynomials computed for the current STARK. - pub fn num_ctl_zs(&self) -> usize { - self.openings.ctl_zs_first.len() - } -} - -/// Circuit version of `StarkProof`. -/// Merkle caps and openings that form the proof of a single STARK. -#[derive(Eq, PartialEq, Debug)] -pub(crate) struct StarkProofTarget { - /// `Target` for the Merkle cap if LDEs of trace values. - pub trace_cap: MerkleCapTarget, - /// `Target` for the Merkle cap of LDEs of lookup helper and CTL columns. - pub auxiliary_polys_cap: MerkleCapTarget, - /// `Target` for the Merkle cap of LDEs of quotient polynomial evaluations. - pub quotient_polys_cap: MerkleCapTarget, - /// `Target`s for the purported values of each polynomial at the challenge point. - pub openings: StarkOpeningSetTarget, - /// `Target`s for the batch FRI argument for all openings. - pub opening_proof: FriProofTarget, -} - -impl StarkProofTarget { - /// Serializes a STARK proof. - pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - buffer.write_target_merkle_cap(&self.trace_cap)?; - buffer.write_target_merkle_cap(&self.auxiliary_polys_cap)?; - buffer.write_target_merkle_cap(&self.quotient_polys_cap)?; - buffer.write_target_fri_proof(&self.opening_proof)?; - self.openings.to_buffer(buffer)?; - Ok(()) - } - - /// Deserializes a STARK proof. - pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { - let trace_cap = buffer.read_target_merkle_cap()?; - let auxiliary_polys_cap = buffer.read_target_merkle_cap()?; - let quotient_polys_cap = buffer.read_target_merkle_cap()?; - let opening_proof = buffer.read_target_fri_proof()?; - let openings = StarkOpeningSetTarget::from_buffer(buffer)?; - - Ok(Self { - trace_cap, - auxiliary_polys_cap, - quotient_polys_cap, - openings, - opening_proof, - }) - } - - /// Recover the length of the trace from a STARK proof and a STARK config. - pub(crate) fn recover_degree_bits(&self, config: &StarkConfig) -> usize { - let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] - .initial_trees_proof - .evals_proofs[0] - .1; - let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); - lde_bits - config.fri_config.rate_bits - } -} - -/// Randomness used for a STARK proof. -pub(crate) struct StarkProofChallenges, const D: usize> { - /// Random values used to combine STARK constraints. - pub stark_alphas: Vec, - - /// Point at which the STARK polynomials are opened. - pub stark_zeta: F::Extension, - - /// Randomness used in FRI. - pub fri_challenges: FriChallenges, -} - -/// Circuit version of `StarkProofChallenges`. -pub(crate) struct StarkProofChallengesTarget { - /// `Target`s for the random values used to combine STARK constraints. - pub stark_alphas: Vec, - /// `ExtensionTarget` for the point at which the STARK polynomials are opened. - pub stark_zeta: ExtensionTarget, - /// `Target`s for the randomness used in FRI. - pub fri_challenges: FriChallengesTarget, -} - -/// Purported values of each polynomial at the challenge point. -#[derive(Debug, Clone)] -pub struct StarkOpeningSet, const D: usize> { - /// Openings of trace polynomials at `zeta`. - pub local_values: Vec, - /// Openings of trace polynomials at `g * zeta`. - pub next_values: Vec, - /// Openings of lookups and cross-table lookups `Z` polynomials at `zeta`. - pub auxiliary_polys: Vec, - /// Openings of lookups and cross-table lookups `Z` polynomials at `g * zeta`. - pub auxiliary_polys_next: Vec, - /// Openings of cross-table lookups `Z` polynomials at `1`. - pub ctl_zs_first: Vec, - /// Openings of quotient polynomials at `zeta`. - pub quotient_polys: Vec, -} - -impl, const D: usize> StarkOpeningSet { - /// Returns a `StarkOpeningSet` given all the polynomial commitments, the number of permutation `Z`polynomials, - /// the evaluation point and a generator `g`. - /// Polynomials are evaluated at point `zeta` and, if necessary, at `g * zeta`. - pub fn new>( - zeta: F::Extension, - g: F, - trace_commitment: &PolynomialBatch, - auxiliary_polys_commitment: &PolynomialBatch, - quotient_commitment: &PolynomialBatch, - num_lookup_columns: usize, - num_ctl_polys: &[usize], - ) -> Self { - let total_num_helper_cols: usize = num_ctl_polys.iter().sum(); - - // Batch evaluates polynomials on the LDE, at a point `z`. - let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { - c.polynomials - .par_iter() - .map(|p| p.to_extension().eval(z)) - .collect::>() - }; - // Batch evaluates polynomials at a base field point `z`. - let eval_commitment_base = |z: F, c: &PolynomialBatch| { - c.polynomials - .par_iter() - .map(|p| p.eval(z)) - .collect::>() - }; - - let auxiliary_first = eval_commitment_base(F::ONE, auxiliary_polys_commitment); - let ctl_zs_first = auxiliary_first[num_lookup_columns + total_num_helper_cols..].to_vec(); - // `g * zeta`. - let zeta_next = zeta.scalar_mul(g); - Self { - local_values: eval_commitment(zeta, trace_commitment), - next_values: eval_commitment(zeta_next, trace_commitment), - auxiliary_polys: eval_commitment(zeta, auxiliary_polys_commitment), - auxiliary_polys_next: eval_commitment(zeta_next, auxiliary_polys_commitment), - ctl_zs_first, - quotient_polys: eval_commitment(zeta, quotient_commitment), - } - } - - /// Constructs the openings required by FRI. - /// All openings but `ctl_zs_first` are grouped together. - pub(crate) fn to_fri_openings(&self) -> FriOpenings { - let zeta_batch = FriOpeningBatch { - values: self - .local_values - .iter() - .chain(&self.auxiliary_polys) - .chain(&self.quotient_polys) - .copied() - .collect_vec(), - }; - let zeta_next_batch = FriOpeningBatch { - values: self - .next_values - .iter() - .chain(&self.auxiliary_polys_next) - .copied() - .collect_vec(), - }; - debug_assert!(!self.ctl_zs_first.is_empty()); - let ctl_first_batch = FriOpeningBatch { - values: self - .ctl_zs_first - .iter() - .copied() - .map(F::Extension::from_basefield) - .collect(), - }; - - FriOpenings { - batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], - } - } -} - -/// Circuit version of `StarkOpeningSet`. -/// `Target`s for the purported values of each polynomial at the challenge point. -#[derive(Eq, PartialEq, Debug)] -pub(crate) struct StarkOpeningSetTarget { - /// `ExtensionTarget`s for the openings of trace polynomials at `zeta`. - pub local_values: Vec>, - /// `ExtensionTarget`s for the opening of trace polynomials at `g * zeta`. - pub next_values: Vec>, - /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `zeta`. - pub auxiliary_polys: Vec>, - /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `g * zeta`. - pub auxiliary_polys_next: Vec>, - /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1. - pub ctl_zs_first: Vec, - /// `ExtensionTarget`s for the opening of quotient polynomials at `zeta`. - pub quotient_polys: Vec>, -} - -impl StarkOpeningSetTarget { - /// Serializes a STARK's opening set. - pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - buffer.write_target_ext_vec(&self.local_values)?; - buffer.write_target_ext_vec(&self.next_values)?; - buffer.write_target_ext_vec(&self.auxiliary_polys)?; - buffer.write_target_ext_vec(&self.auxiliary_polys_next)?; - buffer.write_target_vec(&self.ctl_zs_first)?; - buffer.write_target_ext_vec(&self.quotient_polys)?; - Ok(()) - } - - /// Deserializes a STARK's opening set. - pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { - let local_values = buffer.read_target_ext_vec::()?; - let next_values = buffer.read_target_ext_vec::()?; - let auxiliary_polys = buffer.read_target_ext_vec::()?; - let auxiliary_polys_next = buffer.read_target_ext_vec::()?; - let ctl_zs_first = buffer.read_target_vec()?; - let quotient_polys = buffer.read_target_ext_vec::()?; - - Ok(Self { - local_values, - next_values, - auxiliary_polys, - auxiliary_polys_next, - ctl_zs_first, - quotient_polys, - }) - } - - /// Circuit version of `to_fri_openings`for `FriOpenings`. - /// Constructs the `Target`s the circuit version of FRI. - /// All openings but `ctl_zs_first` are grouped together. - pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget { - let zeta_batch = FriOpeningBatchTarget { - values: self - .local_values - .iter() - .chain(&self.auxiliary_polys) - .chain(&self.quotient_polys) - .copied() - .collect_vec(), - }; - let zeta_next_batch = FriOpeningBatchTarget { - values: self - .next_values - .iter() - .chain(&self.auxiliary_polys_next) - .copied() - .collect_vec(), - }; - debug_assert!(!self.ctl_zs_first.is_empty()); - let ctl_first_batch = FriOpeningBatchTarget { - values: self - .ctl_zs_first - .iter() - .copied() - .map(|t| t.to_ext_target(zero)) - .collect(), - }; - - FriOpeningsTarget { - batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], - } - } -} diff --git a/evm/src/prover.rs b/evm/src/prover.rs index f376b8cd28..8f11c112b1 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -1,44 +1,34 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use anyhow::{anyhow, ensure, Result}; +use anyhow::{anyhow, Result}; +use hashbrown::HashMap; use itertools::Itertools; use once_cell::sync::Lazy; use plonky2::field::extension::Extendable; -use plonky2::field::packable::Packable; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; -use plonky2::field::types::Field; -use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; +use plonky2::field::polynomial::PolynomialValues; use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::GenericConfig; use plonky2::timed; use plonky2::util::timing::TimingTree; -use plonky2::util::transpose; -use plonky2_maybe_rayon::*; -use plonky2_util::{log2_ceil, log2_strict}; +use starky::config::StarkConfig; +#[cfg(debug_assertions)] +use starky::cross_table_lookup::debug_utils::check_ctls; +use starky::cross_table_lookup::{get_ctl_data, CtlData}; +use starky::lookup::GrandProductChallengeSet; +use starky::proof::{MultiProof, StarkProofWithMetadata}; +use starky::prover::prove_with_commitment; +use starky::stark::Stark; use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::config::StarkConfig; -use crate::constraint_consumer::ConstraintConsumer; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cross_table_lookup::{ - cross_table_lookup_data, get_grand_product_challenge_set, CtlCheckVars, CtlData, - GrandProductChallengeSet, -}; -use crate::evaluation_frame::StarkEvaluationFrame; use crate::generation::{generate_traces, GenerationInputs}; use crate::get_challenges::observe_public_values; -use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; -use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; -use crate::stark::Stark; -use crate::vanishing_poly::eval_vanishing_poly; -#[cfg(test)] -use crate::{ - cross_table_lookup::testutils::check_ctls, verifier::testutils::get_memory_extra_looking_values, -}; +use crate::proof::{AllProof, PublicValues}; +#[cfg(debug_assertions)] +use crate::verifier::debug_utils::get_memory_extra_looking_values; /// Generate traces, then create all STARK proofs. pub fn prove( @@ -124,16 +114,15 @@ where observe_public_values::(&mut challenger, &public_values) .map_err(|_| anyhow::Error::msg("Invalid conversion of public values."))?; - // Get challenges for the cross-table lookups. - let ctl_challenges = get_grand_product_challenge_set(&mut challenger, config.num_challenges); // For each STARK, compute its cross-table lookup Z polynomials and get the associated `CtlData`. - let ctl_data_per_table = timed!( + let (ctl_challenges, ctl_data_per_table) = timed!( timing, "compute CTL data", - cross_table_lookup_data::( + get_ctl_data::( + config, &trace_poly_values, &all_stark.cross_table_lookups, - &ctl_challenges, + &mut challenger, all_stark.arithmetic_stark.constraint_degree() ) ); @@ -154,18 +143,26 @@ where )? ); - #[cfg(test)] + // This is an expensive check, hence is only run when `debug_assertions` are enabled. + #[cfg(debug_assertions)] { + let mut extra_values = HashMap::new(); + extra_values.insert( + *Table::Memory, + get_memory_extra_looking_values(&public_values), + ); check_ctls( &trace_poly_values, &all_stark.cross_table_lookups, - &get_memory_extra_looking_values(&public_values), + &extra_values, ); } Ok(AllProof { - stark_proofs, - ctl_challenges, + multi_proof: MultiProof { + stark_proofs, + ctl_challenges, + }, public_values, }) } @@ -331,371 +328,26 @@ where { check_abort_signal(abort_signal.clone())?; - let degree = trace_poly_values[0].len(); - let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits); - let rate_bits = config.fri_config.rate_bits; - let cap_height = config.fri_config.cap_height; - assert!( - fri_params.total_arities() <= degree_bits + rate_bits - cap_height, - "FRI total reduction arity is too large.", - ); - + // Clear buffered outputs. let init_challenger_state = challenger.compact(); - let constraint_degree = stark.constraint_degree(); - let lookup_challenges = stark.uses_lookups().then(|| { - ctl_challenges - .challenges - .iter() - .map(|ch| ch.beta) - .collect::>() - }); - let lookups = stark.lookups(); - let lookup_helper_columns = timed!( - timing, - "compute lookup helper columns", - lookup_challenges.as_ref().map(|challenges| { - let mut columns = Vec::new(); - for lookup in &lookups { - for &challenge in challenges { - columns.extend(lookup_helper_columns( - lookup, - trace_poly_values, - challenge, - constraint_degree, - )); - } - } - columns - }) - ); - let num_lookup_columns = lookup_helper_columns.as_ref().map(|v| v.len()).unwrap_or(0); - - // We add CTLs to the permutation arguments so that we can batch commit to - // all auxiliary polynomials. - let auxiliary_polys = match lookup_helper_columns { - None => { - let mut ctl_polys = ctl_data.ctl_helper_polys(); - ctl_polys.extend(ctl_data.ctl_z_polys()); - ctl_polys - } - Some(mut lookup_columns) => { - lookup_columns.extend(ctl_data.ctl_helper_polys()); - lookup_columns.extend(ctl_data.ctl_z_polys()); - lookup_columns - } - }; - assert!(!auxiliary_polys.is_empty(), "No CTL?"); - - // Get the polynomial commitments for all auxiliary polynomials. - let auxiliary_polys_commitment = timed!( - timing, - "compute auxiliary polynomials commitment", - PolynomialBatch::from_values( - auxiliary_polys, - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - ) - ); - - let auxiliary_polys_cap = auxiliary_polys_commitment.merkle_tree.cap.clone(); - challenger.observe_cap(&auxiliary_polys_cap); - - let alphas = challenger.get_n_challenges(config.num_challenges); - - let num_ctl_polys = ctl_data.num_ctl_helper_polys(); - - #[cfg(test)] - { - check_constraints( - stark, - trace_commitment, - &auxiliary_polys_commitment, - lookup_challenges.as_ref(), - &lookups, - ctl_data, - alphas.clone(), - degree_bits, - num_lookup_columns, - &num_ctl_polys, - ); - } - - check_abort_signal(abort_signal.clone())?; - - let quotient_polys = timed!( - timing, - "compute quotient polys", - compute_quotient_polys::::Packing, C, S, D>( - stark, - trace_commitment, - &auxiliary_polys_commitment, - lookup_challenges.as_ref(), - &lookups, - ctl_data, - alphas, - degree_bits, - num_lookup_columns, - &num_ctl_polys, - config, - ) - ); - let all_quotient_chunks = timed!( - timing, - "split quotient polys", - quotient_polys - .into_par_iter() - .flat_map(|mut quotient_poly| { - quotient_poly - .trim_to_len(degree * stark.quotient_degree_factor()) - .expect( - "Quotient has failed, the vanishing polynomial is not divisible by Z_H", - ); - // Split quotient into degree-n chunks. - quotient_poly.chunks(degree) - }) - .collect() - ); - // Commit to the quotient polynomials. - let quotient_commitment = timed!( - timing, - "compute quotient commitment", - PolynomialBatch::from_coeffs( - all_quotient_chunks, - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - ) - ); - // Observe the quotient polynomials Merkle cap. - let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); - challenger.observe_cap("ient_polys_cap); - - let zeta = challenger.get_extension_challenge::(); - // To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and - // `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since - // `(g * zeta)^n = zeta^n`, where `n` is the order of `g`. - let g = F::primitive_root_of_unity(degree_bits); - ensure!( - zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, - "Opening point is in the subgroup." - ); - - // Compute all openings: evaluate all committed polynomials at `zeta` and, when necessary, at `g * zeta`. - let openings = StarkOpeningSet::new( - zeta, - g, - trace_commitment, - &auxiliary_polys_commitment, - "ient_commitment, - stark.num_lookup_helper_columns(config), - &num_ctl_polys, - ); - // Get the FRI openings and observe them. - challenger.observe_openings(&openings.to_fri_openings()); - - let initial_merkle_trees = vec![ + prove_with_commitment( + stark, + config, + trace_poly_values, trace_commitment, - &auxiliary_polys_commitment, - "ient_commitment, - ]; - - check_abort_signal(abort_signal.clone())?; - - let opening_proof = timed!( + Some(ctl_data), + Some(ctl_challenges), + challenger, + &[], timing, - "compute openings proof", - PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, num_ctl_polys.iter().sum(), num_ctl_polys, config), - &initial_merkle_trees, - challenger, - &fri_params, - timing, - ) - ); - - let proof = StarkProof { - trace_cap: trace_commitment.merkle_tree.cap.clone(), - auxiliary_polys_cap, - quotient_polys_cap, - openings, - opening_proof, - }; - Ok(StarkProofWithMetadata { + ) + .map(|proof_with_pis| StarkProofWithMetadata { + proof: proof_with_pis.proof, init_challenger_state, - proof, }) } -/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, -/// where the `C_i`s are the Stark constraints. -fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( - stark: &S, - trace_commitment: &'a PolynomialBatch, - auxiliary_polys_commitment: &'a PolynomialBatch, - lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], - ctl_data: &CtlData, - alphas: Vec, - degree_bits: usize, - num_lookup_columns: usize, - num_ctl_columns: &[usize], - config: &StarkConfig, -) -> Vec> -where - F: RichField + Extendable, - P: PackedField, - C: GenericConfig, - S: Stark, -{ - let degree = 1 << degree_bits; - let rate_bits = config.fri_config.rate_bits; - let total_num_helper_cols: usize = num_ctl_columns.iter().sum(); - - let quotient_degree_bits = log2_ceil(stark.quotient_degree_factor()); - assert!( - quotient_degree_bits <= rate_bits, - "Having constraints of degree higher than the rate is not supported yet." - ); - let step = 1 << (rate_bits - quotient_degree_bits); - // When opening the `Z`s polys at the "next" point, need to look at the point `next_step` steps away. - let next_step = 1 << quotient_degree_bits; - - // Evaluation of the first Lagrange polynomial on the LDE domain. - let lagrange_first = PolynomialValues::selector(degree, 0).lde_onto_coset(quotient_degree_bits); - // Evaluation of the last Lagrange polynomial on the LDE domain. - let lagrange_last = - PolynomialValues::selector(degree, degree - 1).lde_onto_coset(quotient_degree_bits); - - let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); - - // Retrieve the LDE values at index `i`. - let get_trace_values_packed = - |i_start| -> Vec

{ trace_commitment.get_lde_values_packed(i_start, step) }; - - // Last element of the subgroup. - let last = F::primitive_root_of_unity(degree_bits).inverse(); - let size = degree << quotient_degree_bits; - let coset = F::cyclic_subgroup_coset_known_order( - F::primitive_root_of_unity(degree_bits + quotient_degree_bits), - F::coset_shift(), - size, - ); - - // We will step by `P::WIDTH`, and in each iteration, evaluate the quotient polynomial at - // a batch of `P::WIDTH` points. - let quotient_values = (0..size) - .into_par_iter() - .step_by(P::WIDTH) - .flat_map_iter(|i_start| { - let i_next_start = (i_start + next_step) % size; - let i_range = i_start..i_start + P::WIDTH; - - let x = *P::from_slice(&coset[i_range.clone()]); - let z_last = x - last; - let lagrange_basis_first = *P::from_slice(&lagrange_first.values[i_range.clone()]); - let lagrange_basis_last = *P::from_slice(&lagrange_last.values[i_range]); - - let mut consumer = ConstraintConsumer::new( - alphas.clone(), - z_last, - lagrange_basis_first, - lagrange_basis_last, - ); - // Get the local and next row evaluations for the current STARK. - let vars = S::EvaluationFrame::from_values( - &get_trace_values_packed(i_start), - &get_trace_values_packed(i_next_start), - ); - // Get the local and next row evaluations for the permutation argument, as well as the associated challenges. - let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars { - local_values: auxiliary_polys_commitment.get_lde_values_packed(i_start, step) - [..num_lookup_columns] - .to_vec(), - next_values: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step) - [..num_lookup_columns] - .to_vec(), - challenges: challenges.to_vec(), - }); - - // Get all the data for this STARK's CTLs: - // - the local and next row evaluations for the CTL Z polynomials - // - the associated challenges. - // - for each CTL: - // - the filter `Column` - // - the `Column`s that form the looking/looked table. - - let mut start_index = 0; - let ctl_vars = ctl_data - .zs_columns - .iter() - .enumerate() - .map(|(i, zs_columns)| { - let num_ctl_helper_cols = num_ctl_columns[i]; - let helper_columns = auxiliary_polys_commitment - .get_lde_values_packed(i_start, step)[num_lookup_columns - + start_index - ..num_lookup_columns + start_index + num_ctl_helper_cols] - .to_vec(); - - let ctl_vars = CtlCheckVars:: { - helper_columns, - local_z: auxiliary_polys_commitment.get_lde_values_packed(i_start, step) - [num_lookup_columns + total_num_helper_cols + i], - next_z: auxiliary_polys_commitment - .get_lde_values_packed(i_next_start, step) - [num_lookup_columns + total_num_helper_cols + i], - challenges: zs_columns.challenge, - columns: zs_columns.columns.clone(), - filter: zs_columns.filter.clone(), - }; - - start_index += num_ctl_helper_cols; - - ctl_vars - }) - .collect::>(); - - // Evaluate the polynomial combining all constraints, including those associated - // to the permutation and CTL arguments. - eval_vanishing_poly::( - stark, - &vars, - lookups, - lookup_vars, - &ctl_vars, - &mut consumer, - ); - let mut constraints_evals = consumer.accumulators(); - // We divide the constraints evaluations by `Z_H(x)`. - let denominator_inv: P = z_h_on_coset.eval_inverse_packed(i_start); - for eval in &mut constraints_evals { - *eval *= denominator_inv; - } - - let num_challenges = alphas.len(); - - (0..P::WIDTH).map(move |i| { - (0..num_challenges) - .map(|j| constraints_evals[j].as_slice()[i]) - .collect() - }) - }) - .collect::>(); - - transpose("ient_values) - .into_par_iter() - .map(PolynomialValues::new) - .map(|values| values.coset_ifft(F::coset_shift())) - .collect() -} - /// Utility method that checks whether a kill signal has been emitted by one of the workers, /// which will result in an early abort for all the other processes involved in the same set /// of transactions. @@ -708,134 +360,3 @@ pub fn check_abort_signal(abort_signal: Option>) -> Result<()> { Ok(()) } - -#[cfg(test)] -/// Check that all constraints evaluate to zero on `H`. -/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. -fn check_constraints<'a, F, C, S, const D: usize>( - stark: &S, - trace_commitment: &'a PolynomialBatch, - auxiliary_commitment: &'a PolynomialBatch, - lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], - ctl_data: &CtlData, - alphas: Vec, - degree_bits: usize, - num_lookup_columns: usize, - num_ctl_helper_cols: &[usize], -) where - F: RichField + Extendable, - C: GenericConfig, - S: Stark, -{ - let degree = 1 << degree_bits; - let rate_bits = 0; // Set this to higher value to check constraint degree. - - let total_num_helper_cols: usize = num_ctl_helper_cols.iter().sum(); - - let size = degree << rate_bits; - let step = 1 << rate_bits; - - // Evaluation of the first Lagrange polynomial. - let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits); - // Evaluation of the last Lagrange polynomial. - let lagrange_last = PolynomialValues::selector(degree, degree - 1).lde(rate_bits); - - let subgroup = F::two_adic_subgroup(degree_bits + rate_bits); - - // Get the evaluations of a batch of polynomials over our subgroup. - let get_subgroup_evals = |comm: &PolynomialBatch| -> Vec> { - let values = comm - .polynomials - .par_iter() - .map(|coeffs| coeffs.clone().fft().values) - .collect::>(); - transpose(&values) - }; - - // Get batch evaluations of the trace, permutation and CTL polynomials over our subgroup. - let trace_subgroup_evals = get_subgroup_evals(trace_commitment); - let auxiliary_subgroup_evals = get_subgroup_evals(auxiliary_commitment); - - // Last element of the subgroup. - let last = F::primitive_root_of_unity(degree_bits).inverse(); - - let constraint_values = (0..size) - .map(|i| { - let i_next = (i + step) % size; - - let x = subgroup[i]; - let z_last = x - last; - let lagrange_basis_first = lagrange_first.values[i]; - let lagrange_basis_last = lagrange_last.values[i]; - - let mut consumer = ConstraintConsumer::new( - alphas.clone(), - z_last, - lagrange_basis_first, - lagrange_basis_last, - ); - // Get the local and next row evaluations for the current STARK's trace. - let vars = S::EvaluationFrame::from_values( - &trace_subgroup_evals[i], - &trace_subgroup_evals[i_next], - ); - // Get the local and next row evaluations for the current STARK's permutation argument. - let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars { - local_values: auxiliary_subgroup_evals[i][..num_lookup_columns].to_vec(), - next_values: auxiliary_subgroup_evals[i_next][..num_lookup_columns].to_vec(), - challenges: challenges.to_vec(), - }); - - // Get the local and next row evaluations for the current STARK's CTL Z polynomials. - let mut start_index = 0; - let ctl_vars = ctl_data - .zs_columns - .iter() - .enumerate() - .map(|(iii, zs_columns)| { - let num_helper_cols = num_ctl_helper_cols[iii]; - let helper_columns = auxiliary_subgroup_evals[i][num_lookup_columns - + start_index - ..num_lookup_columns + start_index + num_helper_cols] - .to_vec(); - let ctl_vars = CtlCheckVars:: { - helper_columns, - local_z: auxiliary_subgroup_evals[i] - [num_lookup_columns + total_num_helper_cols + iii], - next_z: auxiliary_subgroup_evals[i_next] - [num_lookup_columns + total_num_helper_cols + iii], - challenges: zs_columns.challenge, - columns: zs_columns.columns.clone(), - filter: zs_columns.filter.clone(), - }; - - start_index += num_helper_cols; - - ctl_vars - }) - .collect::>(); - - // Evaluate the polynomial combining all constraints, including those associated - // to the permutation and CTL arguments. - eval_vanishing_poly::( - stark, - &vars, - lookups, - lookup_vars, - &ctl_vars, - &mut consumer, - ); - consumer.accumulators() - }) - .collect::>(); - - // Assert that all constraints evaluate to 0 over our subgroup. - for v in constraint_values { - assert!( - v.iter().all(|x| x.is_zero()), - "Constraint failed in {}", - std::any::type_name::() - ); - } -} diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 5220ba32a7..f3a8e1db4a 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -4,47 +4,41 @@ use core::fmt::Debug; use anyhow::Result; use ethereum_types::{BigEndianHash, U256}; use plonky2::field::extension::Extendable; -use plonky2::field::types::Field; -use plonky2::fri::witness_util::set_fri_proof_target; use plonky2::gates::exponentiation::ExponentiationGate; use plonky2::gates::gate::GateRef; use plonky2::gates::noop::NoopGate; use plonky2::hash::hash_types::RichField; use plonky2::hash::hashing::PlonkyPermutation; use plonky2::iop::challenger::RecursiveChallenger; -use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::iop::witness::{PartialWitness, Witness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; -use plonky2::util::reducing::ReducingFactorTarget; use plonky2::util::serialization::{ Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, }; -use plonky2::with_context; use plonky2_util::log2_ceil; +use starky::config::StarkConfig; +use starky::cross_table_lookup::{CrossTableLookup, CtlCheckVarsTarget}; +use starky::lookup::{GrandProductChallenge, GrandProductChallengeSet}; +use starky::proof::{StarkProofTarget, StarkProofWithMetadata}; +use starky::recursive_verifier::{ + add_virtual_stark_proof, set_stark_proof_target, verify_stark_proof_with_challenges_circuit, +}; +use starky::stark::Stark; use crate::all_stark::Table; -use crate::config::StarkConfig; -use crate::constraint_consumer::RecursiveConstraintConsumer; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cross_table_lookup::{CrossTableLookup, CtlCheckVarsTarget, GrandProductChallengeSet}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::lookup::{GrandProductChallenge, LookupCheckVarsTarget}; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::proof::{ BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, - ExtraBlockDataTarget, PublicValues, PublicValuesTarget, StarkOpeningSetTarget, StarkProof, - StarkProofChallengesTarget, StarkProofTarget, StarkProofWithMetadata, TrieRoots, - TrieRootsTarget, + ExtraBlockDataTarget, PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, }; -use crate::stark::Stark; use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; -use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::witness::errors::ProgramError; pub(crate) struct PublicInputs> @@ -205,7 +199,7 @@ where } } -/// Returns the recursive Stark circuit. +/// Returns the recursive STARK circuit. pub(crate) fn recursive_stark_circuit< F: RichField + Extendable, C: GenericConfig, @@ -236,7 +230,7 @@ where ); let num_ctl_helper_zs = num_ctl_zs + total_num_helpers; - let proof_target = add_virtual_stark_proof( + let stark_proof_target = add_virtual_stark_proof( &mut builder, stark, inner_config, @@ -246,7 +240,7 @@ where ); builder.register_public_inputs( - &proof_target + &stark_proof_target .trace_cap .0 .iter() @@ -265,7 +259,7 @@ where let ctl_vars = CtlCheckVarsTarget::from_proof( *table, - &proof_target, + &stark_proof_target, cross_table_lookups, &ctl_challenges_target, num_lookup_columns, @@ -279,20 +273,25 @@ where })); let mut challenger = RecursiveChallenger::::from_state(init_challenger_state_target); - let challenges = - proof_target.get_challenges::(&mut builder, &mut challenger, inner_config); + let challenges = stark_proof_target.get_challenges::( + &mut builder, + &mut challenger, + Some(&ctl_challenges_target), + true, + inner_config, + ); let challenger_state = challenger.compact(&mut builder); builder.register_public_inputs(challenger_state.as_ref()); - builder.register_public_inputs(&proof_target.openings.ctl_zs_first); + builder.register_public_inputs(stark_proof_target.openings.ctl_zs_first.as_ref().unwrap()); verify_stark_proof_with_challenges_circuit::( &mut builder, stark, - &proof_target, - &challenges, - &ctl_vars, - &ctl_challenges_target, + &stark_proof_target, + &[], // public inputs + challenges, + Some(&ctl_vars), inner_config, ); @@ -306,7 +305,7 @@ where let circuit = builder.build::(); StarkWrapperCircuit { circuit, - stark_proof_target: proof_target, + stark_proof_target, ctl_challenges_target, init_challenger_state_target, zero_target, @@ -324,122 +323,6 @@ pub(crate) fn add_common_recursion_gates, const D: ))); } -/// Recursively verifies an inner proof. -fn verify_stark_proof_with_challenges_circuit< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - builder: &mut CircuitBuilder, - stark: &S, - proof: &StarkProofTarget, - challenges: &StarkProofChallengesTarget, - ctl_vars: &[CtlCheckVarsTarget], - ctl_challenges: &GrandProductChallengeSet, - inner_config: &StarkConfig, -) where - C::Hasher: AlgebraicHasher, -{ - let zero = builder.zero(); - let one = builder.one_extension(); - - let num_ctl_polys = ctl_vars - .iter() - .map(|ctl| ctl.helper_columns.len()) - .sum::(); - - let StarkOpeningSetTarget { - local_values, - next_values, - auxiliary_polys, - auxiliary_polys_next, - ctl_zs_first, - quotient_polys, - } = &proof.openings; - let vars = S::EvaluationFrameTarget::from_values(local_values, next_values); - - let degree_bits = proof.recover_degree_bits(inner_config); - let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); - let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); - let (l_0, l_last) = - eval_l_0_and_l_last_circuit(builder, degree_bits, challenges.stark_zeta, z_h_zeta); - let last = - builder.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()); - let z_last = builder.sub_extension(challenges.stark_zeta, last); - - let mut consumer = RecursiveConstraintConsumer::::new( - builder.zero_extension(), - challenges.stark_alphas.clone(), - z_last, - l_0, - l_last, - ); - - let num_lookup_columns = stark.num_lookup_helper_columns(inner_config); - let lookup_challenges = (num_lookup_columns > 0).then(|| { - ctl_challenges - .challenges - .iter() - .map(|ch| ch.beta) - .collect::>() - }); - - let lookup_vars = stark.uses_lookups().then(|| LookupCheckVarsTarget { - local_values: auxiliary_polys[..num_lookup_columns].to_vec(), - next_values: auxiliary_polys_next[..num_lookup_columns].to_vec(), - challenges: lookup_challenges.unwrap(), - }); - - with_context!( - builder, - "evaluate vanishing polynomial", - eval_vanishing_poly_circuit::( - builder, - stark, - &vars, - lookup_vars, - ctl_vars, - &mut consumer, - ) - ); - let vanishing_polys_zeta = consumer.accumulators(); - - // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. - let mut scale = ReducingFactorTarget::new(zeta_pow_deg); - for (i, chunk) in quotient_polys - .chunks(stark.quotient_degree_factor()) - .enumerate() - { - let recombined_quotient = scale.reduce(chunk, builder); - let computed_vanishing_poly = builder.mul_extension(z_h_zeta, recombined_quotient); - builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly); - } - - let merkle_caps = vec![ - proof.trace_cap.clone(), - proof.auxiliary_polys_cap.clone(), - proof.quotient_polys_cap.clone(), - ]; - - let fri_instance = stark.fri_instance_target( - builder, - challenges.stark_zeta, - F::primitive_root_of_unity(degree_bits), - num_ctl_polys, - ctl_zs_first.len(), - inner_config, - ); - builder.verify_fri_proof::( - &fri_instance, - &proof.openings.to_fri_openings(zero), - &challenges.fri_challenges, - &merkle_caps, - &proof.opening_proof, - &inner_config.fri_params(degree_bits), - ); -} - /// Recursive version of `get_memory_extra_looking_sum`. pub(crate) fn get_memory_extra_looking_sum_circuit, const D: usize>( builder: &mut CircuitBuilder, @@ -667,25 +550,6 @@ fn add_data_write, const D: usize>( builder.add(running_sum, inverse) } -fn eval_l_0_and_l_last_circuit, const D: usize>( - builder: &mut CircuitBuilder, - log_n: usize, - x: ExtensionTarget, - z_x: ExtensionTarget, -) -> (ExtensionTarget, ExtensionTarget) { - let n = builder.constant_extension(F::Extension::from_canonical_usize(1 << log_n)); - let g = builder.constant_extension(F::Extension::primitive_root_of_unity(log_n)); - let one = builder.one_extension(); - let l_0_deno = builder.mul_sub_extension(n, x, n); - let l_last_deno = builder.mul_sub_extension(g, x, one); - let l_last_deno = builder.mul_extension(n, l_last_deno); - - ( - builder.div_extension(z_x, l_0_deno), - builder.div_extension(z_x, l_last_deno), - ) -} - pub(crate) fn add_virtual_public_values, const D: usize>( builder: &mut CircuitBuilder, ) -> PublicValuesTarget { @@ -770,93 +634,6 @@ pub(crate) fn add_virtual_extra_block_data, const D } } -pub(crate) fn add_virtual_stark_proof< - F: RichField + Extendable, - S: Stark, - const D: usize, ->( - builder: &mut CircuitBuilder, - stark: &S, - config: &StarkConfig, - degree_bits: usize, - num_ctl_helper_zs: usize, - num_ctl_zs: usize, -) -> StarkProofTarget { - let fri_params = config.fri_params(degree_bits); - let cap_height = fri_params.config.cap_height; - - let num_leaves_per_oracle = vec![ - S::COLUMNS, - stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, - stark.quotient_degree_factor() * config.num_challenges, - ]; - - let auxiliary_polys_cap = builder.add_virtual_cap(cap_height); - - StarkProofTarget { - trace_cap: builder.add_virtual_cap(cap_height), - auxiliary_polys_cap, - quotient_polys_cap: builder.add_virtual_cap(cap_height), - openings: add_virtual_stark_opening_set::( - builder, - stark, - num_ctl_helper_zs, - num_ctl_zs, - config, - ), - opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), - } -} - -fn add_virtual_stark_opening_set, S: Stark, const D: usize>( - builder: &mut CircuitBuilder, - stark: &S, - num_ctl_helper_zs: usize, - num_ctl_zs: usize, - config: &StarkConfig, -) -> StarkOpeningSetTarget { - let num_challenges = config.num_challenges; - StarkOpeningSetTarget { - local_values: builder.add_virtual_extension_targets(S::COLUMNS), - next_values: builder.add_virtual_extension_targets(S::COLUMNS), - auxiliary_polys: builder.add_virtual_extension_targets( - stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, - ), - auxiliary_polys_next: builder.add_virtual_extension_targets( - stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, - ), - ctl_zs_first: builder.add_virtual_targets(num_ctl_zs), - quotient_polys: builder - .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), - } -} - -pub(crate) fn set_stark_proof_target, W, const D: usize>( - witness: &mut W, - proof_target: &StarkProofTarget, - proof: &StarkProof, - zero: Target, -) where - F: RichField + Extendable, - C::Hasher: AlgebraicHasher, - W: Witness, -{ - witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap); - witness.set_cap_target(&proof_target.quotient_polys_cap, &proof.quotient_polys_cap); - - witness.set_fri_openings( - &proof_target.openings.to_fri_openings(zero), - &proof.openings.to_fri_openings(), - ); - - witness.set_cap_target( - &proof_target.auxiliary_polys_cap, - &proof.auxiliary_polys_cap, - ); - - set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof); -} - pub fn set_public_value_targets( witness: &mut W, public_values_target: &PublicValuesTarget, diff --git a/evm/src/stark.rs b/evm/src/stark.rs deleted file mode 100644 index 5ff578f9fc..0000000000 --- a/evm/src/stark.rs +++ /dev/null @@ -1,228 +0,0 @@ -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::fri::structure::{ - FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo, - FriPolynomialInfo, -}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -use crate::config::StarkConfig; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::lookup::Lookup; - -const TRACE_ORACLE_INDEX: usize = 0; -const AUXILIARY_ORACLE_INDEX: usize = 1; -const QUOTIENT_ORACLE_INDEX: usize = 2; - -/// Represents a STARK system. -pub trait Stark, const D: usize>: Sync { - /// The total number of columns in the trace. - const COLUMNS: usize = Self::EvaluationFrameTarget::COLUMNS; - - /// This is used to evaluate constraints natively. - type EvaluationFrame: StarkEvaluationFrame

- where - FE: FieldExtension, - P: PackedField; - - /// The `Target` version of `Self::EvaluationFrame`, used to evaluate constraints recursively. - type EvaluationFrameTarget: StarkEvaluationFrame>; - - /// Evaluate constraints at a vector of points. - /// - /// The points are elements of a field `FE`, a degree `D2` extension of `F`. This lets us - /// evaluate constraints over a larger domain if desired. This can also be called with `FE = F` - /// and `D2 = 1`, in which case we are using the trivial extension, i.e. just evaluating - /// constraints over `F`. - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField; - - /// Evaluate constraints at a vector of points from the base field `F`. - fn eval_packed_base>( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) { - self.eval_packed_generic(vars, yield_constr) - } - - /// Evaluate constraints at a single point from the degree `D` extension field. - fn eval_ext( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer, - ) { - self.eval_packed_generic(vars, yield_constr) - } - - /// Evaluate constraints at a vector of points from the degree `D` extension field. This is like - /// `eval_ext`, except in the context of a recursive circuit. - /// Note: constraints must be added through`yield_constr.constraint(builder, constraint)` in the - /// same order as they are given in `eval_packed_generic`. - fn eval_ext_circuit( - &self, - builder: &mut CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ); - - /// The maximum constraint degree. - fn constraint_degree(&self) -> usize; - - /// The maximum constraint degree. - fn quotient_degree_factor(&self) -> usize { - 1.max(self.constraint_degree() - 1) - } - - fn num_quotient_polys(&self, config: &StarkConfig) -> usize { - self.quotient_degree_factor() * config.num_challenges - } - - /// Computes the FRI instance used to prove this Stark. - fn fri_instance( - &self, - zeta: F::Extension, - g: F, - num_ctl_helpers: usize, - num_ctl_zs: Vec, - config: &StarkConfig, - ) -> FriInstanceInfo { - let trace_oracle = FriOracleInfo { - num_polys: Self::COLUMNS, - blinding: false, - }; - let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); - - let num_lookup_columns = self.num_lookup_helper_columns(config); - let num_auxiliary_polys = num_lookup_columns + num_ctl_helpers + num_ctl_zs.len(); - let auxiliary_oracle = FriOracleInfo { - num_polys: num_auxiliary_polys, - blinding: false, - }; - let auxiliary_polys_info = - FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); - - let ctl_zs_info = FriPolynomialInfo::from_range( - AUXILIARY_ORACLE_INDEX, - num_lookup_columns + num_ctl_helpers..num_auxiliary_polys, - ); - - let num_quotient_polys = self.num_quotient_polys(config); - let quotient_oracle = FriOracleInfo { - num_polys: num_quotient_polys, - blinding: false, - }; - let quotient_info = - FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); - - let zeta_batch = FriBatchInfo { - point: zeta, - polynomials: [ - trace_info.clone(), - auxiliary_polys_info.clone(), - quotient_info, - ] - .concat(), - }; - let zeta_next_batch = FriBatchInfo { - point: zeta.scalar_mul(g), - polynomials: [trace_info, auxiliary_polys_info].concat(), - }; - let ctl_first_batch = FriBatchInfo { - point: F::Extension::ONE, - polynomials: ctl_zs_info, - }; - FriInstanceInfo { - oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], - } - } - - /// Computes the FRI instance used to prove this Stark. - fn fri_instance_target( - &self, - builder: &mut CircuitBuilder, - zeta: ExtensionTarget, - g: F, - num_ctl_helper_polys: usize, - num_ctl_zs: usize, - inner_config: &StarkConfig, - ) -> FriInstanceInfoTarget { - let trace_oracle = FriOracleInfo { - num_polys: Self::COLUMNS, - blinding: false, - }; - let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); - - let num_lookup_columns = self.num_lookup_helper_columns(inner_config); - let num_auxiliary_polys = num_lookup_columns + num_ctl_helper_polys + num_ctl_zs; - let auxiliary_oracle = FriOracleInfo { - num_polys: num_auxiliary_polys, - blinding: false, - }; - let auxiliary_polys_info = - FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); - - let ctl_zs_info = FriPolynomialInfo::from_range( - AUXILIARY_ORACLE_INDEX, - num_lookup_columns + num_ctl_helper_polys - ..num_lookup_columns + num_ctl_helper_polys + num_ctl_zs, - ); - - let num_quotient_polys = self.num_quotient_polys(inner_config); - let quotient_oracle = FriOracleInfo { - num_polys: num_quotient_polys, - blinding: false, - }; - let quotient_info = - FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); - - let zeta_batch = FriBatchInfoTarget { - point: zeta, - polynomials: [ - trace_info.clone(), - auxiliary_polys_info.clone(), - quotient_info, - ] - .concat(), - }; - let zeta_next = builder.mul_const_extension(g, zeta); - let zeta_next_batch = FriBatchInfoTarget { - point: zeta_next, - polynomials: [trace_info, auxiliary_polys_info].concat(), - }; - let ctl_first_batch = FriBatchInfoTarget { - point: builder.one_extension(), - polynomials: ctl_zs_info, - }; - FriInstanceInfoTarget { - oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], - } - } - - fn lookups(&self) -> Vec> { - vec![] - } - - fn num_lookup_helper_columns(&self, config: &StarkConfig) -> usize { - self.lookups() - .iter() - .map(|lookup| lookup.num_helper_columns(self.constraint_degree())) - .sum::() - * config.num_challenges - } - - fn uses_lookups(&self) -> bool { - !self.lookups().is_empty() - } -} diff --git a/evm/src/stark_testing.rs b/evm/src/stark_testing.rs deleted file mode 100644 index 3568f00433..0000000000 --- a/evm/src/stark_testing.rs +++ /dev/null @@ -1,157 +0,0 @@ -use anyhow::{ensure, Result}; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; -use plonky2::field::types::{Field, Sample}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::witness::{PartialWitness, WitnessWrite}; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::circuit_data::CircuitConfig; -use plonky2::plonk::config::GenericConfig; -use plonky2::util::transpose; -use plonky2_util::{log2_ceil, log2_strict}; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::stark::Stark; - -const WITNESS_SIZE: usize = 1 << 5; - -/// Tests that the constraints imposed by the given STARK are low-degree by applying them to random -/// low-degree witness polynomials. -pub(crate) fn test_stark_low_degree< - F: RichField + Extendable, - S: Stark, - const D: usize, ->( - stark: S, -) -> Result<()> { - let rate_bits = log2_ceil(stark.constraint_degree() + 1); - - let trace_ldes = random_low_degree_matrix::(S::COLUMNS, rate_bits); - let size = trace_ldes.len(); - - let lagrange_first = PolynomialValues::selector(WITNESS_SIZE, 0).lde(rate_bits); - let lagrange_last = PolynomialValues::selector(WITNESS_SIZE, WITNESS_SIZE - 1).lde(rate_bits); - - let last = F::primitive_root_of_unity(log2_strict(WITNESS_SIZE)).inverse(); - let subgroup = - F::cyclic_subgroup_known_order(F::primitive_root_of_unity(log2_strict(size)), size); - let alpha = F::rand(); - let constraint_evals = (0..size) - .map(|i| { - let vars = S::EvaluationFrame::from_values( - &trace_ldes[i], - &trace_ldes[(i + (1 << rate_bits)) % size], - ); - - let mut consumer = ConstraintConsumer::::new( - vec![alpha], - subgroup[i] - last, - lagrange_first.values[i], - lagrange_last.values[i], - ); - stark.eval_packed_base(&vars, &mut consumer); - consumer.accumulators()[0] - }) - .collect::>(); - - let constraint_poly_values = PolynomialValues::new(constraint_evals); - if !constraint_poly_values.is_zero() { - let constraint_eval_degree = constraint_poly_values.degree(); - let maximum_degree = WITNESS_SIZE * stark.constraint_degree() - 1; - - ensure!( - constraint_eval_degree <= maximum_degree, - "Expected degrees at most {} * {} - 1 = {}, actual {:?}", - WITNESS_SIZE, - stark.constraint_degree(), - maximum_degree, - constraint_eval_degree - ); - } - - Ok(()) -} - -/// Tests that the circuit constraints imposed by the given STARK are coherent with the native constraints. -pub(crate) fn test_stark_circuit_constraints< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - stark: S, -) -> Result<()> { - // Compute native constraint evaluation on random values. - let vars = S::EvaluationFrame::from_values( - &F::Extension::rand_vec(S::COLUMNS), - &F::Extension::rand_vec(S::COLUMNS), - ); - - let alphas = F::rand_vec(1); - let z_last = F::Extension::rand(); - let lagrange_first = F::Extension::rand(); - let lagrange_last = F::Extension::rand(); - let mut consumer = ConstraintConsumer::::new( - alphas - .iter() - .copied() - .map(F::Extension::from_basefield) - .collect(), - z_last, - lagrange_first, - lagrange_last, - ); - stark.eval_ext(&vars, &mut consumer); - let native_eval = consumer.accumulators()[0]; - - // Compute circuit constraint evaluation on same random values. - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::::new(); - - let locals_t = builder.add_virtual_extension_targets(S::COLUMNS); - pw.set_extension_targets(&locals_t, vars.get_local_values()); - let nexts_t = builder.add_virtual_extension_targets(S::COLUMNS); - pw.set_extension_targets(&nexts_t, vars.get_next_values()); - let alphas_t = builder.add_virtual_targets(1); - pw.set_target(alphas_t[0], alphas[0]); - let z_last_t = builder.add_virtual_extension_target(); - pw.set_extension_target(z_last_t, z_last); - let lagrange_first_t = builder.add_virtual_extension_target(); - pw.set_extension_target(lagrange_first_t, lagrange_first); - let lagrange_last_t = builder.add_virtual_extension_target(); - pw.set_extension_target(lagrange_last_t, lagrange_last); - - let vars = S::EvaluationFrameTarget::from_values(&locals_t, &nexts_t); - let mut consumer = RecursiveConstraintConsumer::::new( - builder.zero_extension(), - alphas_t, - z_last_t, - lagrange_first_t, - lagrange_last_t, - ); - stark.eval_ext_circuit(&mut builder, &vars, &mut consumer); - let circuit_eval = consumer.accumulators()[0]; - let native_eval_t = builder.constant_extension(native_eval); - builder.connect_extension(circuit_eval, native_eval_t); - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) -} - -fn random_low_degree_matrix(num_polys: usize, rate_bits: usize) -> Vec> { - let polys = (0..num_polys) - .map(|_| random_low_degree_values(rate_bits)) - .collect::>(); - - transpose(&polys) -} - -fn random_low_degree_values(rate_bits: usize) -> Vec { - PolynomialCoeffs::new(F::rand_vec(WITNESS_SIZE)) - .lde(rate_bits) - .fft() - .values -} diff --git a/evm/src/util.rs b/evm/src/util.rs index aec2e63e17..fdb5a98c3a 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -35,18 +35,6 @@ pub(crate) fn limb_from_bits_le_recursive, const D: }) } -/// A helper function to transpose a row-wise trace and put it in the format that `prove` expects. -pub(crate) fn trace_rows_to_poly_values( - trace_rows: Vec<[F; COLUMNS]>, -) -> Vec> { - let trace_row_vecs = trace_rows.into_iter().map(|row| row.to_vec()).collect_vec(); - let trace_col_vecs: Vec> = transpose(&trace_row_vecs); - trace_col_vecs - .into_iter() - .map(|column| PolynomialValues::new(column)) - .collect() -} - /// Returns the lowest LE 32-bit limb of a `U256` as a field element, /// and errors if the integer is actually greater. pub(crate) fn u256_to_u32(u256: U256) -> Result { diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs deleted file mode 100644 index c1f2d0f92b..0000000000 --- a/evm/src/vanishing_poly.rs +++ /dev/null @@ -1,81 +0,0 @@ -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{ - eval_cross_table_lookup_checks, eval_cross_table_lookup_checks_circuit, CtlCheckVars, - CtlCheckVarsTarget, -}; -use crate::lookup::{ - eval_ext_lookups_circuit, eval_packed_lookups_generic, Lookup, LookupCheckVars, - LookupCheckVarsTarget, -}; -use crate::stark::Stark; - -/// Evaluates all constraint, permutation and cross-table lookup polynomials -/// of the current STARK at the local and next values. -pub(crate) fn eval_vanishing_poly( - stark: &S, - vars: &S::EvaluationFrame, - lookups: &[Lookup], - lookup_vars: Option>, - ctl_vars: &[CtlCheckVars], - consumer: &mut ConstraintConsumer

, -) where - F: RichField + Extendable, - FE: FieldExtension, - P: PackedField, - S: Stark, -{ - // Evaluate all of the STARK's table constraints. - stark.eval_packed_generic(vars, consumer); - if let Some(lookup_vars) = lookup_vars { - // Evaluate the STARK constraints related to the permutation arguments. - eval_packed_lookups_generic::( - stark, - lookups, - vars, - lookup_vars, - consumer, - ); - } - // Evaluate the STARK constraints related to the cross-table lookups. - eval_cross_table_lookup_checks::( - vars, - ctl_vars, - consumer, - stark.constraint_degree(), - ); -} - -/// Circuit version of `eval_vanishing_poly`. -/// Evaluates all constraint, permutation and cross-table lookup polynomials -/// of the current STARK at the local and next values. -pub(crate) fn eval_vanishing_poly_circuit( - builder: &mut CircuitBuilder, - stark: &S, - vars: &S::EvaluationFrameTarget, - lookup_vars: Option>, - ctl_vars: &[CtlCheckVarsTarget], - consumer: &mut RecursiveConstraintConsumer, -) where - F: RichField + Extendable, - S: Stark, -{ - // Evaluate all of the STARK's table constraints. - stark.eval_ext_circuit(builder, vars, consumer); - if let Some(lookup_vars) = lookup_vars { - // Evaluate all of the STARK's constraints related to the permutation argument. - eval_ext_lookups_circuit::(builder, stark, vars, lookup_vars, consumer); - } - // Evaluate all of the STARK's constraints related to the cross-table lookups. - eval_cross_table_lookup_checks_circuit::( - builder, - vars, - ctl_vars, - consumer, - stark.constraint_degree(), - ); -} diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 3e284c7fc4..fd2af86388 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -1,34 +1,22 @@ -use core::any::type_name; - -use anyhow::{ensure, Result}; +use anyhow::Result; use ethereum_types::{BigEndianHash, U256}; use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::types::Field; -use plonky2::fri::verifier::verify_fri_proof; +use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::GenericConfig; -use plonky2::plonk::plonk_common::reduce_with_powers; +use starky::config::StarkConfig; +use starky::cross_table_lookup::{get_ctl_vars_from_proofs, verify_cross_table_lookups}; +use starky::lookup::GrandProductChallenge; +use starky::stark::Stark; +use starky::verifier::verify_stark_proof_with_challenges; use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::config::StarkConfig; -use crate::constraint_consumer::ConstraintConsumer; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cross_table_lookup::{ - num_ctl_helper_columns_by_table, verify_cross_table_lookups, CtlCheckVars, - GrandProductChallengeSet, -}; -use crate::evaluation_frame::StarkEvaluationFrame; -use crate::lookup::{GrandProductChallenge, LookupCheckVars}; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; -use crate::proof::{ - AllProof, AllProofChallenges, PublicValues, StarkOpeningSet, StarkProof, StarkProofChallenges, -}; -use crate::stark::Stark; +use crate::proof::{AllProof, AllProofChallenges, PublicValues}; use crate::util::h2u; -use crate::vanishing_poly::eval_vanishing_poly; pub fn verify_proof, C: GenericConfig, const D: usize>( all_stark: &AllStark, @@ -57,73 +45,71 @@ where cross_table_lookups, } = all_stark; - let num_ctl_helper_cols = num_ctl_helper_columns_by_table( - cross_table_lookups, - all_stark.arithmetic_stark.constraint_degree(), - ); - - let ctl_vars_per_table = CtlCheckVars::from_proofs( - &all_proof.stark_proofs, + let ctl_vars_per_table = get_ctl_vars_from_proofs( + &all_proof.multi_proof, cross_table_lookups, &ctl_challenges, &num_lookup_columns, - &num_ctl_helper_cols, + all_stark.arithmetic_stark.constraint_degree(), ); + let stark_proofs = &all_proof.multi_proof.stark_proofs; + verify_stark_proof_with_challenges( arithmetic_stark, - &all_proof.stark_proofs[Table::Arithmetic as usize].proof, + &stark_proofs[Table::Arithmetic as usize].proof, &stark_challenges[Table::Arithmetic as usize], - &ctl_vars_per_table[Table::Arithmetic as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::Arithmetic as usize]), + &[], config, )?; + verify_stark_proof_with_challenges( byte_packing_stark, - &all_proof.stark_proofs[Table::BytePacking as usize].proof, + &stark_proofs[Table::BytePacking as usize].proof, &stark_challenges[Table::BytePacking as usize], - &ctl_vars_per_table[Table::BytePacking as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::BytePacking as usize]), + &[], config, )?; verify_stark_proof_with_challenges( cpu_stark, - &all_proof.stark_proofs[Table::Cpu as usize].proof, + &stark_proofs[Table::Cpu as usize].proof, &stark_challenges[Table::Cpu as usize], - &ctl_vars_per_table[Table::Cpu as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::Cpu as usize]), + &[], config, )?; verify_stark_proof_with_challenges( keccak_stark, - &all_proof.stark_proofs[Table::Keccak as usize].proof, + &stark_proofs[Table::Keccak as usize].proof, &stark_challenges[Table::Keccak as usize], - &ctl_vars_per_table[Table::Keccak as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::Keccak as usize]), + &[], config, )?; verify_stark_proof_with_challenges( keccak_sponge_stark, - &all_proof.stark_proofs[Table::KeccakSponge as usize].proof, + &stark_proofs[Table::KeccakSponge as usize].proof, &stark_challenges[Table::KeccakSponge as usize], - &ctl_vars_per_table[Table::KeccakSponge as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::KeccakSponge as usize]), + &[], config, )?; verify_stark_proof_with_challenges( logic_stark, - &all_proof.stark_proofs[Table::Logic as usize].proof, + &stark_proofs[Table::Logic as usize].proof, &stark_challenges[Table::Logic as usize], - &ctl_vars_per_table[Table::Logic as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::Logic as usize]), + &[], config, )?; verify_stark_proof_with_challenges( memory_stark, - &all_proof.stark_proofs[Table::Memory as usize].proof, + &stark_proofs[Table::Memory as usize].proof, &stark_challenges[Table::Memory as usize], - &ctl_vars_per_table[Table::Memory as usize], - &ctl_challenges, + Some(&ctl_vars_per_table[Table::Memory as usize]), + &[], config, )?; @@ -141,9 +127,10 @@ where verify_cross_table_lookups::( cross_table_lookups, all_proof + .multi_proof .stark_proofs - .map(|p| p.proof.openings.ctl_zs_first), - extra_looking_sums, + .map(|p| p.proof.openings.ctl_zs_first.unwrap()), + Some(&extra_looking_sums), config, ) } @@ -293,186 +280,8 @@ where running_sum + challenge.combine(row.iter()).inverse() } -pub(crate) fn verify_stark_proof_with_challenges< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - stark: &S, - proof: &StarkProof, - challenges: &StarkProofChallenges, - ctl_vars: &[CtlCheckVars], - ctl_challenges: &GrandProductChallengeSet, - config: &StarkConfig, -) -> Result<()> { - log::debug!("Checking proof: {}", type_name::()); - let num_ctl_polys = ctl_vars - .iter() - .map(|ctl| ctl.helper_columns.len()) - .sum::(); - let num_ctl_z_polys = ctl_vars.len(); - validate_proof_shape(stark, proof, config, num_ctl_polys, num_ctl_z_polys)?; - let StarkOpeningSet { - local_values, - next_values, - auxiliary_polys, - auxiliary_polys_next, - ctl_zs_first: _, - quotient_polys, - } = &proof.openings; - let vars = S::EvaluationFrame::from_values(local_values, next_values); - - let degree_bits = proof.recover_degree_bits(config); - let (l_0, l_last) = eval_l_0_and_l_last(degree_bits, challenges.stark_zeta); - let last = F::primitive_root_of_unity(degree_bits).inverse(); - let z_last = challenges.stark_zeta - last.into(); - let mut consumer = ConstraintConsumer::::new( - challenges - .stark_alphas - .iter() - .map(|&alpha| F::Extension::from_basefield(alpha)) - .collect::>(), - z_last, - l_0, - l_last, - ); - let num_lookup_columns = stark.num_lookup_helper_columns(config); - let lookup_challenges = (num_lookup_columns > 0).then(|| { - ctl_challenges - .challenges - .iter() - .map(|ch| ch.beta) - .collect::>() - }); - - let lookup_vars = stark.uses_lookups().then(|| LookupCheckVars { - local_values: auxiliary_polys[..num_lookup_columns].to_vec(), - next_values: auxiliary_polys_next[..num_lookup_columns].to_vec(), - challenges: lookup_challenges.unwrap(), - }); - let lookups = stark.lookups(); - eval_vanishing_poly::( - stark, - &vars, - &lookups, - lookup_vars, - ctl_vars, - &mut consumer, - ); - let vanishing_polys_zeta = consumer.accumulators(); - - // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. - let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits); - let z_h_zeta = zeta_pow_deg - F::Extension::ONE; - // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. - // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` - // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. - // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each - // `quotient_degree_factor`-sized chunk of the original evaluations. - for (i, chunk) in quotient_polys - .chunks(stark.quotient_degree_factor()) - .enumerate() - { - ensure!( - vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg), - "Mismatch between evaluation and opening of quotient polynomial" - ); - } - - let merkle_caps = vec![ - proof.trace_cap.clone(), - proof.auxiliary_polys_cap.clone(), - proof.quotient_polys_cap.clone(), - ]; - - let num_ctl_zs = ctl_vars - .iter() - .map(|ctl| ctl.helper_columns.len()) - .collect::>(); - verify_fri_proof::( - &stark.fri_instance( - challenges.stark_zeta, - F::primitive_root_of_unity(degree_bits), - num_ctl_polys, - num_ctl_zs, - config, - ), - &proof.openings.to_fri_openings(), - &challenges.fri_challenges, - &merkle_caps, - &proof.opening_proof, - &config.fri_params(degree_bits), - )?; - - Ok(()) -} - -fn validate_proof_shape( - stark: &S, - proof: &StarkProof, - config: &StarkConfig, - num_ctl_helpers: usize, - num_ctl_zs: usize, -) -> anyhow::Result<()> -where - F: RichField + Extendable, - C: GenericConfig, - S: Stark, -{ - let StarkProof { - trace_cap, - auxiliary_polys_cap, - quotient_polys_cap, - openings, - // The shape of the opening proof will be checked in the FRI verifier (see - // validate_fri_proof_shape), so we ignore it here. - opening_proof: _, - } = proof; - - let StarkOpeningSet { - local_values, - next_values, - auxiliary_polys, - auxiliary_polys_next, - ctl_zs_first, - quotient_polys, - } = openings; - - let degree_bits = proof.recover_degree_bits(config); - let fri_params = config.fri_params(degree_bits); - let cap_height = fri_params.config.cap_height; - - let num_auxiliary = num_ctl_helpers + stark.num_lookup_helper_columns(config) + num_ctl_zs; - - ensure!(trace_cap.height() == cap_height); - ensure!(auxiliary_polys_cap.height() == cap_height); - ensure!(quotient_polys_cap.height() == cap_height); - - ensure!(local_values.len() == S::COLUMNS); - ensure!(next_values.len() == S::COLUMNS); - ensure!(auxiliary_polys.len() == num_auxiliary); - ensure!(auxiliary_polys_next.len() == num_auxiliary); - ensure!(ctl_zs_first.len() == num_ctl_zs); - ensure!(quotient_polys.len() == stark.num_quotient_polys(config)); - - Ok(()) -} - -/// Evaluate the Lagrange polynomials `L_0` and `L_(n-1)` at a point `x`. -/// `L_0(x) = (x^n - 1)/(n * (x - 1))` -/// `L_(n-1)(x) = (x^n - 1)/(n * (g * x - 1))`, with `g` the first element of the subgroup. -fn eval_l_0_and_l_last(log_n: usize, x: F) -> (F, F) { - let n = F::from_canonical_usize(1 << log_n); - let g = F::primitive_root_of_unity(log_n); - let z_x = x.exp_power_of_2(log_n) - F::ONE; - let invs = F::batch_multiplicative_inverse(&[n * (x - F::ONE), n * (g * x - F::ONE)]); - - (z_x * invs[0], z_x * invs[1]) -} - -#[cfg(test)] -pub(crate) mod testutils { +#[cfg(debug_assertions)] +pub(crate) mod debug_utils { use super::*; /// Output all the extra memory rows that don't appear in the CPU trace but are @@ -610,26 +419,3 @@ pub(crate) mod testutils { row } } -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::polynomial::PolynomialValues; - use plonky2::field::types::Sample; - - use crate::verifier::eval_l_0_and_l_last; - - #[test] - fn test_eval_l_0_and_l_last() { - type F = GoldilocksField; - let log_n = 5; - let n = 1 << log_n; - - let x = F::rand(); // challenge point - let expected_l_first_x = PolynomialValues::selector(n, 0).ifft().eval(x); - let expected_l_last_x = PolynomialValues::selector(n, n - 1).ifft().eval(x); - - let (l_first_x, l_last_x) = eval_l_0_and_l_last(log_n, x); - assert_eq!(l_first_x, expected_l_first_x); - assert_eq!(l_last_x, expected_l_last_x); - } -} diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index f7f5c9d365..76267a0a41 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -6,15 +6,15 @@ use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; +use starky::config::StarkConfig; +use starky::util::trace_rows_to_poly_values; use crate::all_stark::{AllStark, NUM_TABLES}; use crate::arithmetic::{BinaryOperator, Operation}; use crate::byte_packing::byte_packing_stark::BytePackingOp; -use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; use crate::keccak_sponge::columns::KECCAK_WIDTH_BYTES; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; -use crate::util::trace_rows_to_poly_values; use crate::witness::memory::MemoryOp; use crate::{arithmetic, keccak, keccak_sponge, logic}; diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index 6a15dfc06d..51da107c53 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -11,14 +11,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index 7d07ca19ac..69c90988c5 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -11,15 +11,13 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 15416c8c8d..8f482f72dc 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -11,12 +11,9 @@ use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer}; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; -use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; -use plonky2_evm::Node; +use plonky2_evm::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/erc20.rs b/evm/tests/erc20.rs index 48d0d75364..430da14d5e 100644 --- a/evm/tests/erc20.rs +++ b/evm/tests/erc20.rs @@ -10,14 +10,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/erc721.rs b/evm/tests/erc721.rs index 0c6d50d836..4dfed24958 100644 --- a/evm/tests/erc721.rs +++ b/evm/tests/erc721.rs @@ -10,14 +10,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index 37d874cdac..a95473fcb0 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -12,16 +12,13 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; -use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; use plonky2_evm::generation::mpt::transaction_testing::{AddressOption, LegacyTransactionRlp}; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index 538f2aa798..d759387cb0 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -11,14 +11,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/selfdestruct.rs b/evm/tests/selfdestruct.rs index 829e0b21b0..87b39e3076 100644 --- a/evm/tests/selfdestruct.rs +++ b/evm/tests/selfdestruct.rs @@ -10,14 +10,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index 5fd252df45..cd17fdaeda 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -11,14 +11,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; type F = GoldilocksField; const D: usize = 2; diff --git a/evm/tests/withdrawals.rs b/evm/tests/withdrawals.rs index ef2d19b02a..ef40b52987 100644 --- a/evm/tests/withdrawals.rs +++ b/evm/tests/withdrawals.rs @@ -9,14 +9,12 @@ use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; -use plonky2_evm::all_stark::AllStark; -use plonky2_evm::config::StarkConfig; use plonky2_evm::generation::mpt::AccountRlp; use plonky2_evm::generation::{GenerationInputs, TrieInputs}; use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; -use plonky2_evm::Node; +use plonky2_evm::{AllStark, Node, StarkConfig}; use rand::random; type F = GoldilocksField; diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index edff1bea4a..6c8145eca0 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -360,6 +360,7 @@ impl, H: Hasher, const D: usize> CompressedFriPr } } +#[derive(Debug)] pub struct FriChallenges, const D: usize> { // Scaling factor to combine polynomials. pub fri_alpha: F::Extension, @@ -373,6 +374,7 @@ pub struct FriChallenges, const D: usize> { pub fri_query_indices: Vec, } +#[derive(Debug)] pub struct FriChallengesTarget { pub fri_alpha: ExtensionTarget, pub fri_betas: Vec>, diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 0efae5fcf9..fe64413f9f 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -17,7 +17,9 @@ std = ["anyhow/std", "plonky2/std"] timing = ["plonky2/timing"] [dependencies] +ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. anyhow = { version = "1.0.40", default-features = false } +hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. itertools = { version = "0.11.0", default-features = false } log = { version = "0.4.14", default-features = false } num-bigint = { version = "0.4.3", default-features = false } diff --git a/starky/src/config.rs b/starky/src/config.rs index 24ddb6a78f..8f95c0ea1c 100644 --- a/starky/src/config.rs +++ b/starky/src/config.rs @@ -1,17 +1,49 @@ +//! A [`StarkConfig`] defines all the parameters to be used when proving a +//! [`Stark`][crate::stark::Stark]. +//! +//! The default configuration is aimed for speed, yielding fast but large +//! proofs, with a targeted security level of 100 bits. + +#[cfg(not(feature = "std"))] +use alloc::format; + +use anyhow::{anyhow, Result}; +use plonky2::field::extension::Extendable; +use plonky2::field::types::Field; use plonky2::fri::reduction_strategies::FriReductionStrategy; use plonky2::fri::{FriConfig, FriParams}; +use plonky2::hash::hash_types::RichField; +/// A configuration containing the different parameters used by the STARK prover. +#[derive(Clone, Debug)] pub struct StarkConfig { + /// The targeted security level for the proofs generated with this configuration. pub security_bits: usize, /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly) /// `degree / |F|`. pub num_challenges: usize, + /// The configuration of the FRI sub-protocol. pub fri_config: FriConfig, } +impl Default for StarkConfig { + fn default() -> Self { + Self::standard_fast_config() + } +} + impl StarkConfig { + /// Returns a custom STARK configuration. + pub const fn new(security_bits: usize, num_challenges: usize, fri_config: FriConfig) -> Self { + Self { + security_bits, + num_challenges, + fri_config, + } + } + /// A typical configuration with a rate of 2, resulting in fast but large proofs. /// Targets ~100 bit conjectured security. pub const fn standard_fast_config() -> Self { @@ -28,7 +60,88 @@ impl StarkConfig { } } - pub(crate) fn fri_params(&self, degree_bits: usize) -> FriParams { + /// Outputs the [`FriParams`] used during the FRI sub-protocol by this [`StarkConfig`]. + pub fn fri_params(&self, degree_bits: usize) -> FriParams { self.fri_config.fri_params(degree_bits, false) } + + /// Checks that this STARK configuration is consistent, i.e. that the different + /// parameters meet the targeted security level. + pub fn check_config, const D: usize>(&self) -> Result<()> { + let StarkConfig { + security_bits, + fri_config: + FriConfig { + rate_bits, + proof_of_work_bits, + num_query_rounds, + .. + }, + .. + } = &self; + + // Conjectured FRI security; see the ethSTARK paper. + let fri_field_bits = F::Extension::order().bits() as usize; + let fri_query_security_bits = num_query_rounds * rate_bits + *proof_of_work_bits as usize; + let fri_security_bits = fri_field_bits.min(fri_query_security_bits); + + if fri_security_bits < *security_bits { + Err(anyhow!(format!( + "FRI params fall short of target security {}, reaching only {}", + security_bits, fri_security_bits + ))) + } else { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use plonky2::field::goldilocks_field::GoldilocksField; + + use super::*; + + #[test] + fn test_valid_config() { + type F = GoldilocksField; + const D: usize = 2; + + let config = StarkConfig::standard_fast_config(); + assert!(config.check_config::().is_ok()); + + let high_rate_config = StarkConfig::new( + 100, + 2, + FriConfig { + rate_bits: 3, + cap_height: 4, + proof_of_work_bits: 16, + reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5), + num_query_rounds: 28, + }, + ); + assert!(high_rate_config.check_config::().is_ok()); + } + + #[test] + fn test_invalid_config() { + type F = GoldilocksField; + const D: usize = 2; + + let too_few_queries_config = StarkConfig::new( + 100, + 2, + FriConfig { + rate_bits: 1, + cap_height: 4, + proof_of_work_bits: 16, + reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5), + num_query_rounds: 50, + }, + ); + // The conjectured security yields `rate_bits` * `num_query_rounds` + `proof_of_work_bits` = 66 + // bits of security for FRI, which falls short of the 100 bits of security target. + assert!(too_few_queries_config.check_config::().is_err()); + } } diff --git a/starky/src/constraint_consumer.rs b/starky/src/constraint_consumer.rs index 0354893571..02eff4b197 100644 --- a/starky/src/constraint_consumer.rs +++ b/starky/src/constraint_consumer.rs @@ -1,5 +1,10 @@ -use alloc::vec; -use alloc::vec::Vec; +//! Implementation of the constraint consumer. +//! +//! The [`ConstraintConsumer`], and its circuit counterpart, allow a +//! prover to evaluate all polynomials of a [`Stark`][crate::stark::Stark]. + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::marker::PhantomData; use plonky2::field::extension::Extendable; @@ -9,14 +14,15 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; +/// A [`ConstraintConsumer`] evaluates all constraint, permutation and cross-table +/// lookup polynomials of a [`Stark`][crate::stark::Stark]. +#[derive(Debug)] pub struct ConstraintConsumer { /// Random values used to combine multiple constraints into one. alphas: Vec, /// Running sums of constraints that have been emitted so far, scaled by powers of alpha. - // TODO(JN): This is pub so it can be used in a test. Once we have an API for accessing this - // result, it should be made private. - pub constraint_accs: Vec

, + constraint_accs: Vec

, /// The evaluation of `X - g^(n-1)`. z_last: P, @@ -31,6 +37,7 @@ pub struct ConstraintConsumer { } impl ConstraintConsumer

{ + /// Creates a new instance of [`ConstraintConsumer`]. pub fn new( alphas: Vec, z_last: P, @@ -46,6 +53,8 @@ impl ConstraintConsumer

{ } } + /// Consumes this [`ConstraintConsumer`] and outputs its sum of accumulated + /// constraints scaled by powers of `alpha`. pub fn accumulators(self) -> Vec

{ self.constraint_accs } @@ -76,6 +85,8 @@ impl ConstraintConsumer

{ } } +/// Circuit version of [`ConstraintConsumer`]. +#[derive(Debug)] pub struct RecursiveConstraintConsumer, const D: usize> { /// A random value used to combine multiple constraints into one. alphas: Vec, @@ -98,6 +109,7 @@ pub struct RecursiveConstraintConsumer, const D: us } impl, const D: usize> RecursiveConstraintConsumer { + /// Creates a new instance of [`RecursiveConstraintConsumer`]. pub fn new( zero: ExtensionTarget, alphas: Vec, @@ -115,6 +127,8 @@ impl, const D: usize> RecursiveConstraintConsumer Vec> { self.constraint_accs } diff --git a/evm/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs similarity index 84% rename from evm/src/cross_table_lookup.rs rename to starky/src/cross_table_lookup.rs index 359b5309e8..f6f958a2db 100644 --- a/evm/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -27,8 +27,11 @@ //! is similar, but we provide not only `local_values` but also `next_values` -- corresponding to //! the current and next row values -- when computing the linear combinations. +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::cmp::min; use core::fmt::Debug; +use core::iter::once; use anyhow::{ensure, Result}; use itertools::Itertools; @@ -37,40 +40,39 @@ use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; -use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; +use plonky2::iop::challenger::Challenger; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::plonk::config::GenericConfig; use plonky2::util::ceil_div_usize; -use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::{ - eval_helper_columns, eval_helper_columns_circuit, get_helper_cols, Column, ColumnFilter, - Filter, GrandProductChallenge, + eval_helper_columns, eval_helper_columns_circuit, get_grand_product_challenge_set, + get_helper_cols, Column, ColumnFilter, Filter, GrandProductChallenge, GrandProductChallengeSet, }; -use crate::proof::{StarkProofTarget, StarkProofWithMetadata}; +use crate::proof::{MultiProof, StarkProofTarget, StarkProofWithMetadata}; use crate::stark::Stark; /// An alias for `usize`, to represent the index of a STARK table in a multi-STARK setting. -pub(crate) type TableIdx = usize; +pub type TableIdx = usize; /// A `table` index with a linear combination of columns and a filter. /// `filter` is used to determine the rows to select in `table`. /// `columns` represents linear combinations of the columns of `table`. #[derive(Clone, Debug)] -pub(crate) struct TableWithColumns { +pub struct TableWithColumns { table: TableIdx, columns: Vec>, - pub(crate) filter: Option>, + filter: Option>, } impl TableWithColumns { /// Generates a new `TableWithColumns` given a `table` index, a linear combination of columns `columns` and a `filter`. - pub(crate) fn new(table: TableIdx, columns: Vec>, filter: Option>) -> Self { + pub fn new(table: TableIdx, columns: Vec>, filter: Option>) -> Self { Self { table, columns, @@ -81,7 +83,7 @@ impl TableWithColumns { /// Cross-table lookup data consisting in the lookup table (`looked_table`) and all the tables that look into `looked_table` (`looking_tables`). /// Each `looking_table` corresponds to a STARK's table whose rows have been filtered out and whose columns have been through a linear combination (see `eval_table`). The concatenation of those smaller tables should result in the `looked_table`. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CrossTableLookup { /// Column linear combinations for all tables that are looking into the current table. pub(crate) looking_tables: Vec>, @@ -92,7 +94,7 @@ pub struct CrossTableLookup { impl CrossTableLookup { /// Creates a new `CrossTableLookup` given some looking tables and a looked table. /// All tables should have the same width. - pub(crate) fn new( + pub fn new( looking_tables: Vec>, looked_table: TableWithColumns, ) -> Self { @@ -109,7 +111,7 @@ impl CrossTableLookup { /// - the total number of helper columns for this table, over all Cross-table lookups, /// - the total number of z polynomials for this table, over all Cross-table lookups, /// - the number of helper columns for this table, for each Cross-table lookup. - pub(crate) fn num_ctl_helpers_zs_all( + pub fn num_ctl_helpers_zs_all( ctls: &[Self], table: TableIdx, num_challenges: usize, @@ -119,7 +121,7 @@ impl CrossTableLookup { let mut num_ctls = 0; let mut num_helpers_by_ctl = vec![0; ctls.len()]; for (i, ctl) in ctls.iter().enumerate() { - let all_tables = std::iter::once(&ctl.looked_table).chain(&ctl.looking_tables); + let all_tables = once(&ctl.looked_table).chain(&ctl.looking_tables); let num_appearances = all_tables.filter(|twc| twc.table == table).count(); let is_helpers = num_appearances > 2; if is_helpers { @@ -140,23 +142,23 @@ impl CrossTableLookup { } /// Cross-table lookup data for one table. -#[derive(Clone, Default)] -pub(crate) struct CtlData<'a, F: Field> { +#[derive(Clone, Default, Debug)] +pub struct CtlData<'a, F: Field> { /// Data associated with all Z(x) polynomials for one table. - pub(crate) zs_columns: Vec>, + pub zs_columns: Vec>, } /// Cross-table lookup data associated with one Z(x) polynomial. /// One Z(x) polynomial can be associated to multiple tables, /// built from the same STARK. -#[derive(Clone)] -pub(crate) struct CtlZData<'a, F: Field> { +#[derive(Clone, Debug)] +pub struct CtlZData<'a, F: Field> { /// Helper columns to verify the Z polynomial values. pub(crate) helper_columns: Vec>, /// Z polynomial values. pub(crate) z: PolynomialValues, /// Cross-table lookup challenge. - pub(crate) challenge: GrandProductChallenge, + pub challenge: GrandProductChallenge, /// Vector of column linear combinations for the current tables. pub(crate) columns: Vec<&'a [Column]>, /// Vector of filter columns for the current table. @@ -164,17 +166,26 @@ pub(crate) struct CtlZData<'a, F: Field> { pub(crate) filter: Vec>>, } -impl<'a, F: Field> CtlData<'a, F> { - /// Returns the number of cross-table lookup polynomials. - pub(crate) fn len(&self) -> usize { - self.zs_columns.len() - } - - /// Returns whether there are no cross-table lookups. - pub(crate) fn is_empty(&self) -> bool { - self.zs_columns.is_empty() +impl<'a, F: Field> CtlZData<'a, F> { + /// Returs new CTL data from the provided arguments. + pub fn new( + helper_columns: Vec>, + z: PolynomialValues, + challenge: GrandProductChallenge, + columns: Vec<&'a [Column]>, + filter: Vec>>, + ) -> Self { + Self { + helper_columns, + z, + challenge, + columns, + filter, + } } +} +impl<'a, F: Field> CtlData<'a, F> { /// Returns all the cross-table lookup helper polynomials. pub(crate) fn ctl_helper_polys(&self) -> Vec> { let num_polys = self @@ -210,82 +221,58 @@ impl<'a, F: Field> CtlData<'a, F> { } } -/// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct GrandProductChallengeSet { - pub(crate) challenges: Vec>, -} - -impl GrandProductChallengeSet { - pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - buffer.write_usize(self.challenges.len())?; - for challenge in &self.challenges { - buffer.write_target(challenge.beta)?; - buffer.write_target(challenge.gamma)?; - } - Ok(()) - } - - pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { - let length = buffer.read_usize()?; - let mut challenges = Vec::with_capacity(length); - for _ in 0..length { - challenges.push(GrandProductChallenge { - beta: buffer.read_target()?, - gamma: buffer.read_target()?, - }); - } - - Ok(GrandProductChallengeSet { challenges }) - } -} - -fn get_grand_product_challenge>( - challenger: &mut Challenger, -) -> GrandProductChallenge { - let beta = challenger.get_challenge(); - let gamma = challenger.get_challenge(); - GrandProductChallenge { beta, gamma } -} - -pub(crate) fn get_grand_product_challenge_set>( - challenger: &mut Challenger, - num_challenges: usize, -) -> GrandProductChallengeSet { - let challenges = (0..num_challenges) - .map(|_| get_grand_product_challenge(challenger)) - .collect(); - GrandProductChallengeSet { challenges } -} - -fn get_grand_product_challenge_target< +/// Outputs a tuple of (challenges, data) of CTL challenges and all +/// the CTL data necessary to prove a multi-STARK system. +pub fn get_ctl_data<'a, F, C, const D: usize, const N: usize>( + config: &StarkConfig, + trace_poly_values: &[Vec>; N], + all_cross_table_lookups: &'a [CrossTableLookup], + challenger: &mut Challenger, + max_constraint_degree: usize, +) -> (GrandProductChallengeSet, [CtlData<'a, F>; N]) +where F: RichField + Extendable, - H: AlgebraicHasher, - const D: usize, ->( - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, -) -> GrandProductChallenge { - let beta = challenger.get_challenge(builder); - let gamma = challenger.get_challenge(builder); - GrandProductChallenge { beta, gamma } + C: GenericConfig, +{ + // Get challenges for the cross-table lookups. + let ctl_challenges = get_grand_product_challenge_set(challenger, config.num_challenges); + + // For each STARK, compute its cross-table lookup Z polynomials + // and get the associated `CtlData`. + let ctl_data = cross_table_lookup_data::( + trace_poly_values, + all_cross_table_lookups, + &ctl_challenges, + max_constraint_degree, + ); + + (ctl_challenges, ctl_data) } -pub(crate) fn get_grand_product_challenge_set_target< +/// Outputs all the CTL data necessary to prove a multi-STARK system. +pub fn get_ctl_vars_from_proofs<'a, F, C, const D: usize, const N: usize>( + multi_proof: &MultiProof, + all_cross_table_lookups: &'a [CrossTableLookup], + ctl_challenges: &'a GrandProductChallengeSet, + num_lookup_columns: &'a [usize; N], + max_constraint_degree: usize, +) -> [Vec>::Extension, >::Extension, D>>; + N] +where F: RichField + Extendable, - H: AlgebraicHasher, - const D: usize, ->( - builder: &mut CircuitBuilder, - challenger: &mut RecursiveChallenger, - num_challenges: usize, -) -> GrandProductChallengeSet { - let challenges = (0..num_challenges) - .map(|_| get_grand_product_challenge_target(builder, challenger)) - .collect(); - GrandProductChallengeSet { challenges } + C: GenericConfig, +{ + let num_ctl_helper_cols = + num_ctl_helper_columns_by_table(all_cross_table_lookups, max_constraint_degree); + + CtlCheckVars::from_proofs( + &multi_proof.stark_proofs, + all_cross_table_lookups, + ctl_challenges, + num_lookup_columns, + &num_ctl_helper_cols, + ) } - /// Returns the number of helper columns for each `Table`. pub(crate) fn num_ctl_helper_columns_by_table( ctls: &[CrossTableLookup], @@ -314,6 +301,17 @@ pub(crate) fn num_ctl_helper_columns_by_table( res } +/// Gets the auxiliary polynomials associated to these CTL data. +pub(crate) fn get_ctl_auxiliary_polys( + ctl_data: Option<&CtlData>, +) -> Option>> { + ctl_data.map(|data| { + let mut ctl_polys = data.ctl_helper_polys(); + ctl_polys.extend(data.ctl_z_polys()); + ctl_polys + }) +} + /// Generates all the cross-table lookup data, for all tables. /// - `trace_poly_values` corresponds to the trace values for all tables. /// - `cross_table_lookups` corresponds to all the cross-table lookups, i.e. the looked and looking tables, as described in `CrossTableLookup`. @@ -467,8 +465,8 @@ fn partial_sums( } /// Data necessary to check the cross-table lookups of a given table. -#[derive(Clone)] -pub(crate) struct CtlCheckVars<'a, F, FE, P, const D2: usize> +#[derive(Clone, Debug)] +pub struct CtlCheckVars<'a, F, FE, P, const D2: usize> where F: Field, FE: FieldExtension, @@ -493,13 +491,24 @@ impl<'a, F: RichField + Extendable, const D: usize> CtlCheckVars<'a, F, F::Extension, F::Extension, D> { /// Extracts the `CtlCheckVars` for each STARK. - pub(crate) fn from_proofs, const N: usize>( + pub fn from_proofs, const N: usize>( proofs: &[StarkProofWithMetadata; N], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_lookup_columns: &[usize; N], num_helper_ctl_columns: &Vec<[usize; N]>, ) -> [Vec; N] { + let mut ctl_vars_per_table = [0; N].map(|_| vec![]); + // If there are no auxiliary polys in the proofs `openings`, + // return early. The verifier will reject the proofs when + // calling `validate_proof_shape`. + if proofs + .iter() + .any(|p| p.proof.openings.auxiliary_polys.is_none()) + { + return ctl_vars_per_table; + } + let mut total_num_helper_cols_by_table = [0; N]; for p_ctls in num_helper_ctl_columns { for j in 0..N { @@ -514,8 +523,14 @@ impl<'a, F: RichField + Extendable, const D: usize> .map(|(p, &num_lookup)| { let openings = &p.proof.openings; - let ctl_zs = &openings.auxiliary_polys[num_lookup..]; - let ctl_zs_next = &openings.auxiliary_polys_next[num_lookup..]; + let ctl_zs = &openings + .auxiliary_polys + .as_ref() + .expect("We cannot have CTls without auxiliary polynomials.")[num_lookup..]; + let ctl_zs_next = &openings + .auxiliary_polys_next + .as_ref() + .expect("We cannot have CTls without auxiliary polynomials.")[num_lookup..]; ctl_zs.iter().zip(ctl_zs_next).collect::>() }) .collect::>(); @@ -523,7 +538,6 @@ impl<'a, F: RichField + Extendable, const D: usize> // Put each cross-table lookup polynomial into the correct table data: if a CTL polynomial is extracted from looking/looked table t, then we add it to the `CtlCheckVars` of table t. let mut start_indices = [0; N]; let mut z_indices = [0; N]; - let mut ctl_vars_per_table = [0; N].map(|_| vec![]); for ( CrossTableLookup { looking_tables, @@ -698,8 +712,8 @@ pub(crate) fn eval_cross_table_lookup_checks { +#[derive(Clone, Debug)] +pub struct CtlCheckVarsTarget { ///Evaluation of the helper columns to check that the Z polyomial /// was constructed correctly. pub(crate) helper_columns: Vec>, @@ -716,8 +730,8 @@ pub(crate) struct CtlCheckVarsTarget { } impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { - /// Circuit version of `from_proofs`. Extracts the `CtlCheckVarsTarget` for each STARK. - pub(crate) fn from_proof( + /// Circuit version of `from_proofs`, for a single STARK. + pub fn from_proof( table: TableIdx, proof: &StarkProofTarget, cross_table_lookups: &'a [CrossTableLookup], @@ -729,15 +743,24 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { // Get all cross-table lookup polynomial openings for each STARK proof. let ctl_zs = { let openings = &proof.openings; - let ctl_zs = openings.auxiliary_polys.iter().skip(num_lookup_columns); + let ctl_zs = openings + .auxiliary_polys + .as_ref() + .expect("We cannot have CTls without auxiliary polynomials.") + .iter() + .skip(num_lookup_columns); let ctl_zs_next = openings .auxiliary_polys_next + .as_ref() + .expect("We cannot have CTls without auxiliary polynomials.") .iter() .skip(num_lookup_columns); ctl_zs.zip(ctl_zs_next).collect::>() }; - // Put each cross-table lookup polynomial into the correct table data: if a CTL polynomial is extracted from looking/looked table t, then we add it to the `CtlCheckVars` of table t. + // Put each cross-table lookup polynomial into the correct table's data. + // If a CTL polynomial is extracted from the looking/looked table `t``, + // then we add it to the `CtlCheckVars` of table `t``. let mut z_index = 0; let mut start_index = 0; let mut ctl_vars = vec![]; @@ -750,7 +773,8 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { ) in cross_table_lookups.iter().enumerate() { for &challenges in &ctl_challenges.challenges { - // Group looking tables by `Table`, since we bundle the looking tables taken from the same `Table` together thanks to helper columns. + // Group looking tables by `Table`, since we bundle the looking tables + // taken from the same `Table` together thanks to helper columns. let count = looking_tables .iter() @@ -779,8 +803,6 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { start_index += num_helper_ctl_columns[i]; z_index += 1; - // let columns = group.0.clone(); - // let filter = group.1.clone(); ctl_vars.push(Self { helper_columns, local_z: *looking_z, @@ -921,14 +943,10 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< } /// Verifies all cross-table lookups. -pub(crate) fn verify_cross_table_lookups< - F: RichField + Extendable, - const D: usize, - const N: usize, ->( +pub fn verify_cross_table_lookups, const D: usize, const N: usize>( cross_table_lookups: &[CrossTableLookup], ctl_zs_first: [Vec; N], - ctl_extra_looking_sums: Vec>, + ctl_extra_looking_sums: Option<&[Vec]>, config: &StarkConfig, ) -> Result<()> { let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); @@ -941,7 +959,9 @@ pub(crate) fn verify_cross_table_lookups< ) in cross_table_lookups.iter().enumerate() { // Get elements looking into `looked_table` that are not associated to any STARK. - let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table]; + let extra_sum_vec: &[F] = ctl_extra_looking_sums + .map(|v| v[looked_table.table].as_ref()) + .unwrap_or_default(); // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { @@ -974,7 +994,7 @@ pub(crate) fn verify_cross_table_lookups< } /// Circuit version of `verify_cross_table_lookups`. Verifies all cross-table lookups. -pub(crate) fn verify_cross_table_lookups_circuit< +pub fn verify_cross_table_lookups_circuit< F: RichField + Extendable, const D: usize, const N: usize, @@ -982,7 +1002,7 @@ pub(crate) fn verify_cross_table_lookups_circuit< builder: &mut CircuitBuilder, cross_table_lookups: Vec>, ctl_zs_first: [Vec; N], - ctl_extra_looking_sums: Vec>, + ctl_extra_looking_sums: Option<&[Vec]>, inner_config: &StarkConfig, ) { let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); @@ -992,7 +1012,9 @@ pub(crate) fn verify_cross_table_lookups_circuit< } in cross_table_lookups.into_iter() { // Get elements looking into `looked_table` that are not associated to any STARK. - let extra_sum_vec = &ctl_extra_looking_sums[looked_table.table]; + let extra_sum_vec: &[Target] = ctl_extra_looking_sums + .map(|v| v[looked_table.table].as_ref()) + .unwrap_or_default(); // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { @@ -1019,26 +1041,32 @@ pub(crate) fn verify_cross_table_lookups_circuit< debug_assert!(ctl_zs_openings.iter_mut().all(|iter| iter.next().is_none())); } -#[cfg(test)] -pub(crate) mod testutils { - use std::collections::HashMap; - +/// Debugging module, to assert correctness of the different CTLs of a multi-STARK system, +/// that can be used during the proof generation process. +/// +/// **Note**: this is an expensive check, hence is only available when the `debug_assertions` +/// flag is activated, to not hinder performances with regular `release` build. +#[cfg(debug_assertions)] +pub mod debug_utils { + #[cfg(not(feature = "std"))] + use alloc::{vec, vec::Vec}; + + use hashbrown::HashMap; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; - use crate::all_stark::Table; - use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; + use super::{CrossTableLookup, TableIdx, TableWithColumns}; - type MultiSet = HashMap, Vec<(Table, usize)>>; + type MultiSet = HashMap, Vec<(TableIdx, usize)>>; /// Check that the provided traces and cross-table lookups are consistent. - pub(crate) fn check_ctls( + pub fn check_ctls( trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], - extra_memory_looking_values: &[Vec], + extra_looking_values: &HashMap>>, ) { for (i, ctl) in cross_table_lookups.iter().enumerate() { - check_ctl(trace_poly_values, ctl, i, extra_memory_looking_values); + check_ctl(trace_poly_values, ctl, i, extra_looking_values.get(&i)); } } @@ -1046,7 +1074,7 @@ pub(crate) mod testutils { trace_poly_values: &[Vec>], ctl: &CrossTableLookup, ctl_index: usize, - extra_memory_looking_values: &[Vec], + extra_looking_values: Option<&Vec>>, ) { let CrossTableLookup { looking_tables, @@ -1063,15 +1091,15 @@ pub(crate) mod testutils { } process_table(trace_poly_values, looked_table, &mut looked_multiset); - // Extra looking values for memory - if ctl_index == Table::Memory as usize { - for row in extra_memory_looking_values.iter() { + // Include extra looking values if any for this `ctl_index`. + if let Some(values) = extra_looking_values { + for row in values.iter() { // The table and the row index don't matter here, as we just want to enforce - // that the special extra values do appear when looking against the Memory table. + // that the special extra values do appear when looking against the specified table. looking_multiset .entry(row.to_vec()) .or_default() - .push((Table::Cpu, 0)); + .push((0, 0)); } } @@ -1106,10 +1134,7 @@ pub(crate) mod testutils { .iter() .map(|c| c.eval_table(trace, i)) .collect::>(); - multiset - .entry(row) - .or_default() - .push((Table::all()[table.table], i)); + multiset.entry(row).or_default().push((table.table, i)); } else { assert_eq!(filter, F::ZERO, "Non-binary filter?") } @@ -1117,8 +1142,8 @@ pub(crate) mod testutils { } fn check_locations( - looking_locations: &[(Table, usize)], - looked_locations: &[(Table, usize)], + looking_locations: &[(TableIdx, usize)], + looked_locations: &[(TableIdx, usize)], ctl_index: usize, row: &[F], ) { diff --git a/starky/src/evaluation_frame.rs b/starky/src/evaluation_frame.rs index e2dcf2dbeb..fbfaf71e89 100644 --- a/starky/src/evaluation_frame.rs +++ b/starky/src/evaluation_frame.rs @@ -1,3 +1,5 @@ +//! Implementation of constraint evaluation frames for STARKs. + /// A trait for viewing an evaluation frame of a STARK table. /// /// It allows to access the current and next rows at a given step @@ -8,6 +10,7 @@ pub trait StarkEvaluationFrame &[T]; + /// Returns the public inputs for this evaluation frame. fn get_public_inputs(&self) -> &[U]; /// Outputs a new evaluation frame from the provided local and next values. @@ -24,6 +28,9 @@ pub trait StarkEvaluationFrame Self; } +/// An evaluation frame to be used when defining constraints of a STARK system, that +/// implements the [`StarkEvaluationFrame`] trait. +#[derive(Debug)] pub struct StarkFrame< T: Copy + Clone + Default, U: Copy + Clone + Default, diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 903c0abff4..4bfbf40443 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -1,5 +1,9 @@ -use alloc::vec; -use alloc::vec::Vec; +//! An example of generating and verifying STARK proofs for the Fibonacci sequence. +//! The toy STARK system also includes two columns that are a permutation of the other, +//! to highlight the use of the permutation argument with logUp. + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::marker::PhantomData; use plonky2::field::extension::{Extendable, FieldExtension}; @@ -16,9 +20,8 @@ use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; /// Toy STARK system used for testing. -/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition -/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. -/// Note: The `i, j` columns are only used to test the permutation argument. +/// Computes a Fibonacci sequence with state `[x0, x1]` using the state transition +/// `x0' <- x1, x1' <- x0 + x1. #[derive(Copy, Clone)] struct FibonacciStark, const D: usize> { num_rows: usize, @@ -41,6 +44,120 @@ impl, const D: usize> FibonacciStark { } } + /// Generate the trace using `x0, x1` as initial state values. + fn generate_trace(&self, x0: F, x1: F) -> Vec> { + let trace_rows = (0..self.num_rows) + .scan([x0, x1], |acc, _| { + let tmp = *acc; + acc[0] = tmp[1]; + acc[1] = tmp[0] + tmp[1]; + Some(tmp) + }) + .collect::>(); + trace_rows_to_poly_values(trace_rows) + } +} + +const FIBONACCI_COLUMNS: usize = 2; +const FIBONACCI_PUBLIC_INPUTS: usize = 3; + +impl, const D: usize> Stark for FibonacciStark { + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame< + ExtensionTarget, + ExtensionTarget, + FIBONACCI_COLUMNS, + FIBONACCI_PUBLIC_INPUTS, + >; + + fn eval_packed_generic( + &self, + vars: &Self::EvaluationFrame, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + let public_inputs = vars.get_public_inputs(); + + // Check public inputs. + yield_constr.constraint_first_row(local_values[0] - public_inputs[Self::PI_INDEX_X0]); + yield_constr.constraint_first_row(local_values[1] - public_inputs[Self::PI_INDEX_X1]); + yield_constr.constraint_last_row(local_values[1] - public_inputs[Self::PI_INDEX_RES]); + + // x0' <- x1 + yield_constr.constraint_transition(next_values[0] - local_values[1]); + // x1' <- x0 + x1 + yield_constr.constraint_transition(next_values[1] - local_values[0] - local_values[1]); + } + + fn eval_ext_circuit( + &self, + builder: &mut CircuitBuilder, + vars: &Self::EvaluationFrameTarget, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + let public_inputs = vars.get_public_inputs(); + // Check public inputs. + let pis_constraints = [ + builder.sub_extension(local_values[0], public_inputs[Self::PI_INDEX_X0]), + builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_X1]), + builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_RES]), + ]; + yield_constr.constraint_first_row(builder, pis_constraints[0]); + yield_constr.constraint_first_row(builder, pis_constraints[1]); + yield_constr.constraint_last_row(builder, pis_constraints[2]); + + // x0' <- x1 + let first_col_constraint = builder.sub_extension(next_values[0], local_values[1]); + yield_constr.constraint_transition(builder, first_col_constraint); + // x1' <- x0 + x1 + let second_col_constraint = { + let tmp = builder.sub_extension(next_values[1], local_values[0]); + builder.sub_extension(tmp, local_values[1]) + }; + yield_constr.constraint_transition(builder, second_col_constraint); + } + + fn constraint_degree(&self) -> usize { + 2 + } +} + +/// Similar system than above, but with extra columns to illustrate the permutation argument. +/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition +/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. +/// Note: The `i, j` columns are the columns used to test the permutation argument. +#[derive(Copy, Clone)] +struct FibonacciWithPermutationStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> FibonacciWithPermutationStark { + // The first public input is `x0`. + const PI_INDEX_X0: usize = 0; + // The second public input is `x1`. + const PI_INDEX_X1: usize = 1; + // The third public input is the second element of the last row, which should be equal to the + // `num_rows`-th Fibonacci number. + const PI_INDEX_RES: usize = 2; + + const fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + /// Generate the trace using `x0, x1, 0, 1, 1` as initial state values. fn generate_trace(&self, x0: F, x1: F) -> Vec> { let mut trace_rows = (0..self.num_rows) @@ -59,17 +176,23 @@ impl, const D: usize> FibonacciStark { } } -const COLUMNS: usize = 5; -const PUBLIC_INPUTS: usize = 3; +const FIBONACCI_PERM_COLUMNS: usize = 5; +const FIBONACCI_PERM_PUBLIC_INPUTS: usize = 3; -impl, const D: usize> Stark for FibonacciStark { - type EvaluationFrame = StarkFrame +impl, const D: usize> Stark + for FibonacciWithPermutationStark +{ + type EvaluationFrame = StarkFrame where FE: FieldExtension, P: PackedField; - type EvaluationFrameTarget = - StarkFrame, ExtensionTarget, COLUMNS, PUBLIC_INPUTS>; + type EvaluationFrameTarget = StarkFrame< + ExtensionTarget, + ExtensionTarget, + FIBONACCI_PERM_COLUMNS, + FIBONACCI_PERM_PUBLIC_INPUTS, + >; fn eval_packed_generic( &self, @@ -151,7 +274,7 @@ mod tests { use plonky2::util::timing::TimingTree; use crate::config::StarkConfig; - use crate::fibonacci_stark::FibonacciStark; + use crate::fibonacci_stark::{FibonacciStark, FibonacciWithPermutationStark}; use crate::proof::StarkProofWithPublicInputs; use crate::prover::prove; use crate::recursive_verifier::{ @@ -171,14 +294,30 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S = FibonacciStark; + type S1 = FibonacciStark; + type S2 = FibonacciWithPermutationStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); + + // Test first STARK + let stark = S1::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( + let proof = prove::( + stark, + &config, + trace, + &public_inputs, + &mut TimingTree::default(), + )?; + + verify_stark_proof(stark, proof, &config)?; + + // Test second STARK + let stark = S2::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( stark, &config, trace, @@ -194,10 +333,14 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S = FibonacciStark; + type S1 = FibonacciStark; + type S2 = FibonacciWithPermutationStark; let num_rows = 1 << 5; - let stark = S::new(num_rows); + let stark = S1::new(num_rows); + test_stark_low_degree(stark)?; + + let stark = S2::new(num_rows); test_stark_low_degree(stark) } @@ -206,11 +349,14 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S = FibonacciStark; + type S1 = FibonacciStark; + type S2 = FibonacciWithPermutationStark; let num_rows = 1 << 5; - let stark = S::new(num_rows); - test_stark_circuit_constraints::(stark) + let stark = S1::new(num_rows); + test_stark_circuit_constraints::(stark)?; + let stark = S2::new(num_rows); + test_stark_circuit_constraints::(stark) } #[test] @@ -219,14 +365,31 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S = FibonacciStark; + type S1 = FibonacciStark; + type S2 = FibonacciWithPermutationStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); + + // Test first STARK + let stark = S1::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( + stark, + &config, + trace, + &public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true)?; + + // Test second STARK + let stark = S2::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( + let proof = prove::( stark, &config, trace, @@ -235,7 +398,7 @@ mod tests { )?; verify_stark_proof(stark, proof.clone(), &config)?; - recursive_proof::(stark, proof, &config, true) + recursive_proof::(stark, proof, &config, true) } fn recursive_proof< @@ -257,8 +420,9 @@ mod tests { let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); + let pt = + add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero()); verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 5f9beddc3e..be75b0e010 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -1,5 +1,3 @@ -use alloc::vec::Vec; - use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialCoeffs; use plonky2::fri::proof::{FriProof, FriProofTarget}; @@ -12,12 +10,23 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::config::StarkConfig; -use crate::lookup::{get_grand_product_challenge_set, get_grand_product_challenge_set_target}; +use crate::lookup::{ + get_grand_product_challenge_set, get_grand_product_challenge_set_target, + GrandProductChallengeSet, +}; use crate::proof::*; -use crate::stark::Stark; +/// Generates challenges for a STARK proof from a challenger and given +/// all the arguments needed to update the challenger state. +/// +/// Note: `trace_cap` is passed as `Option` to signify whether to observe it +/// or not by the challenger. Observing it here could be redundant in a +/// multi-STARK system where trace caps would have already been observed +/// before proving individually each STARK. fn get_challenges( - trace_cap: &MerkleCap, + challenger: &mut Challenger, + challenges: Option<&GrandProductChallengeSet>, + trace_cap: Option<&MerkleCap>, auxiliary_polys_cap: Option<&MerkleCap>, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, @@ -33,15 +42,21 @@ where { let num_challenges = config.num_challenges; - let mut challenger = Challenger::::new(); + if let Some(cap) = &trace_cap { + challenger.observe_cap(cap); + } - challenger.observe_cap(trace_cap); + let lookup_challenge_set = if let Some(&challenges) = challenges.as_ref() { + Some(challenges.clone()) + } else { + auxiliary_polys_cap + .is_some() + .then(|| get_grand_product_challenge_set(challenger, num_challenges)) + }; - let lookup_challenge_set = auxiliary_polys_cap.map(|auxiliary_polys_cap| { - let tmp = get_grand_product_challenge_set(&mut challenger, num_challenges); - challenger.observe_cap(auxiliary_polys_cap); - tmp - }); + if let Some(cap) = &auxiliary_polys_cap { + challenger.observe_cap(cap); + } let stark_alphas = challenger.get_n_challenges(num_challenges); @@ -64,25 +79,27 @@ where } } -impl StarkProofWithPublicInputs +impl StarkProof where F: RichField + Extendable, C: GenericConfig, { - // TODO: Should be used later in compression? - #![allow(dead_code)] - pub(crate) fn fri_query_indices(&self, config: &StarkConfig, degree_bits: usize) -> Vec { - self.get_challenges(config, degree_bits) - .fri_challenges - .fri_query_indices - } - /// Computes all Fiat-Shamir challenges used in the STARK proof. - pub(crate) fn get_challenges( + /// For a single STARK system, the `ignore_trace_cap` boolean should + /// always be set to `false`. + /// + /// Multi-STARK systems may already observe individual trace caps + /// ahead of proving each table, and hence may ignore observing + /// again the cap when generating individual challenges. + pub fn get_challenges( &self, + challenger: &mut Challenger, + challenges: Option<&GrandProductChallengeSet>, + ignore_trace_cap: bool, config: &StarkConfig, - degree_bits: usize, ) -> StarkProofChallenges { + let degree_bits = self.recover_degree_bits(config); + let StarkProof { trace_cap, auxiliary_polys_cap, @@ -95,9 +112,17 @@ where pow_witness, .. }, - } = &self.proof; + } = &self; + + let trace_cap = if ignore_trace_cap { + None + } else { + Some(trace_cap) + }; get_challenges::( + challenger, + challenges, trace_cap, auxiliary_polys_cap.as_ref(), quotient_polys_cap, @@ -111,14 +136,37 @@ where } } -#[allow(clippy::too_many_arguments)] -pub(crate) fn get_challenges_target< +impl StarkProofWithPublicInputs +where F: RichField + Extendable, C: GenericConfig, - const D: usize, ->( +{ + /// Computes all Fiat-Shamir challenges used in the STARK proof. + /// For a single STARK system, the `ignore_trace_cap` boolean should + /// always be set to `false`. + /// + /// Multi-STARK systems may already observe individual trace caps + /// ahead of proving each table, and hence may ignore observing + /// again the cap when generating individual challenges. + pub fn get_challenges( + &self, + challenger: &mut Challenger, + challenges: Option<&GrandProductChallengeSet>, + ignore_trace_cap: bool, + config: &StarkConfig, + ) -> StarkProofChallenges { + self.proof + .get_challenges(challenger, challenges, ignore_trace_cap, config) + } +} + +/// Circuit version of `get_challenges`, with the same flexibility around +/// `trace_cap` being passed as an `Option`. +fn get_challenges_target( builder: &mut CircuitBuilder, - trace_cap: &MerkleCapTarget, + challenger: &mut RecursiveChallenger, + challenges: Option<&GrandProductChallengeSet>, + trace_cap: Option<&MerkleCapTarget>, auxiliary_polys_cap: Option<&MerkleCapTarget>, quotient_polys_cap: &MerkleCapTarget, openings: &StarkOpeningSetTarget, @@ -128,26 +176,34 @@ pub(crate) fn get_challenges_target< config: &StarkConfig, ) -> StarkProofChallengesTarget where + F: RichField + Extendable, + C: GenericConfig, C::Hasher: AlgebraicHasher, { let num_challenges = config.num_challenges; - let mut challenger = RecursiveChallenger::::new(builder); + if let Some(trace_cap) = trace_cap { + challenger.observe_cap(trace_cap); + } - challenger.observe_cap(trace_cap); + let lookup_challenge_set = if let Some(&challenges) = challenges.as_ref() { + Some(challenges.clone()) + } else { + auxiliary_polys_cap + .is_some() + .then(|| get_grand_product_challenge_set_target(builder, challenger, num_challenges)) + }; - let lookup_challenge_set = auxiliary_polys_cap.map(|permutation_zs_cap| { - let tmp = get_grand_product_challenge_set_target(builder, &mut challenger, num_challenges); - challenger.observe_cap(permutation_zs_cap); - tmp - }); + if let Some(cap) = auxiliary_polys_cap { + challenger.observe_cap(cap); + } let stark_alphas = challenger.get_n_challenges(builder, num_challenges); challenger.observe_cap(quotient_polys_cap); let stark_zeta = challenger.get_extension_challenge(builder); - challenger.observe_openings(&openings.to_fri_openings()); + challenger.observe_openings(&openings.to_fri_openings(builder.zero())); StarkProofChallengesTarget { lookup_challenge_set, @@ -163,10 +219,20 @@ where } } -impl StarkProofWithPublicInputsTarget { - pub(crate) fn get_challenges( +impl StarkProofTarget { + /// Creates all Fiat-Shamir `Target` challenges used in the STARK proof. + /// For a single STARK system, the `ignore_trace_cap` boolean should + /// always be set to `false`. + /// + /// Multi-STARK systems may already observe individual trace caps + /// ahead of proving each table, and hence may ignore observing + /// again the cap when generating individual challenges. + pub fn get_challenges( &self, builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, + challenges: Option<&GrandProductChallengeSet>, + ignore_trace_cap: bool, config: &StarkConfig, ) -> StarkProofChallengesTarget where @@ -186,10 +252,18 @@ impl StarkProofWithPublicInputsTarget { pow_witness, .. }, - } = &self.proof; + } = self; + + let trace_cap = if ignore_trace_cap { + None + } else { + Some(trace_cap) + }; get_challenges_target::( builder, + challenger, + challenges, trace_cap, auxiliary_polys_cap.as_ref(), quotient_polys_cap, @@ -202,6 +276,32 @@ impl StarkProofWithPublicInputsTarget { } } +impl StarkProofWithPublicInputsTarget { + /// Creates all Fiat-Shamir `Target` challenges used in the STARK proof. + /// For a single STARK system, the `ignore_trace_cap` boolean should + /// always be set to `false`. + /// + /// Multi-STARK systems may already observe individual trace caps + /// ahead of proving each table, and hence may ignore observing + /// again the cap when generating individual challenges. + pub fn get_challenges( + &self, + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, + challenges: Option<&GrandProductChallengeSet>, + ignore_trace_cap: bool, + config: &StarkConfig, + ) -> StarkProofChallengesTarget + where + F: RichField + Extendable, + C: GenericConfig, + C::Hasher: AlgebraicHasher, + { + self.proof + .get_challenges::(builder, challenger, challenges, ignore_trace_cap, config) + } +} + // TODO: Deal with the compressed stuff. // impl, C: GenericConfig, const D: usize> // CompressedProofWithPublicInputs diff --git a/starky/src/lib.rs b/starky/src/lib.rs index f6b4f5e0c7..63777fbaf2 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -1,14 +1,332 @@ +//! A FRI-based STARK implementation over the Goldilocks field, with support +//! for recursive proof verification through the plonky2 SNARK backend. +//! +//! This library is intended to provide all the necessary tools to prove, +//! verify, and recursively verify STARK statements. While the library +//! is tailored for a system with a single STARK, it also is flexible +//! enough to support a multi-STARK system, i.e. a system of independent +//! STARK statements possibly sharing common values. See section below for +//! more information on how to define such a system. +//! +//! +//! # Defining a STARK statement +//! +//! A STARK system is configured by a [`StarkConfig`][crate::config::StarkConfig] +//! defining all the parameters to be used when generating proofs associated +//! to the statement. How constraints should be defined over the STARK trace is +//! defined through the [`Stark`][crate::stark::Stark] trait, that takes a +//! [`StarkEvaluationFrame`][crate::evaluation_frame::StarkEvaluationFrame] of +//! two consecutive rows and a list of public inputs. +//! +//! ### Example: Fibonacci sequence +//! +//! To build a STARK for the modified Fibonacci sequence starting with two +//! user-provided values `x0` and `x1`, one can do the following: +//! +//! ```rust +//! # use core::marker::PhantomData; +//! // Imports all basic types. +//! use plonky2::field::extension::{Extendable, FieldExtension}; +//! use plonky2::field::packed::PackedField; +//! use plonky2::field::polynomial::PolynomialValues; +//! use plonky2::hash::hash_types::RichField; +//! # use starky::util::trace_rows_to_poly_values; +//! +//! // Imports to define the constraints of our STARK. +//! use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +//! use starky::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +//! use starky::stark::Stark; +//! +//! // Imports to define the recursive constraints of our STARK. +//! use plonky2::iop::ext_target::ExtensionTarget; +//! use plonky2::plonk::circuit_builder::CircuitBuilder; +//! +//! pub struct FibonacciStark, const D: usize> { +//! num_rows: usize, +//! _phantom: PhantomData, +//! } +//! +//! // Define witness generation. +//! impl, const D: usize> FibonacciStark { +//! // The first public input is `x0`. +//! const PI_INDEX_X0: usize = 0; +//! // The second public input is `x1`. +//! const PI_INDEX_X1: usize = 1; +//! // The third public input is the second element of the last row, +//! // which should be equal to the `num_rows`-th Fibonacci number. +//! const PI_INDEX_RES: usize = 2; +//! +//! /// Generate the trace using `x0, x1, 0` as initial state values. +//! fn generate_trace(&self, x0: F, x1: F) -> Vec> { +//! let mut trace_rows = (0..self.num_rows) +//! .scan([x0, x1, F::ZERO], |acc, _| { +//! let tmp = *acc; +//! acc[0] = tmp[1]; +//! acc[1] = tmp[0] + tmp[1]; +//! acc[2] = tmp[2] + F::ONE; +//! Some(tmp) +//! }) +//! .collect::>(); +//! +//! // Transpose the row-wise trace for the prover. +//! trace_rows_to_poly_values(trace_rows) +//! } +//! } +//! +//! // Define constraints. +//! const COLUMNS: usize = 3; +//! const PUBLIC_INPUTS: usize = 3; +//! +//! impl, const D: usize> Stark for FibonacciStark { +//! type EvaluationFrame = StarkFrame +//! where +//! FE: FieldExtension, +//! P: PackedField; +//! +//! type EvaluationFrameTarget = +//! StarkFrame, ExtensionTarget, COLUMNS, PUBLIC_INPUTS>; +//! +//! // Define this STARK's constraints. +//! fn eval_packed_generic( +//! &self, +//! vars: &Self::EvaluationFrame, +//! yield_constr: &mut ConstraintConsumer

, +//! ) where +//! FE: FieldExtension, +//! P: PackedField, +//! { +//! let local_values = vars.get_local_values(); +//! let next_values = vars.get_next_values(); +//! let public_inputs = vars.get_public_inputs(); +//! +//! // Check public inputs. +//! yield_constr.constraint_first_row(local_values[0] - public_inputs[Self::PI_INDEX_X0]); +//! yield_constr.constraint_first_row(local_values[1] - public_inputs[Self::PI_INDEX_X1]); +//! yield_constr.constraint_last_row(local_values[1] - public_inputs[Self::PI_INDEX_RES]); +//! +//! // Enforce the Fibonacci transition constraints. +//! // x0' <- x1 +//! yield_constr.constraint_transition(next_values[0] - local_values[1]); +//! // x1' <- x0 + x1 +//! yield_constr.constraint_transition(next_values[1] - local_values[0] - local_values[1]); +//! } +//! +//! // Define the constraints to recursively verify this STARK. +//! fn eval_ext_circuit( +//! &self, +//! builder: &mut CircuitBuilder, +//! vars: &Self::EvaluationFrameTarget, +//! yield_constr: &mut RecursiveConstraintConsumer, +//! ) { +//! let local_values = vars.get_local_values(); +//! let next_values = vars.get_next_values(); +//! let public_inputs = vars.get_public_inputs(); +//! +//! // Check public inputs. +//! let pis_constraints = [ +//! builder.sub_extension(local_values[0], public_inputs[Self::PI_INDEX_X0]), +//! builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_X1]), +//! builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_RES]), +//! ]; +//! +//! yield_constr.constraint_first_row(builder, pis_constraints[0]); +//! yield_constr.constraint_first_row(builder, pis_constraints[1]); +//! yield_constr.constraint_last_row(builder, pis_constraints[2]); +//! +//! // Enforce the Fibonacci transition constraints. +//! // x0' <- x1 +//! let first_col_constraint = builder.sub_extension(next_values[0], local_values[1]); +//! yield_constr.constraint_transition(builder, first_col_constraint); +//! // x1' <- x0 + x1 +//! let second_col_constraint = { +//! let tmp = builder.sub_extension(next_values[1], local_values[0]); +//! builder.sub_extension(tmp, local_values[1]) +//! }; +//! yield_constr.constraint_transition(builder, second_col_constraint); +//! } +//! +//! fn constraint_degree(&self) -> usize { +//! 2 +//! } +//! } +//! ``` +//! +//! One can then instantiate a new `FibonacciStark` instance, generate an associated +//! STARK trace, and generate a proof for it. +//! +//! ```rust +//! # use anyhow::Result; +//! # use core::marker::PhantomData; +//! # // Imports all basic types. +//! # use plonky2::field::extension::{Extendable, FieldExtension}; +//! # use plonky2::field::types::Field; +//! # use plonky2::field::packed::PackedField; +//! # use plonky2::field::polynomial::PolynomialValues; +//! # use plonky2::hash::hash_types::RichField; +//! # use starky::util::trace_rows_to_poly_values; +//! # // Imports to define the constraints of our STARK. +//! # use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +//! # use starky::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +//! # use starky::stark::Stark; +//! # // Imports to define the recursive constraints of our STARK. +//! # use plonky2::iop::ext_target::ExtensionTarget; +//! # use plonky2::plonk::circuit_builder::CircuitBuilder; +//! # use plonky2::util::timing::TimingTree; +//! # use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; +//! # use starky::prover::prove; +//! # use starky::verifier::verify_stark_proof; +//! # use starky::config::StarkConfig; +//! # +//! # #[derive(Copy, Clone)] +//! # pub struct FibonacciStark, const D: usize> { +//! # num_rows: usize, +//! # _phantom: PhantomData, +//! # } +//! # // Define witness generation. +//! # impl, const D: usize> FibonacciStark { +//! # // The first public input is `x0`. +//! # const PI_INDEX_X0: usize = 0; +//! # // The second public input is `x1`. +//! # const PI_INDEX_X1: usize = 1; +//! # // The third public input is the second element of the last row, +//! # // which should be equal to the `num_rows`-th Fibonacci number. +//! # const PI_INDEX_RES: usize = 2; +//! # /// Generate the trace using `x0, x1, 0` as initial state values. +//! # fn generate_trace(&self, x0: F, x1: F) -> Vec> { +//! # let mut trace_rows = (0..self.num_rows) +//! # .scan([x0, x1, F::ZERO], |acc, _| { +//! # let tmp = *acc; +//! # acc[0] = tmp[1]; +//! # acc[1] = tmp[0] + tmp[1]; +//! # acc[2] = tmp[2] + F::ONE; +//! # Some(tmp) +//! # }) +//! # .collect::>(); +//! # // Transpose the row-wise trace for the prover. +//! # trace_rows_to_poly_values(trace_rows) +//! # } +//! # const fn new(num_rows: usize) -> Self { +//! # Self { +//! # num_rows, +//! # _phantom: PhantomData, +//! # } +//! # } +//! # } +//! # // Define constraints. +//! # const COLUMNS: usize = 3; +//! # const PUBLIC_INPUTS: usize = 3; +//! # impl, const D: usize> Stark for FibonacciStark { +//! # type EvaluationFrame = StarkFrame +//! # where +//! # FE: FieldExtension, +//! # P: PackedField; +//! # type EvaluationFrameTarget = +//! # StarkFrame, ExtensionTarget, COLUMNS, PUBLIC_INPUTS>; +//! # // Define this STARK's constraints. +//! # fn eval_packed_generic( +//! # &self, +//! # vars: &Self::EvaluationFrame, +//! # yield_constr: &mut ConstraintConsumer

, +//! # ) where +//! # FE: FieldExtension, +//! # P: PackedField, +//! # { +//! # let local_values = vars.get_local_values(); +//! # let next_values = vars.get_next_values(); +//! # let public_inputs = vars.get_public_inputs(); +//! # // Check public inputs. +//! # yield_constr.constraint_first_row(local_values[0] - public_inputs[Self::PI_INDEX_X0]); +//! # yield_constr.constraint_first_row(local_values[1] - public_inputs[Self::PI_INDEX_X1]); +//! # yield_constr.constraint_last_row(local_values[1] - public_inputs[Self::PI_INDEX_RES]); +//! # // Enforce the Fibonacci transition constraints. +//! # // x0' <- x1 +//! # yield_constr.constraint_transition(next_values[0] - local_values[1]); +//! # // x1' <- x0 + x1 +//! # yield_constr.constraint_transition(next_values[1] - local_values[0] - local_values[1]); +//! # } +//! # // Define the constraints to recursively verify this STARK. +//! # fn eval_ext_circuit( +//! # &self, +//! # builder: &mut CircuitBuilder, +//! # vars: &Self::EvaluationFrameTarget, +//! # yield_constr: &mut RecursiveConstraintConsumer, +//! # ) { +//! # let local_values = vars.get_local_values(); +//! # let next_values = vars.get_next_values(); +//! # let public_inputs = vars.get_public_inputs(); +//! # // Check public inputs. +//! # let pis_constraints = [ +//! # builder.sub_extension(local_values[0], public_inputs[Self::PI_INDEX_X0]), +//! # builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_X1]), +//! # builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_RES]), +//! # ]; +//! # yield_constr.constraint_first_row(builder, pis_constraints[0]); +//! # yield_constr.constraint_first_row(builder, pis_constraints[1]); +//! # yield_constr.constraint_last_row(builder, pis_constraints[2]); +//! # // Enforce the Fibonacci transition constraints. +//! # // x0' <- x1 +//! # let first_col_constraint = builder.sub_extension(next_values[0], local_values[1]); +//! # yield_constr.constraint_transition(builder, first_col_constraint); +//! # // x1' <- x0 + x1 +//! # let second_col_constraint = { +//! # let tmp = builder.sub_extension(next_values[1], local_values[0]); +//! # builder.sub_extension(tmp, local_values[1]) +//! # }; +//! # yield_constr.constraint_transition(builder, second_col_constraint); +//! # } +//! # fn constraint_degree(&self) -> usize { +//! # 2 +//! # } +//! # } +//! # fn fibonacci(n: usize, x0: F, x1: F) -> F { +//! # (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 +//! # } +//! # +//! const D: usize = 2; +//! const CONFIG: StarkConfig = StarkConfig::standard_fast_config(); +//! type C = PoseidonGoldilocksConfig; +//! type F = >::F; +//! type S = FibonacciStark; +//! +//! fn main() { +//! let num_rows = 1 << 10; +//! let x0 = F::from_canonical_u32(2); +//! let x1 = F::from_canonical_u32(7); +//! +//! let public_inputs = [x0, x1, fibonacci(num_rows - 1, x0, x1)]; +//! let stark = FibonacciStark::::new(num_rows); +//! let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); +//! +//! let proof = prove::( +//! stark, +//! &CONFIG, +//! trace, +//! &public_inputs, +//! &mut TimingTree::default(), +//! ).expect("We should have a valid proof!"); +//! +//! verify_stark_proof(stark, proof, &CONFIG) +//! .expect("We should be able to verify this proof!") +//! } +//! ``` +//! + #![allow(clippy::too_many_arguments)] +#![allow(clippy::needless_range_loop)] #![allow(clippy::type_complexity)] -#![allow(unused)] // TODO: Remove post code migration +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] extern crate alloc; mod get_challenges; pub mod config; pub mod constraint_consumer; +pub mod cross_table_lookup; pub mod evaluation_frame; pub mod lookup; pub mod proof; @@ -17,7 +335,7 @@ pub mod recursive_verifier; pub mod stark; pub mod stark_testing; pub mod util; -pub mod vanishing_poly; +mod vanishing_poly; pub mod verifier; #[cfg(test)] diff --git a/starky/src/lookup.rs b/starky/src/lookup.rs index 19f2042481..80a01b0859 100644 --- a/starky/src/lookup.rs +++ b/starky/src/lookup.rs @@ -1,5 +1,8 @@ -use alloc::vec; -use alloc::vec::Vec; +//! A Lookup protocol leveraging logarithmic derivatives, +//! introduced in . + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use core::borrow::Borrow; use core::fmt::Debug; use core::iter::repeat; @@ -37,6 +40,7 @@ pub struct Filter { } impl Filter { + /// Returns a filter from the provided `products` and `constants` vectors. pub fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { Self { products, @@ -113,14 +117,6 @@ impl Filter { .map(|col| col.eval_table(table, row)) .sum() } - - pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { - let length = table[0].len(); - - (0..length) - .map(|row| self.eval_table(table, row)) - .collect::>() - } } /// Represent two linear combination of columns, corresponding to the current and next row values. @@ -402,12 +398,24 @@ impl Column { pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); +/// A [`Lookup`] defines a set of `columns`` whose values should appear in a +/// `table_column` (i.e. the lookup table associated to these looking columns), +/// along with a `frequencies_column` indicating the frequency of each looking +/// column in the looked table. +/// +/// It also features a `filter_columns` vector, optionally adding at most one +/// filter per looking column. +/// +/// The lookup argumented implemented here is based on logarithmic derivatives, +/// a technique described with the whole lookup protocol in +/// . +#[derive(Debug)] pub struct Lookup { /// Columns whose values should be contained in the lookup table. /// These are the f_i(x) polynomials in the logUp paper. pub columns: Vec>, /// Column containing the lookup table. - /// This is the t(x) polynomial in the paper. + /// This is the t(x) polynomial in the logUp paper. pub table_column: Column, /// Column containing the frequencies of `columns` in `table_column`. /// This is the m(x) polynomial in the paper. @@ -419,6 +427,7 @@ pub struct Lookup { } impl Lookup { + /// Outputs the number of helper columns needed by this [`Lookup`]. pub fn num_helper_columns(&self, constraint_degree: usize) -> usize { // One helper column for each column batch of size `constraint_degree-1`, // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. @@ -428,18 +437,18 @@ impl Lookup { /// Randomness for a single instance of a permutation check protocol. #[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) struct GrandProductChallenge { +pub struct GrandProductChallenge { /// Randomness used to combine multiple columns into one. - pub(crate) beta: T, + pub beta: T, /// Random offset that's added to the beta-reduced column values. - pub(crate) gamma: T, + pub gamma: T, } impl GrandProductChallenge { - pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( - &self, - terms: T, - ) -> P + /// Combines a series of values `t_i` with these challenge random values. + /// In particular, given `beta` and `gamma` challenges, this will compute + /// `(Σ t_i * beta^i) + gamma`. + pub fn combine<'a, FE, P, T: IntoIterator, const D2: usize>(&self, terms: T) -> P where FE: FieldExtension, P: PackedField, @@ -462,7 +471,8 @@ impl GrandProductChallenge { } impl GrandProductChallenge { - pub(crate) fn combine_base_circuit, const D: usize>( + /// Circuit version of `combine`. + pub fn combine_base_circuit, const D: usize>( &self, builder: &mut CircuitBuilder, terms: &[Target], @@ -475,11 +485,14 @@ impl GrandProductChallenge { /// Like `GrandProductChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone, Eq, PartialEq, Debug)] pub struct GrandProductChallengeSet { - pub(crate) challenges: Vec>, + /// A sequence of `num_challenges` challenge pairs, where `num_challenges` + /// is defined in [`StarkConfig`][crate::config::StarkConfig]. + pub challenges: Vec>, } impl GrandProductChallengeSet { - pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + /// Serializes this `GrandProductChallengeSet` of `Target`s. + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { buffer.write_usize(self.challenges.len())?; for challenge in &self.challenges { buffer.write_target(challenge.beta)?; @@ -488,7 +501,8 @@ impl GrandProductChallengeSet { Ok(()) } - pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { + /// Serializes a `GrandProductChallengeSet` of `Target`s from the provided buffer. + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { let length = buffer.read_usize()?; let mut challenges = Vec::with_capacity(length); for _ in 0..length { @@ -510,7 +524,9 @@ fn get_grand_product_challenge>( GrandProductChallenge { beta, gamma } } -pub(crate) fn get_grand_product_challenge_set>( +/// Generates a new `GrandProductChallengeSet` containing `num_challenges` +/// pairs of challenges from the current `challenger` state. +pub fn get_grand_product_challenge_set>( challenger: &mut Challenger, num_challenges: usize, ) -> GrandProductChallengeSet { @@ -533,7 +549,8 @@ fn get_grand_product_challenge_target< GrandProductChallenge { beta, gamma } } -pub(crate) fn get_grand_product_challenge_set_target< +/// Circuit version of `get_grand_product_challenge_set`. +pub fn get_grand_product_challenge_set_target< F: RichField + Extendable, H: AlgebraicHasher, const D: usize, @@ -570,7 +587,6 @@ pub(crate) fn lookup_helper_columns( assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); let num_helper_columns = lookup.num_helper_columns(constraint_degree); - let mut helper_columns: Vec> = Vec::with_capacity(num_helper_columns); let looking_cols = lookup .columns @@ -762,7 +778,6 @@ pub(crate) fn get_helper_cols( let mut helper_columns = Vec::with_capacity(num_helper_columns); - let mut filter_index = 0; for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { let (first_col, first_filter) = cols_filts.next().unwrap(); @@ -842,6 +857,7 @@ pub(crate) fn get_helper_cols( helper_columns } +#[derive(Debug)] pub(crate) struct LookupCheckVars where F: Field, @@ -919,6 +935,7 @@ pub(crate) fn eval_packed_lookups_generic { pub(crate) local_values: Vec>, pub(crate) next_values: Vec>, @@ -936,7 +953,6 @@ pub(crate) fn eval_ext_lookups_circuit< lookup_vars: LookupCheckVarsTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let one = builder.one_extension(); let degree = stark.constraint_degree(); let lookups = stark.lookups(); diff --git a/starky/src/proof.rs b/starky/src/proof.rs index e22399288e..b6ea53efc9 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -1,5 +1,9 @@ -use alloc::vec; -use alloc::vec::Vec; +//! All the different proof types and their associated `circuit` versions +//! to be used when proving (recursive) [`Stark`][crate::stark::Stark] +//! statements + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; @@ -14,17 +18,19 @@ use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; -use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::lookup::GrandProductChallengeSet; +/// Merkle caps and openings that form the proof of a single STARK. #[derive(Debug, Clone)] pub struct StarkProof, C: GenericConfig, const D: usize> { /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, - /// Merkle cap of LDEs of permutation Z values. + /// Optional merkle cap of LDEs of permutation Z values, if any. pub auxiliary_polys_cap: Option>, /// Merkle cap of LDEs of trace values. pub quotient_polys_cap: MerkleCap, @@ -46,15 +52,57 @@ impl, C: GenericConfig, const D: usize> S } } +/// Circuit version of [`StarkProof`]. +/// Merkle caps and openings that form the proof of a single STARK. +#[derive(Clone, Debug, PartialEq, Eq)] pub struct StarkProofTarget { + /// `Target` for the Merkle cap trace values LDEs. pub trace_cap: MerkleCapTarget, + /// Optional `Target` for the Merkle cap of lookup helper and CTL columns LDEs, if any. pub auxiliary_polys_cap: Option, + /// `Target` for the Merkle cap of quotient polynomial evaluations LDEs. pub quotient_polys_cap: MerkleCapTarget, + /// `Target`s for the purported values of each polynomial at the challenge point. pub openings: StarkOpeningSetTarget, + /// `Target`s for the batch FRI argument for all openings. pub opening_proof: FriProofTarget, } impl StarkProofTarget { + /// Serializes a STARK proof. + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_merkle_cap(&self.trace_cap)?; + buffer.write_bool(self.auxiliary_polys_cap.is_some())?; + if let Some(poly) = &self.auxiliary_polys_cap { + buffer.write_target_merkle_cap(poly)?; + } + buffer.write_target_merkle_cap(&self.quotient_polys_cap)?; + buffer.write_target_fri_proof(&self.opening_proof)?; + self.openings.to_buffer(buffer)?; + Ok(()) + } + + /// Deserializes a STARK proof. + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let trace_cap = buffer.read_target_merkle_cap()?; + let auxiliary_polys_cap = if buffer.read_bool()? { + Some(buffer.read_target_merkle_cap()?) + } else { + None + }; + let quotient_polys_cap = buffer.read_target_merkle_cap()?; + let opening_proof = buffer.read_target_fri_proof()?; + let openings = StarkOpeningSetTarget::from_buffer(buffer)?; + + Ok(Self { + trace_cap, + auxiliary_polys_cap, + quotient_polys_cap, + openings, + opening_proof, + }) + } + /// Recover the length of the trace from a STARK proof and a STARK config. pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] @@ -66,22 +114,31 @@ impl StarkProofTarget { } } +/// Merkle caps and openings that form the proof of a single STARK, along with its public inputs. #[derive(Debug, Clone)] pub struct StarkProofWithPublicInputs< F: RichField + Extendable, C: GenericConfig, const D: usize, > { + /// A STARK proof. pub proof: StarkProof, + /// Public inputs associated to this STARK proof. // TODO: Maybe make it generic over a `S: Stark` and replace with `[F; S::PUBLIC_INPUTS]`. pub public_inputs: Vec, } +/// Circuit version of [`StarkProofWithPublicInputs`]. +#[derive(Debug, Clone)] pub struct StarkProofWithPublicInputsTarget { + /// `Target` STARK proof. pub proof: StarkProofTarget, + /// `Target` public inputs for this STARK proof. pub public_inputs: Vec, } +/// A compressed proof format of a single STARK. +#[derive(Debug, Clone)] pub struct CompressedStarkProof< F: RichField + Extendable, C: GenericConfig, @@ -95,69 +152,158 @@ pub struct CompressedStarkProof< pub opening_proof: CompressedFriProof, } +/// A compressed [`StarkProof`] format of a single STARK with its public inputs. +#[derive(Debug, Clone)] pub struct CompressedStarkProofWithPublicInputs< F: RichField + Extendable, C: GenericConfig, const D: usize, > { + /// A compressed STARK proof. pub proof: CompressedStarkProof, + /// Public inputs for this compressed STARK proof. pub public_inputs: Vec, } -pub(crate) struct StarkProofChallenges, const D: usize> { - /// Randomness used in any permutation arguments. - pub lookup_challenge_set: Option>, +/// A [`StarkProof`] along with metadata about the initial Fiat-Shamir state, which is used when +/// creating a recursive wrapper proof around a STARK proof. +#[derive(Debug, Clone)] +pub struct StarkProofWithMetadata +where + F: RichField + Extendable, + C: GenericConfig, +{ + /// Initial Fiat-Shamir state. + pub init_challenger_state: >::Permutation, + /// Proof for a single STARK. + pub proof: StarkProof, +} + +/// A combination of STARK proofs for independent statements operating on possibly shared variables, +/// along with Cross-Table Lookup (CTL) challenges to assert consistency of common variables across tables. +#[derive(Debug, Clone)] +pub struct MultiProof< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + const N: usize, +> { + /// Proofs for all the different STARK modules. + pub stark_proofs: [StarkProofWithMetadata; N], + /// Cross-table lookup challenges. + pub ctl_challenges: GrandProductChallengeSet, +} +impl, C: GenericConfig, const D: usize, const N: usize> + MultiProof +{ + /// Returns the degree (i.e. the trace length) of each STARK proof, + /// from their common [`StarkConfig`]. + pub fn recover_degree_bits(&self, config: &StarkConfig) -> [usize; N] { + core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config)) + } +} + +/// Randomness used for a STARK proof. +#[derive(Debug)] +pub struct StarkProofChallenges, const D: usize> { + /// Optional randomness used in any permutation argument. + pub lookup_challenge_set: Option>, /// Random values used to combine STARK constraints. pub stark_alphas: Vec, - /// Point at which the STARK polynomials are opened. pub stark_zeta: F::Extension, - + /// Randomness used in FRI. pub fri_challenges: FriChallenges, } -pub(crate) struct StarkProofChallengesTarget { +/// Circuit version of [`StarkProofChallenges`]. +#[derive(Debug)] +pub struct StarkProofChallengesTarget { + /// Optional `Target`'s randomness used in any permutation argument. pub lookup_challenge_set: Option>, + /// `Target`s for the random values used to combine STARK constraints. pub stark_alphas: Vec, + /// `ExtensionTarget` for the point at which the STARK polynomials are opened. pub stark_zeta: ExtensionTarget, + /// `Target`s for the randomness used in FRI. pub fri_challenges: FriChallengesTarget, } +/// Randomness for all STARK proofs contained in a [`MultiProof`]`. +#[derive(Debug)] +pub struct MultiProofChallenges, const D: usize, const N: usize> { + /// Randomness used in each STARK proof. + pub stark_challenges: [StarkProofChallenges; N], + /// Randomness used for cross-table lookups. It is shared by all STARKs. + pub ctl_challenges: GrandProductChallengeSet, +} + /// Purported values of each polynomial at the challenge point. #[derive(Debug, Clone)] pub struct StarkOpeningSet, const D: usize> { + /// Openings of trace polynomials at `zeta`. pub local_values: Vec, + /// Openings of trace polynomials at `g * zeta`. pub next_values: Vec, + /// Openings of lookups and cross-table lookups `Z` polynomials at `zeta`. pub auxiliary_polys: Option>, + /// Openings of lookups and cross-table lookups `Z` polynomials at `g * zeta`. pub auxiliary_polys_next: Option>, + /// Openings of cross-table lookups `Z` polynomials at `1`. + pub ctl_zs_first: Option>, + /// Openings of quotient polynomials at `zeta`. pub quotient_polys: Vec, } impl, const D: usize> StarkOpeningSet { + /// Returns a `StarkOpeningSet` given all the polynomial commitments, the number + /// of permutation `Z`polynomials, the evaluation point and a generator `g`. + /// + /// Polynomials are evaluated at point `zeta` and, if necessary, at `g * zeta`. pub fn new>( zeta: F::Extension, g: F, trace_commitment: &PolynomialBatch, auxiliary_polys_commitment: Option<&PolynomialBatch>, quotient_commitment: &PolynomialBatch, + num_lookup_columns: usize, + requires_ctl: bool, + num_ctl_polys: &[usize], ) -> Self { + // Batch evaluates polynomials on the LDE, at a point `z`. let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { c.polynomials .par_iter() .map(|p| p.to_extension().eval(z)) .collect::>() }; + // Batch evaluates polynomials at a base field point `z`. + let eval_commitment_base = |z: F, c: &PolynomialBatch| { + c.polynomials + .par_iter() + .map(|p| p.eval(z)) + .collect::>() + }; + + let auxiliary_first = auxiliary_polys_commitment.map(|c| eval_commitment_base(F::ONE, c)); + // `g * zeta`. let zeta_next = zeta.scalar_mul(g); Self { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta_next, trace_commitment), auxiliary_polys: auxiliary_polys_commitment.map(|c| eval_commitment(zeta, c)), auxiliary_polys_next: auxiliary_polys_commitment.map(|c| eval_commitment(zeta_next, c)), + ctl_zs_first: requires_ctl.then(|| { + let total_num_helper_cols: usize = num_ctl_polys.iter().sum(); + auxiliary_first.unwrap()[num_lookup_columns + total_num_helper_cols..].to_vec() + }), quotient_polys: eval_commitment(zeta, quotient_commitment), } } + /// Constructs the openings required by FRI. + /// All openings but `ctl_zs_first` are grouped together. pub(crate) fn to_fri_openings(&self) -> FriOpenings { let zeta_batch = FriOpeningBatch { values: self @@ -176,22 +322,107 @@ impl, const D: usize> StarkOpeningSet { .copied() .collect_vec(), }; - FriOpenings { - batches: vec![zeta_batch, zeta_next_batch], + + let mut batches = vec![zeta_batch, zeta_next_batch]; + + if let Some(ctl_zs_first) = self.ctl_zs_first.as_ref() { + debug_assert!(!ctl_zs_first.is_empty()); + debug_assert!(self.auxiliary_polys.is_some()); + debug_assert!(self.auxiliary_polys_next.is_some()); + + let ctl_first_batch = FriOpeningBatch { + values: ctl_zs_first + .iter() + .copied() + .map(F::Extension::from_basefield) + .collect(), + }; + + batches.push(ctl_first_batch); } + + FriOpenings { batches } } } +/// Circuit version of [`StarkOpeningSet`]. +/// `Target`s for the purported values of each polynomial at the challenge point. +#[derive(Clone, Debug, PartialEq, Eq)] pub struct StarkOpeningSetTarget { + /// `ExtensionTarget`s for the openings of trace polynomials at `zeta`. pub local_values: Vec>, + /// `ExtensionTarget`s for the opening of trace polynomials at `g * zeta`. pub next_values: Vec>, + /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `zeta`. pub auxiliary_polys: Option>>, + /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at `g * zeta`. pub auxiliary_polys_next: Option>>, + /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1. + pub ctl_zs_first: Option>, + /// `ExtensionTarget`s for the opening of quotient polynomials at `zeta`. pub quotient_polys: Vec>, } impl StarkOpeningSetTarget { - pub(crate) fn to_fri_openings(&self) -> FriOpeningsTarget { + /// Serializes a STARK's opening set. + pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target_ext_vec(&self.local_values)?; + buffer.write_target_ext_vec(&self.next_values)?; + if let Some(poly) = &self.auxiliary_polys { + buffer.write_bool(true)?; + buffer.write_target_ext_vec(poly)?; + } else { + buffer.write_bool(false)?; + } + if let Some(poly_next) = &self.auxiliary_polys_next { + buffer.write_bool(true)?; + buffer.write_target_ext_vec(poly_next)?; + } else { + buffer.write_bool(false)?; + } + if let Some(ctl_zs_first) = &self.ctl_zs_first { + buffer.write_bool(true)?; + buffer.write_target_vec(ctl_zs_first)?; + } else { + buffer.write_bool(false)?; + } + buffer.write_target_ext_vec(&self.quotient_polys)?; + Ok(()) + } + + /// Deserializes a STARK's opening set. + pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { + let local_values = buffer.read_target_ext_vec::()?; + let next_values = buffer.read_target_ext_vec::()?; + let auxiliary_polys = if buffer.read_bool()? { + Some(buffer.read_target_ext_vec::()?) + } else { + None + }; + let auxiliary_polys_next = if buffer.read_bool()? { + Some(buffer.read_target_ext_vec::()?) + } else { + None + }; + let ctl_zs_first = if buffer.read_bool()? { + Some(buffer.read_target_vec()?) + } else { + None + }; + let quotient_polys = buffer.read_target_ext_vec::()?; + + Ok(Self { + local_values, + next_values, + auxiliary_polys, + auxiliary_polys_next, + ctl_zs_first, + quotient_polys, + }) + } + + /// Circuit version of `to_fri_openings`for [`FriOpeningsTarget`]. + pub(crate) fn to_fri_openings(&self, zero: Target) -> FriOpeningsTarget { let zeta_batch = FriOpeningBatchTarget { values: self .local_values @@ -209,8 +440,24 @@ impl StarkOpeningSetTarget { .copied() .collect_vec(), }; - FriOpeningsTarget { - batches: vec![zeta_batch, zeta_next_batch], + + let mut batches = vec![zeta_batch, zeta_next_batch]; + + if let Some(ctl_zs_first) = self.ctl_zs_first.as_ref() { + debug_assert!(!ctl_zs_first.is_empty()); + debug_assert!(self.auxiliary_polys.is_some()); + debug_assert!(self.auxiliary_polys_next.is_some()); + + let ctl_first_batch = FriOpeningBatchTarget { + values: ctl_zs_first + .iter() + .copied() + .map(|t| t.to_ext_target(zero)) + .collect(), + }; + + batches.push(ctl_first_batch); } + FriOpeningsTarget { batches } } } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index f9b40217d6..7014bdd34d 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -1,3 +1,6 @@ +//! Implementation of the STARK prover. + +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::iter::once; @@ -20,15 +23,17 @@ use plonky2_maybe_rayon::*; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; +use crate::cross_table_lookup::{get_ctl_auxiliary_polys, CtlCheckVars, CtlData}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::{ - get_grand_product_challenge_set, lookup_helper_columns, Lookup, LookupCheckVars, + get_grand_product_challenge_set, lookup_helper_columns, GrandProductChallengeSet, Lookup, + LookupCheckVars, }; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -#[allow(clippy::useless_asref)] +/// From a STARK trace, computes a STARK proof to attest its correctness. pub fn prove( stark: S, config: &StarkConfig, @@ -68,54 +73,120 @@ where let mut challenger = Challenger::new(); challenger.observe_cap(&trace_cap); - // Lookup argument. + prove_with_commitment( + &stark, + config, + &trace_poly_values, + &trace_commitment, + None, + None, + &mut challenger, + public_inputs, + timing, + ) +} + +/// Generates a proof for a single STARK table, including: +/// +/// - the initial state of the challenger, +/// - all the required Merkle caps, +/// - all the required polynomial and FRI argument openings. +/// - individual `ctl_data` and common `ctl_challenges` if the STARK is part +/// of a multi-STARK system. +pub fn prove_with_commitment( + stark: &S, + config: &StarkConfig, + trace_poly_values: &[PolynomialValues], + trace_commitment: &PolynomialBatch, + ctl_data: Option<&CtlData>, + ctl_challenges: Option<&GrandProductChallengeSet>, + challenger: &mut Challenger, + public_inputs: &[F], + timing: &mut TimingTree, +) -> Result> +where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + let degree = trace_poly_values[0].len(); + let degree_bits = log2_strict(degree); + let fri_params = config.fri_params(degree_bits); + let rate_bits = config.fri_config.rate_bits; + let cap_height = config.fri_config.cap_height; + assert!( + fri_params.total_arities() <= degree_bits + rate_bits - cap_height, + "FRI total reduction arity is too large.", + ); + + // Permutation arguments. + let constraint_degree = stark.constraint_degree(); - let lookups = stark.lookups(); let lookup_challenges = stark.uses_lookups().then(|| { - get_grand_product_challenge_set(&mut challenger, config.num_challenges) - .challenges - .iter() - .map(|ch| ch.beta) - .collect::>() + if let Some(c) = ctl_challenges { + c.challenges.iter().map(|ch| ch.beta).collect::>() + } else { + get_grand_product_challenge_set(challenger, config.num_challenges) + .challenges + .iter() + .map(|ch| ch.beta) + .collect::>() + } }); - let num_lookup_columns = lookups - .iter() - .map(|l| l.num_helper_columns(constraint_degree)) - .sum(); - - let auxiliary_polys_commitment = stark.uses_lookups().then(|| { - let lookup_helper_columns = timed!(timing, "compute lookup helper columns", { - let challenges = lookup_challenges.as_ref().expect("We do have challenges."); - let mut columns = Vec::with_capacity(num_lookup_columns); + let lookups = stark.lookups(); + let lookup_helper_columns = timed!( + timing, + "compute lookup helper columns", + lookup_challenges.as_ref().map(|challenges| { + let mut columns = Vec::new(); for lookup in &lookups { for &challenge in challenges { columns.extend(lookup_helper_columns( lookup, - &trace_poly_values, + trace_poly_values, challenge, constraint_degree, )); } } columns - }); + }) + ); + let num_lookup_columns = lookup_helper_columns.as_ref().map_or(0, |v| v.len()); + + // We add CTLs, if there are any, to the permutation arguments so that + // we can batch commit to all auxiliary polynomials. + let auxiliary_polys = match lookup_helper_columns { + None => get_ctl_auxiliary_polys(ctl_data), + Some(mut lookup_columns) => { + if let Some(p) = get_ctl_auxiliary_polys(ctl_data) { + lookup_columns.extend(p) + }; + + Some(lookup_columns) + } + }; + + debug_assert!( + (stark.uses_lookups() || stark.requires_ctls()) || auxiliary_polys.is_none(), + "There should be auxiliary polynomials if and only if we have either lookups or require cross-table lookups." + ); - // Get the polynomial commitments for all auxiliary polynomials. - let auxiliary_polys_commitment = timed!( + // Get the polynomial commitments for all auxiliary polynomials. + let auxiliary_polys_commitment = auxiliary_polys.map(|aux_polys| { + timed!( timing, - "compute permutation Z commitments", + "compute auxiliary polynomials commitment", PolynomialBatch::from_values( - lookup_helper_columns, + aux_polys, rate_bits, false, config.fri_config.cap_height, timing, None, ) - ); - - auxiliary_polys_commitment + ) }); let auxiliary_polys_cap = auxiliary_polys_commitment @@ -127,18 +198,25 @@ where let alphas = challenger.get_n_challenges(config.num_challenges); - #[cfg(test)] + let num_ctl_polys = ctl_data + .map(|data| data.num_ctl_helper_polys()) + .unwrap_or_default(); + + // This is an expensive check, hence is only run when `debug_assertions` are enabled. + #[cfg(debug_assertions)] { check_constraints( - &stark, - &trace_commitment, + stark, + trace_commitment, public_inputs, &auxiliary_polys_commitment, lookup_challenges.as_ref(), &lookups, + ctl_data, alphas.clone(), degree_bits, num_lookup_columns, + &num_ctl_polys, ); } @@ -146,19 +224,20 @@ where timing, "compute quotient polys", compute_quotient_polys::::Packing, C, S, D>( - &stark, - &trace_commitment, + stark, + trace_commitment, &auxiliary_polys_commitment, lookup_challenges.as_ref(), &lookups, + ctl_data, public_inputs, - alphas, + alphas.clone(), degree_bits, num_lookup_columns, + &num_ctl_polys, config, ) ); - let all_quotient_chunks = timed!( timing, "split quotient polys", @@ -175,7 +254,7 @@ where }) .collect() ); - + // Commit to the quotient polynomials. let quotient_commitment = timed!( timing, "compute quotient commitment", @@ -188,12 +267,12 @@ where None, ) ); - // Observe the quotient polynomials Merkle cap. let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); challenger.observe_cap("ient_polys_cap); let zeta = challenger.get_extension_challenge::(); + // To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and // `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since // `(g * zeta)^n = zeta^n`, where `n` is the order of `g`. @@ -207,15 +286,17 @@ where let openings = StarkOpeningSet::new( zeta, g, - &trace_commitment, + trace_commitment, auxiliary_polys_commitment.as_ref(), "ient_commitment, + stark.num_lookup_helper_columns(config), + stark.requires_ctls(), + &num_ctl_polys, ); - // Get the FRI openings and observe them. challenger.observe_openings(&openings.to_fri_openings()); - let initial_merkle_trees = once(&trace_commitment) + let initial_merkle_trees = once(trace_commitment) .chain(&auxiliary_polys_commitment) .chain(once("ient_commitment)) .collect_vec(); @@ -224,15 +305,16 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, config), + &stark.fri_instance(zeta, g, num_ctl_polys.iter().sum(), num_ctl_polys, config), &initial_merkle_trees, - &mut challenger, + challenger, &fri_params, timing, ) ); + let proof = StarkProof { - trace_cap, + trace_cap: trace_commitment.merkle_tree.cap.clone(), auxiliary_polys_cap, quotient_polys_cap, openings, @@ -246,17 +328,19 @@ where } /// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, -/// where the `C_i`s are the Stark constraints. +/// where the `C_i`s are the STARK constraints. fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, auxiliary_polys_commitment: &'a Option>, lookup_challenges: Option<&'a Vec>, lookups: &[Lookup], + ctl_data: Option<&CtlData>, public_inputs: &[F], alphas: Vec, degree_bits: usize, num_lookup_columns: usize, + num_ctl_columns: &[usize], config: &StarkConfig, ) -> Vec> where @@ -267,6 +351,7 @@ where { let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; + let total_num_helper_cols: usize = num_ctl_columns.iter().sum(); let quotient_degree_bits = log2_ceil(stark.quotient_degree_factor()); assert!( @@ -331,15 +416,62 @@ where local_values: auxiliary_polys_commitment .as_ref() .unwrap() - .get_lde_values_packed(i_start, step) + .get_lde_values_packed(i_start, step)[..num_lookup_columns] .to_vec(), next_values: auxiliary_polys_commitment .as_ref() .unwrap() - .get_lde_values_packed(i_next_start, step), + .get_lde_values_packed(i_next_start, step)[..num_lookup_columns] + .to_vec(), challenges: challenges.to_vec(), }); + // Get all the data for this STARK's CTLs, if any: + // - the local and next row evaluations for the CTL Z polynomials + // - the associated challenges. + // - for each CTL: + // - the filter `Column` + // - the `Column`s that form the looking/looked table. + + let ctl_vars = ctl_data.map(|data| { + let mut start_index = 0; + data.zs_columns + .iter() + .enumerate() + .map(|(i, zs_columns)| { + let num_ctl_helper_cols = num_ctl_columns[i]; + let helper_columns = auxiliary_polys_commitment + .as_ref() + .unwrap() + .get_lde_values_packed(i_start, step) + [num_lookup_columns + start_index + ..num_lookup_columns + start_index + num_ctl_helper_cols] + .to_vec(); + + let ctl_vars = CtlCheckVars:: { + helper_columns, + local_z: auxiliary_polys_commitment + .as_ref() + .unwrap() + .get_lde_values_packed(i_start, step) + [num_lookup_columns + total_num_helper_cols + i], + next_z: auxiliary_polys_commitment + .as_ref() + .unwrap() + .get_lde_values_packed(i_next_start, step) + [num_lookup_columns + total_num_helper_cols + i], + challenges: zs_columns.challenge, + columns: zs_columns.columns.clone(), + filter: zs_columns.filter.clone(), + }; + + start_index += num_ctl_helper_cols; + + ctl_vars + }) + .collect::>() + }); + // Evaluate the polynomial combining all constraints, including // those associated to the permutation arguments. eval_vanishing_poly::( @@ -347,6 +479,7 @@ where &vars, lookups, lookup_vars, + ctl_vars.as_deref(), &mut consumer, ); @@ -375,9 +508,15 @@ where .collect() } -#[cfg(test)] /// Check that all constraints evaluate to zero on `H`. /// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. +/// +/// Debugging module, to assert that all constraints evaluate to zero on `H`. +/// It can also be used to check the degree of the constraints by evaluating on a larger subgroup. +/// +/// **Note**: this is an expensive check, hence is only available when the `debug_assertions` +/// flag is activated, to not hinder performances with regular `release` build. +#[cfg(debug_assertions)] fn check_constraints<'a, F, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, @@ -385,9 +524,11 @@ fn check_constraints<'a, F, C, S, const D: usize>( auxiliary_commitment: &'a Option>, lookup_challenges: Option<&'a Vec>, lookups: &[Lookup], + ctl_data: Option<&CtlData>, alphas: Vec, degree_bits: usize, num_lookup_columns: usize, + num_ctl_helper_cols: &[usize], ) where F: RichField + Extendable, C: GenericConfig, @@ -395,6 +536,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( { let degree = 1 << degree_bits; let rate_bits = 0; // Set this to higher value to check constraint degree. + let total_num_helper_cols: usize = num_ctl_helper_cols.iter().sum(); let size = degree << rate_bits; let step = 1 << rate_bits; @@ -446,11 +588,44 @@ fn check_constraints<'a, F, C, S, const D: usize>( ); // Get the local and next row evaluations for the current STARK's permutation argument. let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars { - local_values: auxiliary_subgroup_evals.as_ref().unwrap()[i].clone(), - next_values: auxiliary_subgroup_evals.as_ref().unwrap()[i_next].clone(), + local_values: auxiliary_subgroup_evals.as_ref().unwrap()[i][..num_lookup_columns] + .to_vec(), + next_values: auxiliary_subgroup_evals.as_ref().unwrap()[i_next] + [..num_lookup_columns] + .to_vec(), challenges: challenges.to_vec(), }); + // Get the local and next row evaluations for the current STARK's CTL Z polynomials. + let mut start_index = 0; + let ctl_vars = ctl_data.map(|data| { + data.zs_columns + .iter() + .enumerate() + .map(|(iii, zs_columns)| { + let num_helper_cols = num_ctl_helper_cols[iii]; + let helper_columns = auxiliary_subgroup_evals.as_ref().unwrap()[i] + [num_lookup_columns + start_index + ..num_lookup_columns + start_index + num_helper_cols] + .to_vec(); + let ctl_vars = CtlCheckVars:: { + helper_columns, + local_z: auxiliary_subgroup_evals.as_ref().unwrap()[i] + [num_lookup_columns + total_num_helper_cols + iii], + next_z: auxiliary_subgroup_evals.as_ref().unwrap()[i_next] + [num_lookup_columns + total_num_helper_cols + iii], + challenges: zs_columns.challenge, + columns: zs_columns.columns.clone(), + filter: zs_columns.filter.clone(), + }; + + start_index += num_helper_cols; + + ctl_vars + }) + .collect::>() + }); + // Evaluate the polynomial combining all constraints, including those associated // to the permutation arguments. eval_vanishing_poly::( @@ -458,6 +633,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( &vars, lookups, lookup_vars, + ctl_vars.as_deref(), &mut consumer, ); consumer.accumulators() diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index e91583f19b..9bc62e6b5c 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -1,4 +1,7 @@ -use alloc::vec; +//! Implementation of the STARK recursive verifier, i.e. where proof +//! verification if encoded in a plonky2 circuit. + +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::iter::once; @@ -8,7 +11,9 @@ use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; use plonky2::hash::hash_types::RichField; +use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; use plonky2::iop::witness::Witness; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; @@ -17,6 +22,7 @@ use plonky2::with_context; use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; +use crate::cross_table_lookup::CtlCheckVarsTarget; use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::LookupCheckVarsTarget; use crate::proof::{ @@ -26,6 +32,8 @@ use crate::proof::{ use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly_circuit; +/// Encodes the verification of a [`StarkProofWithPublicInputsTarget`] +/// for some statement in a circuit. pub fn verify_stark_proof_circuit< F: RichField + Extendable, C: GenericConfig, @@ -40,51 +48,57 @@ pub fn verify_stark_proof_circuit< C::Hasher: AlgebraicHasher, { assert_eq!(proof_with_pis.public_inputs.len(), S::PUBLIC_INPUTS); - let degree_bits = proof_with_pis.proof.recover_degree_bits(inner_config); + + let mut challenger = RecursiveChallenger::::new(builder); let challenges = with_context!( builder, "compute challenges", - proof_with_pis.get_challenges::(builder, inner_config) + proof_with_pis.get_challenges::(builder, &mut challenger, None, false, inner_config) ); verify_stark_proof_with_challenges_circuit::( builder, - stark, - proof_with_pis, + &stark, + &proof_with_pis.proof, + &proof_with_pis.public_inputs, challenges, + None, inner_config, - degree_bits, ); } -/// Recursively verifies an inner proof. -fn verify_stark_proof_with_challenges_circuit< +/// Recursively verifies an inner STARK proof. +pub fn verify_stark_proof_with_challenges_circuit< F: RichField + Extendable, C: GenericConfig, S: Stark, const D: usize, >( builder: &mut CircuitBuilder, - stark: S, - proof_with_pis: StarkProofWithPublicInputsTarget, + stark: &S, + proof: &StarkProofTarget, + public_inputs: &[Target], challenges: StarkProofChallengesTarget, + ctl_vars: Option<&[CtlCheckVarsTarget]>, inner_config: &StarkConfig, - degree_bits: usize, ) where C::Hasher: AlgebraicHasher, { - check_lookup_options(&stark, &proof_with_pis, &challenges).unwrap(); + check_lookup_options(stark, proof, &challenges).unwrap(); + + let zero = builder.zero(); let one = builder.one_extension(); - let StarkProofWithPublicInputsTarget { - proof, - public_inputs, - } = proof_with_pis; + let num_ctl_polys = ctl_vars + .map(|v| v.iter().map(|ctl| ctl.helper_columns.len()).sum::()) + .unwrap_or_default(); + let StarkOpeningSetTarget { local_values, next_values, auxiliary_polys, auxiliary_polys_next, + ctl_zs_first, quotient_polys, } = &proof.openings; @@ -92,11 +106,12 @@ fn verify_stark_proof_with_challenges_circuit< local_values, next_values, &public_inputs - .into_iter() - .map(|t| builder.convert_to_ext(t)) + .iter() + .map(|&t| builder.convert_to_ext(t)) .collect::>(), ); + let degree_bits = proof.recover_degree_bits(inner_config); let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); let (l_0, l_last) = @@ -117,6 +132,7 @@ fn verify_stark_proof_with_challenges_circuit< let lookup_challenges = stark.uses_lookups().then(|| { challenges .lookup_challenge_set + .as_ref() .unwrap() .challenges .iter() @@ -133,7 +149,14 @@ fn verify_stark_proof_with_challenges_circuit< with_context!( builder, "evaluate vanishing polynomial", - eval_vanishing_poly_circuit::(builder, &stark, &vars, lookup_vars, &mut consumer) + eval_vanishing_poly_circuit::( + builder, + stark, + &vars, + lookup_vars, + ctl_vars, + &mut consumer + ) ); let vanishing_polys_zeta = consumer.accumulators(); @@ -148,20 +171,22 @@ fn verify_stark_proof_with_challenges_circuit< builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly); } - let merkle_caps = once(proof.trace_cap) - .chain(proof.auxiliary_polys_cap) - .chain(once(proof.quotient_polys_cap)) + let merkle_caps = once(proof.trace_cap.clone()) + .chain(proof.auxiliary_polys_cap.clone()) + .chain(once(proof.quotient_polys_cap.clone())) .collect_vec(); let fri_instance = stark.fri_instance_target( builder, challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), + num_ctl_polys, + ctl_zs_first.as_ref().map_or(0, |c| c.len()), inner_config, ); builder.verify_fri_proof::( &fri_instance, - &proof.openings.to_fri_openings(), + &proof.openings.to_fri_openings(zero), &challenges.fri_challenges, &merkle_caps, &proof.opening_proof, @@ -188,17 +213,27 @@ fn eval_l_0_and_l_last_circuit, const D: usize>( ) } +/// Adds a new `StarkProofWithPublicInputsTarget` to this circuit. pub fn add_virtual_stark_proof_with_pis< F: RichField + Extendable, S: Stark, const D: usize, >( builder: &mut CircuitBuilder, - stark: S, + stark: &S, config: &StarkConfig, degree_bits: usize, + num_ctl_helper_zs: usize, + num_ctl_zs: usize, ) -> StarkProofWithPublicInputsTarget { - let proof = add_virtual_stark_proof::(builder, stark, config, degree_bits); + let proof = add_virtual_stark_proof::( + builder, + stark, + config, + degree_bits, + num_ctl_helper_zs, + num_ctl_zs, + ); let public_inputs = builder.add_virtual_targets(S::PUBLIC_INPUTS); StarkProofWithPublicInputsTarget { proof, @@ -206,58 +241,79 @@ pub fn add_virtual_stark_proof_with_pis< } } +/// Adds a new `StarkProofTarget` to this circuit. pub fn add_virtual_stark_proof, S: Stark, const D: usize>( builder: &mut CircuitBuilder, - stark: S, + stark: &S, config: &StarkConfig, degree_bits: usize, + num_ctl_helper_zs: usize, + num_ctl_zs: usize, ) -> StarkProofTarget { let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; - let num_leaves_per_oracle = vec![ - S::COLUMNS, - stark.num_lookup_helper_columns(config), - stark.quotient_degree_factor() * config.num_challenges, - ]; + let num_leaves_per_oracle = once(S::COLUMNS) + .chain( + (stark.uses_lookups() || stark.requires_ctls()) + .then(|| stark.num_lookup_helper_columns(config) + num_ctl_helper_zs), + ) + .chain(once(stark.quotient_degree_factor() * config.num_challenges)) + .collect_vec(); - let auxiliary_polys_cap = stark - .uses_lookups() + let auxiliary_polys_cap = (stark.uses_lookups() || stark.requires_ctls()) .then(|| builder.add_virtual_cap(cap_height)); StarkProofTarget { trace_cap: builder.add_virtual_cap(cap_height), auxiliary_polys_cap, quotient_polys_cap: builder.add_virtual_cap(cap_height), - openings: add_stark_opening_set_target::(builder, stark, config), + openings: add_virtual_stark_opening_set::( + builder, + stark, + num_ctl_helper_zs, + num_ctl_zs, + config, + ), opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), } } -fn add_stark_opening_set_target, S: Stark, const D: usize>( +fn add_virtual_stark_opening_set, S: Stark, const D: usize>( builder: &mut CircuitBuilder, - stark: S, + stark: &S, + num_ctl_helper_zs: usize, + num_ctl_zs: usize, config: &StarkConfig, ) -> StarkOpeningSetTarget { - let num_challenges = config.num_challenges; StarkOpeningSetTarget { local_values: builder.add_virtual_extension_targets(S::COLUMNS), next_values: builder.add_virtual_extension_targets(S::COLUMNS), - auxiliary_polys: stark.uses_lookups().then(|| { - builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config)) + auxiliary_polys: (stark.uses_lookups() || stark.requires_ctls()).then(|| { + builder.add_virtual_extension_targets( + stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, + ) }), - auxiliary_polys_next: stark.uses_lookups().then(|| { - builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config)) + auxiliary_polys_next: (stark.uses_lookups() || stark.requires_ctls()).then(|| { + builder.add_virtual_extension_targets( + stark.num_lookup_helper_columns(config) + num_ctl_helper_zs, + ) }), + ctl_zs_first: stark + .requires_ctls() + .then(|| builder.add_virtual_targets(num_ctl_zs)), quotient_polys: builder - .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), + .add_virtual_extension_targets(stark.quotient_degree_factor() * config.num_challenges), } } +/// Set the targets in a `StarkProofWithPublicInputsTarget` to +/// their corresponding values in a `StarkProofWithPublicInputs`. pub fn set_stark_proof_with_pis_target, W, const D: usize>( witness: &mut W, stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget, stark_proof_with_pis: &StarkProofWithPublicInputs, + zero: Target, ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, @@ -277,13 +333,16 @@ pub fn set_stark_proof_with_pis_target, W, const D witness.set_target(pi_t, pi); } - set_stark_proof_target(witness, pt, proof); + set_stark_proof_target(witness, pt, proof, zero); } +/// Set the targets in a [`StarkProofTarget`] to their corresponding values in a +/// [`StarkProof`]. pub fn set_stark_proof_target, W, const D: usize>( witness: &mut W, proof_target: &StarkProofTarget, proof: &StarkProof, + zero: Target, ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, @@ -293,7 +352,7 @@ pub fn set_stark_proof_target, W, const D: usize>( witness.set_cap_target(&proof_target.quotient_polys_cap, &proof.quotient_polys_cap); witness.set_fri_openings( - &proof_target.openings.to_fri_openings(), + &proof_target.openings.to_fri_openings(zero), &proof.openings.to_fri_openings(), ); @@ -308,23 +367,23 @@ pub fn set_stark_proof_target, W, const D: usize>( } /// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff -/// the Stark uses a permutation argument. +/// the STARK uses a permutation argument. fn check_lookup_options, S: Stark, const D: usize>( stark: &S, - proof_with_pis: &StarkProofWithPublicInputsTarget, + proof: &StarkProofTarget, challenges: &StarkProofChallengesTarget, ) -> Result<()> { let options_is_some = [ - proof_with_pis.proof.auxiliary_polys_cap.is_some(), - proof_with_pis.proof.openings.auxiliary_polys.is_some(), - proof_with_pis.proof.openings.auxiliary_polys_next.is_some(), + proof.auxiliary_polys_cap.is_some(), + proof.openings.auxiliary_polys.is_some(), + proof.openings.auxiliary_polys_next.is_some(), challenges.lookup_challenge_set.is_some(), ]; ensure!( options_is_some - .into_iter() - .all(|b| b == stark.uses_lookups()), - "Lookups data doesn't match with Stark configuration." + .iter() + .all(|&b| b == stark.uses_lookups() || stark.requires_ctls()), + "Lookups data doesn't match with STARK configuration." ); Ok(()) } diff --git a/starky/src/stark.rs b/starky/src/stark.rs index a9f2b2602f..0e2b3bd7b9 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -1,5 +1,8 @@ -use alloc::vec; -use alloc::vec::Vec; +//! Implementation of the [`Stark`] trait that defines the set of constraints +//! related to a statement. + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; @@ -17,14 +20,11 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::Lookup; -const TRACE_ORACLE_INDEX: usize = 0; -const AUXILIARY_ORACLE_INDEX: usize = 1; -const QUOTIENT_ORACLE_INDEX: usize = 2; - /// Represents a STARK system. pub trait Stark, const D: usize>: Sync { /// The total number of columns in the trace. const COLUMNS: usize = Self::EvaluationFrameTarget::COLUMNS; + /// The total number of public inputs. const PUBLIC_INPUTS: usize = Self::EvaluationFrameTarget::PUBLIC_INPUTS; /// This is used to evaluate constraints natively. @@ -36,7 +36,7 @@ pub trait Stark, const D: usize>: Sync { /// The `Target` version of `Self::EvaluationFrame`, used to evaluate constraints recursively. type EvaluationFrameTarget: StarkEvaluationFrame, ExtensionTarget>; - /// Evaluate constraints at a vector of points. + /// Evaluates constraints at a vector of points. /// /// The points are elements of a field `FE`, a degree `D2` extension of `F`. This lets us /// evaluate constraints over a larger domain if desired. This can also be called with `FE = F` @@ -50,7 +50,7 @@ pub trait Stark, const D: usize>: Sync { FE: FieldExtension, P: PackedField; - /// Evaluate constraints at a vector of points from the base field `F`. + /// Evaluates constraints at a vector of points from the base field `F`. fn eval_packed_base>( &self, vars: &Self::EvaluationFrame, @@ -59,7 +59,7 @@ pub trait Stark, const D: usize>: Sync { self.eval_packed_generic(vars, yield_constr) } - /// Evaluate constraints at a single point from the degree `D` extension field. + /// Evaluates constraints at a single point from the degree `D` extension field. fn eval_ext( &self, vars: &Self::EvaluationFrame, @@ -68,10 +68,10 @@ pub trait Stark, const D: usize>: Sync { self.eval_packed_generic(vars, yield_constr) } - /// Evaluate constraints at a vector of points from the degree `D` extension field. This is like - /// `eval_ext`, except in the context of a recursive circuit. - /// Note: constraints must be added through`yield_constr.constraint(builder, constraint)` in the - /// same order as they are given in `eval_packed_generic`. + /// Evaluates constraints at a vector of points from the degree `D` extension field. + /// This is like `eval_ext`, except in the context of a recursive circuit. + /// Note: constraints must be added through`yield_constr.constraint(builder, constraint)` + /// in the same order as they are given in `eval_packed_generic`. fn eval_ext_circuit( &self, builder: &mut CircuitBuilder, @@ -79,14 +79,16 @@ pub trait Stark, const D: usize>: Sync { yield_constr: &mut RecursiveConstraintConsumer, ); - /// The maximum constraint degree. + /// Outputs the maximum constraint degree of this [`Stark`]. fn constraint_degree(&self) -> usize; - /// The maximum constraint degree. + /// Outputs the maximum quotient polynomial's degree factor of this [`Stark`]. fn quotient_degree_factor(&self) -> usize { 1.max(self.constraint_degree() - 1) } + /// Outputs the number of quotient polynomials this [`Stark`] would require with + /// the provided [`StarkConfig`] fn num_quotient_polys(&self, config: &StarkConfig) -> usize { self.quotient_degree_factor() * config.num_challenges } @@ -96,30 +98,36 @@ pub trait Stark, const D: usize>: Sync { &self, zeta: F::Extension, g: F, + num_ctl_helpers: usize, + num_ctl_zs: Vec, config: &StarkConfig, ) -> FriInstanceInfo { - let trace_oracle = FriOracleInfo { + let mut oracles = vec![]; + let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS); + oracles.push(FriOracleInfo { num_polys: Self::COLUMNS, blinding: false, - }; - let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); + }); let num_lookup_columns = self.num_lookup_helper_columns(config); - let num_auxiliary_polys = num_lookup_columns; - let auxiliary_oracle = FriOracleInfo { - num_polys: num_auxiliary_polys, - blinding: false, + let num_auxiliary_polys = num_lookup_columns + num_ctl_helpers + num_ctl_zs.len(); + let auxiliary_polys_info = if self.uses_lookups() || self.requires_ctls() { + let aux_polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_auxiliary_polys); + oracles.push(FriOracleInfo { + num_polys: num_auxiliary_polys, + blinding: false, + }); + aux_polys + } else { + vec![] }; - let auxiliary_polys_info = - FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); let num_quotient_polys = self.num_quotient_polys(config); - let quotient_oracle = FriOracleInfo { + let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); + oracles.push(FriOracleInfo { num_polys: num_quotient_polys, blinding: false, - }; - let quotient_info = - FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); + }); let zeta_batch = FriBatchInfo { point: zeta, @@ -135,10 +143,22 @@ pub trait Stark, const D: usize>: Sync { polynomials: [trace_info, auxiliary_polys_info].concat(), }; - FriInstanceInfo { - oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch], + let mut batches = vec![zeta_batch, zeta_next_batch]; + + if self.requires_ctls() { + let ctl_zs_info = FriPolynomialInfo::from_range( + 1, // auxiliary oracle index + num_lookup_columns + num_ctl_helpers..num_auxiliary_polys, + ); + let ctl_first_batch = FriBatchInfo { + point: F::Extension::ONE, + polynomials: ctl_zs_info, + }; + + batches.push(ctl_first_batch); } + + FriInstanceInfo { oracles, batches } } /// Computes the FRI instance used to prove this Stark. @@ -147,30 +167,36 @@ pub trait Stark, const D: usize>: Sync { builder: &mut CircuitBuilder, zeta: ExtensionTarget, g: F, + num_ctl_helper_polys: usize, + num_ctl_zs: usize, config: &StarkConfig, ) -> FriInstanceInfoTarget { - let trace_oracle = FriOracleInfo { + let mut oracles = vec![]; + let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS); + oracles.push(FriOracleInfo { num_polys: Self::COLUMNS, blinding: false, - }; - let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS); + }); let num_lookup_columns = self.num_lookup_helper_columns(config); - let num_auxiliary_polys = num_lookup_columns; - let auxiliary_oracle = FriOracleInfo { - num_polys: num_auxiliary_polys, - blinding: false, + let num_auxiliary_polys = num_lookup_columns + num_ctl_helper_polys + num_ctl_zs; + let auxiliary_polys_info = if self.uses_lookups() || self.requires_ctls() { + let aux_polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_auxiliary_polys); + oracles.push(FriOracleInfo { + num_polys: num_auxiliary_polys, + blinding: false, + }); + aux_polys + } else { + vec![] }; - let auxiliary_polys_info = - FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys); let num_quotient_polys = self.num_quotient_polys(config); - let quotient_oracle = FriOracleInfo { + let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); + oracles.push(FriOracleInfo { num_polys: num_quotient_polys, blinding: false, - }; - let quotient_info = - FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys); + }); let zeta_batch = FriBatchInfoTarget { point: zeta, @@ -187,16 +213,31 @@ pub trait Stark, const D: usize>: Sync { polynomials: [trace_info, auxiliary_polys_info].concat(), }; - FriInstanceInfoTarget { - oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch], + let mut batches = vec![zeta_batch, zeta_next_batch]; + + if self.requires_ctls() { + let ctl_zs_info = FriPolynomialInfo::from_range( + 1, // auxiliary oracle index + num_lookup_columns + num_ctl_helper_polys..num_auxiliary_polys, + ); + let ctl_first_batch = FriBatchInfoTarget { + point: builder.one_extension(), + polynomials: ctl_zs_info, + }; + + batches.push(ctl_first_batch); } + + FriInstanceInfoTarget { oracles, batches } } + /// Outputs all the [`Lookup`] this STARK table needs to perform across its columns. fn lookups(&self) -> Vec> { vec![] } + /// Outputs the number of total lookup helper columns, based on this STARK's vector + /// of [`Lookup`] and the number of challenges used by this [`StarkConfig`]. fn num_lookup_helper_columns(&self, config: &StarkConfig) -> usize { self.lookups() .iter() @@ -205,7 +246,17 @@ pub trait Stark, const D: usize>: Sync { * config.num_challenges } + /// Indicates whether this STARK uses lookups over some of its columns, and as such requires + /// additional steps during proof generation to handle auxiliary polynomials. fn uses_lookups(&self) -> bool { !self.lookups().is_empty() } + + /// Indicates whether this STARK belongs to a multi-STARK system, and as such may require + /// cross-table lookups to connect shared values across different traces. + /// + /// It defaults to `false`, i.e. for simple uni-STARK systems. + fn requires_ctls(&self) -> bool { + false + } } diff --git a/starky/src/stark_testing.rs b/starky/src/stark_testing.rs index a454a29c34..cc73284490 100644 --- a/starky/src/stark_testing.rs +++ b/starky/src/stark_testing.rs @@ -1,5 +1,7 @@ -use alloc::vec; -use alloc::vec::Vec; +//! Utility module for testing [`Stark`] implementation. + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; use anyhow::{ensure, Result}; use plonky2::field::extension::{Extendable, FieldExtension}; diff --git a/starky/src/util.rs b/starky/src/util.rs index 1adee0003b..08b2c70253 100644 --- a/starky/src/util.rs +++ b/starky/src/util.rs @@ -1,3 +1,6 @@ +//! Utility module providing some helper functions. + +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use itertools::Itertools; diff --git a/starky/src/vanishing_poly.rs b/starky/src/vanishing_poly.rs index 6a179fe27a..c5ea5c1076 100644 --- a/starky/src/vanishing_poly.rs +++ b/starky/src/vanishing_poly.rs @@ -4,17 +4,24 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookup::{ + eval_cross_table_lookup_checks, eval_cross_table_lookup_checks_circuit, CtlCheckVars, + CtlCheckVarsTarget, +}; use crate::lookup::{ eval_ext_lookups_circuit, eval_packed_lookups_generic, Lookup, LookupCheckVars, LookupCheckVarsTarget, }; use crate::stark::Stark; +/// Evaluates all constraint, permutation and cross-table lookup polynomials +/// of the current STARK at the local and next values. pub(crate) fn eval_vanishing_poly( stark: &S, vars: &S::EvaluationFrame, lookups: &[Lookup], lookup_vars: Option>, + ctl_vars: Option<&[CtlCheckVars]>, consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -22,6 +29,7 @@ pub(crate) fn eval_vanishing_poly( P: PackedField, S: Stark, { + // Evaluate all of the STARK's table constraints. stark.eval_packed_generic(vars, consumer); if let Some(lookup_vars) = lookup_vars { // Evaluate the STARK constraints related to the permutation arguments. @@ -33,21 +41,45 @@ pub(crate) fn eval_vanishing_poly( consumer, ); } + if let Some(ctl_vars) = ctl_vars { + // Evaluate the STARK constraints related to the CTLs. + eval_cross_table_lookup_checks::( + vars, + ctl_vars, + consumer, + stark.constraint_degree(), + ); + } } +/// Circuit version of `eval_vanishing_poly`. +/// Evaluates all constraint, permutation and cross-table lookup polynomials +/// of the current STARK at the local and next values. pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, vars: &S::EvaluationFrameTarget, lookup_vars: Option>, + ctl_vars: Option<&[CtlCheckVarsTarget]>, consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, S: Stark, { + // Evaluate all of the STARK's table constraints. stark.eval_ext_circuit(builder, vars, consumer); if let Some(lookup_vars) = lookup_vars { // Evaluate all of the STARK's constraints related to the permutation argument. eval_ext_lookups_circuit::(builder, stark, vars, lookup_vars, consumer); } + if let Some(ctl_vars) = ctl_vars { + // Evaluate all of the STARK's constraints related to the CTLs. + eval_cross_table_lookup_checks_circuit::( + builder, + vars, + ctl_vars, + consumer, + stark.constraint_degree(), + ); + } } diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 577405ef4f..7959ae0f2e 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -1,4 +1,8 @@ +//! Implementation of the STARK verifier. + +#[cfg(not(feature = "std"))] use alloc::vec::Vec; +use core::any::type_name; use core::iter::once; use anyhow::{anyhow, ensure, Result}; @@ -8,17 +12,20 @@ use plonky2::field::types::Field; use plonky2::fri::verifier::verify_fri_proof; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; +use crate::cross_table_lookup::CtlCheckVars; use crate::evaluation_frame::StarkEvaluationFrame; use crate::lookup::LookupCheckVars; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; +/// Verifies a [`StarkProofWithPublicInputs`] against a STARK statement. pub fn verify_stark_proof< F: RichField + Extendable, C: GenericConfig, @@ -30,36 +37,66 @@ pub fn verify_stark_proof< config: &StarkConfig, ) -> Result<()> { ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); - let degree_bits = proof_with_pis.proof.recover_degree_bits(config); - let challenges = proof_with_pis.get_challenges(config, degree_bits); - verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) + let mut challenger = Challenger::::new(); + + let challenges = proof_with_pis.get_challenges(&mut challenger, None, false, config); + + verify_stark_proof_with_challenges( + &stark, + &proof_with_pis.proof, + &challenges, + None, + &proof_with_pis.public_inputs, + config, + ) } -pub(crate) fn verify_stark_proof_with_challenges< +/// Verifies a [`StarkProofWithPublicInputs`] against a STARK statement, +/// with the provided [`StarkProofChallenges`]. +/// It also supports optional cross-table lookups data and challenges, +/// in case this proof is part of a multi-STARK system. +pub fn verify_stark_proof_with_challenges( + stark: &S, + proof: &StarkProof, + challenges: &StarkProofChallenges, + ctl_vars: Option<&[CtlCheckVars]>, + public_inputs: &[F], + config: &StarkConfig, +) -> Result<()> +where F: RichField + Extendable, C: GenericConfig, S: Stark, - const D: usize, ->( - stark: S, - proof_with_pis: StarkProofWithPublicInputs, - challenges: StarkProofChallenges, - degree_bits: usize, - config: &StarkConfig, -) -> Result<()> { - validate_proof_shape(&stark, &proof_with_pis, config)?; - - let StarkProofWithPublicInputs { +{ + log::debug!("Checking proof: {}", type_name::()); + + let (num_ctl_z_polys, num_ctl_polys) = ctl_vars + .map(|ctls| { + ( + ctls.len(), + ctls.iter().map(|ctl| ctl.helper_columns.len()).sum(), + ) + }) + .unwrap_or_default(); + + validate_proof_shape( + stark, proof, public_inputs, - } = proof_with_pis; + config, + num_ctl_polys, + num_ctl_z_polys, + )?; + let StarkOpeningSet { local_values, next_values, auxiliary_polys, auxiliary_polys_next, + ctl_zs_first: _, quotient_polys, } = &proof.openings; + let vars = S::EvaluationFrame::from_values( local_values, next_values, @@ -69,9 +106,12 @@ pub(crate) fn verify_stark_proof_with_challenges< .map(F::Extension::from_basefield) .collect::>(), ); + + let degree_bits = proof.recover_degree_bits(config); let (l_0, l_last) = eval_l_0_and_l_last(degree_bits, challenges.stark_zeta); let last = F::primitive_root_of_unity(degree_bits).inverse(); let z_last = challenges.stark_zeta - last.into(); + let mut consumer = ConstraintConsumer::::new( challenges .stark_alphas @@ -84,28 +124,34 @@ pub(crate) fn verify_stark_proof_with_challenges< ); let num_lookup_columns = stark.num_lookup_helper_columns(config); - let lookup_challenges = (num_lookup_columns > 0).then(|| { - challenges - .lookup_challenge_set - .unwrap() - .challenges - .iter() - .map(|ch| ch.beta) - .collect::>() - }); + let lookup_challenges = if stark.uses_lookups() { + Some( + challenges + .lookup_challenge_set + .as_ref() + .unwrap() + .challenges + .iter() + .map(|ch| ch.beta) + .collect::>(), + ) + } else { + None + }; let lookup_vars = stark.uses_lookups().then(|| LookupCheckVars { - local_values: auxiliary_polys.as_ref().unwrap().clone(), - next_values: auxiliary_polys_next.as_ref().unwrap().clone(), + local_values: auxiliary_polys.as_ref().unwrap()[..num_lookup_columns].to_vec(), + next_values: auxiliary_polys_next.as_ref().unwrap()[..num_lookup_columns].to_vec(), challenges: lookup_challenges.unwrap(), }); let lookups = stark.lookups(); eval_vanishing_poly::( - &stark, + stark, &vars, &lookups, lookup_vars, + ctl_vars, &mut consumer, ); let vanishing_polys_zeta = consumer.accumulators(); @@ -128,15 +174,25 @@ pub(crate) fn verify_stark_proof_with_challenges< ); } - let merkle_caps = once(proof.trace_cap) - .chain(proof.auxiliary_polys_cap) - .chain(once(proof.quotient_polys_cap)) + let merkle_caps = once(proof.trace_cap.clone()) + .chain(proof.auxiliary_polys_cap.clone()) + .chain(once(proof.quotient_polys_cap.clone())) .collect_vec(); + let num_ctl_zs = ctl_vars + .map(|vars| { + vars.iter() + .map(|ctl| ctl.helper_columns.len()) + .collect::>() + }) + .unwrap_or_default(); + verify_fri_proof::( &stark.fri_instance( challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), + num_ctl_polys, + num_ctl_zs, config, ), &proof.openings.to_fri_openings(), @@ -151,18 +207,17 @@ pub(crate) fn verify_stark_proof_with_challenges< fn validate_proof_shape( stark: &S, - proof_with_pis: &StarkProofWithPublicInputs, + proof: &StarkProof, + public_inputs: &[F], config: &StarkConfig, + num_ctl_helpers: usize, + num_ctl_zs: usize, ) -> anyhow::Result<()> where F: RichField + Extendable, C: GenericConfig, S: Stark, { - let StarkProofWithPublicInputs { - proof, - public_inputs, - } = proof_with_pis; let degree_bits = proof.recover_degree_bits(config); let StarkProof { @@ -180,6 +235,7 @@ where next_values, auxiliary_polys, auxiliary_polys_next, + ctl_zs_first, quotient_polys, } = openings; @@ -188,8 +244,6 @@ where let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; - let num_auxiliary = stark.num_lookup_helper_columns(config); - ensure!(trace_cap.height() == cap_height); ensure!(quotient_polys_cap.height() == cap_height); @@ -202,6 +256,9 @@ where auxiliary_polys_cap, auxiliary_polys, auxiliary_polys_next, + num_ctl_helpers, + num_ctl_zs, + ctl_zs_first, config, )?; @@ -221,21 +278,24 @@ fn eval_l_0_and_l_last(log_n: usize, x: F) -> (F, F) { } /// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff -/// the Stark uses a permutation argument. -fn check_lookup_options< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( +/// the STARK uses a permutation argument. +fn check_lookup_options( stark: &S, auxiliary_polys_cap: &Option>::Hasher>>, auxiliary_polys: &Option>::Extension>>, auxiliary_polys_next: &Option>::Extension>>, + num_ctl_helpers: usize, + num_ctl_zs: usize, + ctl_zs_first: &Option>, config: &StarkConfig, -) -> Result<()> { - if stark.uses_lookups() { - let num_auxiliary = stark.num_lookup_helper_columns(config); +) -> Result<()> +where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + if stark.uses_lookups() || stark.requires_ctls() { + let num_auxiliary = stark.num_lookup_helper_columns(config) + num_ctl_helpers + num_ctl_zs; let cap_height = config.fri_config.cap_height; let auxiliary_polys_cap = auxiliary_polys_cap @@ -248,6 +308,10 @@ fn check_lookup_options< .as_ref() .ok_or_else(|| anyhow!("Missing auxiliary_polys_next"))?; + if let Some(ctl_zs_first) = ctl_zs_first { + ensure!(ctl_zs_first.len() == num_ctl_zs); + } + ensure!(auxiliary_polys_cap.height() == cap_height); ensure!(auxiliary_polys.len() == num_auxiliary); ensure!(auxiliary_polys_next.len() == num_auxiliary); From 710225c9e0ac5822b2965ce74951cf000bbb8a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Tue, 13 Feb 2024 17:53:52 +0100 Subject: [PATCH 143/175] Simulate jumpdest data with the interpreter (#1489) * Simulate jumpdest data with the interpreter * Fix mising type paramenter on some tests * Refactor simulation and fix some intepreter bugs * Fix bug in interpreter * Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> * Address remaining reviews * [WIP] Fixing memory issue * [WIP] Fixed memory issue but erc20 failing * Fix interpreter halting issue * Restore transition.rs * Minor * Adress reviews * Address reviews * Missing fix --------- Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> --- evm/src/cpu/kernel/interpreter.rs | 231 +++++++++++++----- evm/src/cpu/kernel/mod.rs | 3 +- evm/src/cpu/kernel/tests/account_code.rs | 23 +- evm/src/cpu/kernel/tests/add11.rs | 5 +- evm/src/cpu/kernel/tests/balance.rs | 8 +- evm/src/cpu/kernel/tests/bignum/mod.rs | 3 +- evm/src/cpu/kernel/tests/blake2_f.rs | 3 +- evm/src/cpu/kernel/tests/block_hash.rs | 13 +- evm/src/cpu/kernel/tests/bls381.rs | 3 +- evm/src/cpu/kernel/tests/bn254.rs | 17 +- evm/src/cpu/kernel/tests/core/access_lists.rs | 13 +- .../cpu/kernel/tests/core/create_addresses.rs | 7 +- .../cpu/kernel/tests/core/intrinsic_gas.rs | 7 +- .../kernel/tests/core/jumpdest_analysis.rs | 71 ++++-- evm/src/cpu/kernel/tests/ecc/curve_ops.rs | 108 +++++--- evm/src/cpu/kernel/tests/ecc/ecrecover.rs | 9 +- evm/src/cpu/kernel/tests/exp.rs | 9 +- evm/src/cpu/kernel/tests/hash.rs | 5 +- evm/src/cpu/kernel/tests/log.rs | 9 +- evm/src/cpu/kernel/tests/mpt/delete.rs | 3 +- evm/src/cpu/kernel/tests/mpt/hash.rs | 3 +- evm/src/cpu/kernel/tests/mpt/hex_prefix.rs | 7 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 3 +- evm/src/cpu/kernel/tests/mpt/load.rs | 13 +- evm/src/cpu/kernel/tests/mpt/read.rs | 3 +- evm/src/cpu/kernel/tests/packing.rs | 4 +- evm/src/cpu/kernel/tests/receipt.rs | 13 +- evm/src/cpu/kernel/tests/rlp/decode.rs | 19 +- evm/src/cpu/kernel/tests/rlp/encode.rs | 19 +- evm/src/cpu/kernel/tests/rlp/num_bytes.rs | 7 +- evm/src/cpu/kernel/tests/signed_syscalls.rs | 3 +- .../transaction_parsing/parse_type_0_txn.rs | 4 +- evm/src/generation/mod.rs | 84 ------- evm/src/generation/prover_input.rs | 91 ++++--- 34 files changed, 511 insertions(+), 312 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index cdd2e99f37..a8937cf2e2 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -9,8 +9,11 @@ use eth_trie_utils::partial_trie::PartialTrie; use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; use keccak_hash::keccak; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; use super::assembler::BYTES_PER_OFFSET; +use super::utils::u256_from_bool; +use crate::cpu::halt; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -23,7 +26,7 @@ use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::state::{all_withdrawals_prover_inputs_reversed, GenerationState}; use crate::generation::GenerationInputs; use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::util::{h2u, u256_to_usize}; +use crate::util::{h2u, u256_to_u8, u256_to_usize}; use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; @@ -32,8 +35,6 @@ use crate::witness::state::RegistersState; use crate::witness::transition::decode; use crate::witness::util::stack_peek; -type F = GoldilocksField; - /// Halt interpreter execution whenever a jump to this offset is done. const DEFAULT_HALT_OFFSET: usize = 0xdeadbeef; @@ -55,14 +56,17 @@ impl MemoryState { } } -pub(crate) struct Interpreter<'a> { +pub(crate) struct Interpreter<'a, F: Field> { pub(crate) generation_state: GenerationState, prover_inputs_map: &'a HashMap, pub(crate) halt_offsets: Vec, + // The interpreter will halt only if the current context matches halt_context + halt_context: Option, pub(crate) debug_offsets: Vec, running: bool, opcode_count: [usize; 0x100], memops: Vec, + jumpdest_table: HashMap>, } /// Structure storing the state of the interpreter's registers. @@ -80,10 +84,10 @@ struct InterpreterCheckpoint { mem_len: usize, } -pub(crate) fn run_interpreter( +pub(crate) fn run_interpreter( initial_offset: usize, initial_stack: Vec, -) -> anyhow::Result> { +) -> anyhow::Result> { run( &KERNEL.code, initial_offset, @@ -100,9 +104,9 @@ pub(crate) struct InterpreterMemoryInitialization { pub memory: Vec<(usize, Vec)>, } -pub(crate) fn run_interpreter_with_memory( +pub(crate) fn run_interpreter_with_memory( memory_init: InterpreterMemoryInitialization, -) -> anyhow::Result> { +) -> anyhow::Result> { let label = KERNEL.global_labels[&memory_init.label]; let mut stack = memory_init.stack; stack.reverse(); @@ -119,17 +123,47 @@ pub(crate) fn run_interpreter_with_memory( Ok(interpreter) } -pub(crate) fn run<'a>( +pub(crate) fn run<'a, F: Field>( code: &'a [u8], initial_offset: usize, initial_stack: Vec, prover_inputs: &'a HashMap, -) -> anyhow::Result> { +) -> anyhow::Result> { let mut interpreter = Interpreter::new(code, initial_offset, initial_stack, prover_inputs); interpreter.run()?; Ok(interpreter) } +/// Simulates the CPU execution from `state` until the program counter reaches `final_label` +/// in the current context. +pub(crate) fn simulate_cpu_and_get_user_jumps( + final_label: &str, + state: &GenerationState, +) -> Option>> { + match state.jumpdest_table { + Some(_) => None, + None => { + let halt_pc = KERNEL.global_labels[final_label]; + let initial_context = state.registers.context; + let mut interpreter = + Interpreter::new_with_state_and_halt_condition(state, halt_pc, initial_context); + + log::debug!("Simulating CPU for jumpdest analysis."); + + interpreter.run(); + + log::debug!("jdt = {:?}", interpreter.jumpdest_table); + + interpreter + .generation_state + .set_jumpdest_analysis_inputs(interpreter.jumpdest_table); + + log::debug!("Simulated CPU for jumpdest analysis halted."); + interpreter.generation_state.jumpdest_table + } + } +} + /// Different types of Memory operations in the interpreter, and the data required to revert them. enum InterpreterMemOpKind { /// We need to provide the context. @@ -140,7 +174,7 @@ enum InterpreterMemOpKind { Write(U256, usize, usize, usize), } -impl<'a> Interpreter<'a> { +impl<'a, F: Field> Interpreter<'a, F> { pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { let mut result = Self::new( &KERNEL.code, @@ -177,10 +211,12 @@ impl<'a> Interpreter<'a> { // `DEFAULT_HALT_OFFSET` is used as a halting point for the interpreter, // while the label `halt` is the halting label in the kernel. halt_offsets: vec![DEFAULT_HALT_OFFSET, KERNEL.global_labels["halt"]], + halt_context: None, debug_offsets: vec![], running: false, opcode_count: [0; 256], memops: vec![], + jumpdest_table: HashMap::new(), }; result.generation_state.registers.program_counter = initial_offset; let initial_stack_len = initial_stack.len(); @@ -194,6 +230,24 @@ impl<'a> Interpreter<'a> { result } + pub(crate) fn new_with_state_and_halt_condition( + state: &GenerationState, + halt_offset: usize, + halt_context: usize, + ) -> Self { + Self { + generation_state: state.soft_clone(), + prover_inputs_map: &KERNEL.prover_inputs, + halt_offsets: vec![halt_offset], + halt_context: Some(halt_context), + debug_offsets: vec![], + running: false, + opcode_count: [0; 256], + memops: vec![], + jumpdest_table: HashMap::new(), + } + } + /// Initializes the interpreter state given `GenerationInputs`, using the KERNEL code. pub(crate) fn initialize_interpreter_state_with_kernel(&mut self, inputs: GenerationInputs) { self.initialize_interpreter_state(inputs, KERNEL.code_hash, KERNEL.code.len()); @@ -399,9 +453,18 @@ impl<'a> Interpreter<'a> { self.running = true; while self.running { let pc = self.generation_state.registers.program_counter; - if self.is_kernel() && self.halt_offsets.contains(&pc) { + + if let Some(halt_context) = self.halt_context { + if self.is_kernel() + && self.halt_offsets.contains(&pc) + && halt_context == self.generation_state.registers.context + { + self.running = false; + return Ok(()); + } + } else if self.halt_offsets.contains(&pc) { return Ok(()); - }; + } let checkpoint = self.checkpoint(); let result = self.run_opcode(); @@ -426,13 +489,16 @@ impl<'a> Interpreter<'a> { } }?; } - println!("Opcode count:"); - for i in 0..0x100 { - if self.opcode_count[i] > 0 { - println!("{}: {}", get_mnemonic(i as u8), self.opcode_count[i]) + #[cfg(debug_assertions)] + { + println!("Opcode count:"); + for i in 0..0x100 { + if self.opcode_count[i] > 0 { + println!("{}: {}", get_mnemonic(i as u8), self.opcode_count[i]) + } } + println!("Total: {}", self.opcode_count.into_iter().sum::()); } - println!("Total: {}", self.opcode_count.into_iter().sum::()); Ok(()) } @@ -587,14 +653,6 @@ impl<'a> Interpreter<'a> { } } - pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { - self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] - .content - .iter() - .map(|x| x.bit(0)) - .collect() - } - pub(crate) fn set_jumpdest_analysis_inputs(&mut self, jumps: HashMap>) { self.generation_state.set_jumpdest_analysis_inputs(jumps); } @@ -685,12 +743,42 @@ impl<'a> Interpreter<'a> { } fn run_opcode(&mut self) -> Result<(), ProgramError> { + // Jumpdest analysis is performed natively by the interpreter and not + // using the non-deterministic Kernel assembly code. + if self.is_kernel() + && self.generation_state.registers.program_counter + == KERNEL.global_labels["jumpdest_analysis"] + { + self.generation_state.registers.program_counter = + KERNEL.global_labels["jumpdest_analysis_end"]; + self.generation_state + .set_jumpdest_bits(&self.generation_state.get_current_code()?); + } + let opcode = self .code() .get(self.generation_state.registers.program_counter) .byte(0); self.opcode_count[opcode as usize] += 1; self.incr(1); + + let op = decode(self.generation_state.registers, opcode)?; + self.generation_state.registers.gas_used += gas_to_charge(op); + + #[cfg(debug_assertions)] + if !self.is_kernel() { + println!( + "User instruction {:?}, stack = {:?}, ctx = {}", + op, + { + let mut stack = self.stack(); + stack.reverse(); + stack + }, + self.generation_state.registers.context + ); + } + match opcode { 0x00 => self.run_syscall(opcode, 0, false), // "STOP", 0x01 => self.run_add(), // "ADD", @@ -811,20 +899,16 @@ impl<'a> Interpreter<'a> { } }?; + #[cfg(debug_assertions)] if self .debug_offsets .contains(&self.generation_state.registers.program_counter) { - println!("At {}, stack={:?}", self.offset_name(), self.stack()); + println!("At {},", self.offset_name()); } else if let Some(label) = self.offset_label() { println!("At {label}"); } - let op = decode(self.generation_state.registers, opcode) - // We default to prover inputs, as those are kernel-only instructions that charge nothing. - .unwrap_or(Operation::ProverInput); - self.generation_state.registers.gas_used += gas_to_charge(op); - if !self.is_kernel() { let gas_limit_address = MemoryAddress { context: self.context(), @@ -1027,6 +1111,7 @@ impl<'a> Interpreter<'a> { .byte(0) }) .collect::>(); + #[cfg(debug_assertions)] println!("Hashing {:?}", &bytes); let hash = keccak(bytes); self.push(U256::from_big_endian(hash.as_bytes())) @@ -1087,51 +1172,75 @@ impl<'a> Interpreter<'a> { self.push(syscall_info) } - fn set_jumpdest_bit(&mut self, x: U256) -> U256 { + fn get_jumpdest_bit(&self, offset: usize) -> U256 { if self.generation_state.memory.contexts[self.context()].segments [Segment::JumpdestBits.unscale()] .content .len() - > x.low_u32() as usize + > offset { self.generation_state.memory.get(MemoryAddress { context: self.context(), segment: Segment::JumpdestBits.unscale(), - virt: x.low_u32() as usize, + virt: offset, }) } else { 0.into() } } - fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let jumpdest_bit = self.set_jumpdest_bit(x); + pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] + .content + .iter() + .map(|x| x.bit(0)) + .collect() + } + + fn add_jumpdest_offset(&mut self, offset: usize) { + if let Some(jumpdest_table) = self + .jumpdest_table + .get_mut(&self.generation_state.registers.context) + { + jumpdest_table.insert(offset); + } else { + self.jumpdest_table.insert( + self.generation_state.registers.context, + BTreeSet::from([offset]), + ); + } + } + + fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { + let offset = self.pop()?; // Check that the destination is valid. - let x: u32 = x - .try_into() - .map_err(|_| ProgramError::InvalidJumpDestination)?; + let offset: usize = u256_to_usize(offset)?; + + let jumpdest_bit = self.get_jumpdest_bit(offset); if !self.is_kernel() && jumpdest_bit != U256::one() { return Err(ProgramError::InvalidJumpDestination); } - self.jump_to(x as usize, false) + self.jump_to(offset, false) } fn run_jumpi(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let b = self.pop()?; - if !b.is_zero() { - let x: u32 = x - .try_into() - .map_err(|_| ProgramError::InvalidJumpiDestination)?; - self.jump_to(x as usize, true)?; + let offset = self.pop()?; + let cond = self.pop()?; + + let offset: usize = offset + .try_into() + .map_err(|_| ProgramError::InvalidJumpiDestination)?; + + let jumpdest_bit = self.get_jumpdest_bit(offset); + + if !cond.is_zero() && (self.is_kernel() || jumpdest_bit == U256::one()) { + self.jump_to(offset, true)?; } - let jumpdest_bit = self.set_jumpdest_bit(x); - if !b.is_zero() && !self.is_kernel() && jumpdest_bit != U256::one() { + if !cond.is_zero() && !self.is_kernel() && jumpdest_bit != U256::one() { return Err(ProgramError::InvalidJumpiDestination); } Ok(()) @@ -1167,9 +1276,10 @@ impl<'a> Interpreter<'a> { self.generation_state.observe_contract(tip_h256)?; } - if self.halt_offsets.contains(&offset) { - self.running = false; + if !self.is_kernel() { + self.add_jumpdest_offset(offset); } + Ok(()) } @@ -1237,6 +1347,7 @@ impl<'a> Interpreter<'a> { } self.set_context(new_ctx); self.generation_state.registers.stack_len = new_sp; + Ok(()) } @@ -1603,8 +1714,16 @@ pub(crate) use unpack_address; #[cfg(test)] mod tests { - use super::*; + use std::collections::HashMap; + + use ethereum_types::U256; + use plonky2::field::goldilocks_field::GoldilocksField as F; + + use crate::cpu::kernel::constants::context_metadata::ContextMetadata; + use crate::cpu::kernel::interpreter::{run, Interpreter}; use crate::memory::segments::Segment; + use crate::witness::memory::MemoryAddress; + use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_run() -> anyhow::Result<()> { @@ -1612,7 +1731,7 @@ mod tests { 0x60, 0x1, 0x60, 0x2, 0x1, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56, ]; // PUSH1, 1, PUSH1, 2, ADD, PUSH4 deadbeef, JUMP assert_eq!( - run(&code, 0, vec![], &HashMap::new())?.stack(), + run::(&code, 0, vec![], &HashMap::new())?.stack(), &[0x3.into()], ); Ok(()) @@ -1637,7 +1756,7 @@ mod tests { 0x60, 0xff, 0x60, 0x0, 0x52, 0x60, 0, 0x51, 0x60, 0x1, 0x51, 0x60, 0x42, 0x60, 0x27, 0x53, ]; - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); interpreter.set_code(1, code.to_vec()); diff --git a/evm/src/cpu/kernel/mod.rs b/evm/src/cpu/kernel/mod.rs index e82474914c..5a6717f214 100644 --- a/evm/src/cpu/kernel/mod.rs +++ b/evm/src/cpu/kernel/mod.rs @@ -10,8 +10,7 @@ mod parser; pub mod stack; mod utils; -#[cfg(test)] -mod interpreter; +pub(crate) mod interpreter; #[cfg(test)] mod tests; diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 5e2dddca9e..b3a075cf7a 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -6,6 +6,8 @@ use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField as F; +use plonky2::field::types::Field; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -20,7 +22,10 @@ use crate::witness::memory::MemoryAddress; use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::Node; -pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieInputs) { +pub(crate) fn initialize_mpts( + interpreter: &mut Interpreter, + trie_inputs: &TrieInputs, +) { // Load all MPTs. let (trie_root_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); @@ -70,8 +75,8 @@ fn random_code() -> Vec { // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( - interpreter: &mut Interpreter, +fn prepare_interpreter( + interpreter: &mut Interpreter, address: Address, account: &AccountRlp, ) -> Result<()> { @@ -151,7 +156,7 @@ fn test_extcodesize() -> Result<()> { let code = random_code(); let account = test_account(&code); - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); let address: Address = thread_rng().gen(); // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter(&mut interpreter, address, &account)?; @@ -183,7 +188,7 @@ fn test_extcodecopy() -> Result<()> { let code = random_code(); let account = test_account(&code); - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); let address: Address = thread_rng().gen(); // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter(&mut interpreter, address, &account)?; @@ -252,8 +257,8 @@ fn test_extcodecopy() -> Result<()> { /// Prepare the interpreter for storage tests by inserting all necessary accounts /// in the state trie, adding the code we want to context 1 and switching the context. -fn prepare_interpreter_all_accounts( - interpreter: &mut Interpreter, +fn prepare_interpreter_all_accounts( + interpreter: &mut Interpreter, trie_inputs: TrieInputs, addr: [u8; 20], code: &[u8], @@ -318,7 +323,7 @@ fn sstore() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter_all_accounts(&mut interpreter, trie_inputs, addr, &code)?; @@ -407,7 +412,7 @@ fn sload() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter_all_accounts(&mut interpreter, trie_inputs, addr, &code)?; diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index c5eb29397e..de5450c5ce 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -6,6 +6,7 @@ use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256}; use hex_literal::hex; use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; @@ -155,7 +156,7 @@ fn test_add11_yml() { }; let initial_stack = vec![]; - let mut interpreter = + let mut interpreter: Interpreter = Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); let route_txn_label = KERNEL.global_labels["main"]; @@ -297,7 +298,7 @@ fn test_add11_yml_with_exception() { }; let initial_stack = vec![]; - let mut interpreter = + let mut interpreter: Interpreter = Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); let route_txn_label = KERNEL.global_labels["main"]; diff --git a/evm/src/cpu/kernel/tests/balance.rs b/evm/src/cpu/kernel/tests/balance.rs index b393c05cf5..af190ae4ce 100644 --- a/evm/src/cpu/kernel/tests/balance.rs +++ b/evm/src/cpu/kernel/tests/balance.rs @@ -2,6 +2,8 @@ use anyhow::Result; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{Address, BigEndianHash, H256, U256}; use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField as F; +use plonky2::field::types::Field; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -24,8 +26,8 @@ fn test_account(balance: U256) -> AccountRlp { // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( - interpreter: &mut Interpreter, +fn prepare_interpreter( + interpreter: &mut Interpreter, address: Address, account: &AccountRlp, ) -> Result<()> { @@ -107,7 +109,7 @@ fn test_balance() -> Result<()> { let balance = U256(rng.gen()); let account = test_account(balance); - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); let address: Address = rng.gen(); // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter(&mut interpreter, address, &account)?; diff --git a/evm/src/cpu/kernel/tests/bignum/mod.rs b/evm/src/cpu/kernel/tests/bignum/mod.rs index 0cc6f0dc1b..cc0e47af3b 100644 --- a/evm/src/cpu/kernel/tests/bignum/mod.rs +++ b/evm/src/cpu/kernel/tests/bignum/mod.rs @@ -8,6 +8,7 @@ use ethereum_types::U256; use itertools::Itertools; use num::{BigUint, One, Zero}; use num_bigint::RandBigInt; +use plonky2::field::goldilocks_field::GoldilocksField as F; use plonky2_util::ceil_div_usize; use rand::Rng; @@ -99,7 +100,7 @@ fn run_test(fn_label: &str, memory: Vec, stack: Vec) -> Result<(Vec< initial_stack.push(retdest); initial_stack.reverse(); - let mut interpreter = Interpreter::new_with_kernel(fn_label, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(fn_label, initial_stack); interpreter.set_current_general_memory(memory); interpreter.run()?; diff --git a/evm/src/cpu/kernel/tests/blake2_f.rs b/evm/src/cpu/kernel/tests/blake2_f.rs index c5d800c5b6..7d9349c7fd 100644 --- a/evm/src/cpu/kernel/tests/blake2_f.rs +++ b/evm/src/cpu/kernel/tests/blake2_f.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::interpreter::{ run_interpreter_with_memory, InterpreterMemoryInitialization, @@ -71,7 +72,7 @@ fn run_blake2_f( memory: vec![], }; - let result = run_interpreter_with_memory(interpreter_setup).unwrap(); + let result = run_interpreter_with_memory::(interpreter_setup).unwrap(); let mut hash = result.stack().to_vec(); hash.reverse(); diff --git a/evm/src/cpu/kernel/tests/block_hash.rs b/evm/src/cpu/kernel/tests/block_hash.rs index 23ba233721..9c77951d63 100644 --- a/evm/src/cpu/kernel/tests/block_hash.rs +++ b/evm/src/cpu/kernel/tests/block_hash.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::{H256, U256}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -19,7 +20,8 @@ fn test_correct_block_hash() -> Result<()> { let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(blockhash_label, initial_stack); interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, 256.into()); @@ -48,7 +50,8 @@ fn test_big_index_block_hash() -> Result<()> { let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(blockhash_label, initial_stack); interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); @@ -78,7 +81,8 @@ fn test_small_index_block_hash() -> Result<()> { let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(blockhash_label, initial_stack); interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); @@ -106,7 +110,8 @@ fn test_block_hash_with_overflow() -> Result<()> { let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - let mut interpreter = Interpreter::new_with_kernel(blockhash_label, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(blockhash_label, initial_stack); interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs index aeba6fbd96..1ffa711505 100644 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ b/evm/src/cpu/kernel/tests/bls381.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::Rng; use crate::cpu::kernel::interpreter::{ @@ -23,7 +24,7 @@ fn test_bls_fp2_mul() -> Result<()> { segment: KernelGeneral, memory: vec![], }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter = run_interpreter_with_memory::(setup).unwrap(); let stack: Vec = interpreter.stack().iter().rev().cloned().collect(); let output = Fp2::::from_stack(&stack); diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs index 8a90ff2479..efe2ed9f17 100644 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ b/evm/src/cpu/kernel/tests/bn254.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::Rng; use crate::cpu::kernel::interpreter::{ @@ -23,7 +24,7 @@ fn run_bn_mul_fp6(f: Fp6, g: Fp6, label: &str) -> Fp6 { segment: BnPairing, memory: vec![], }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter = run_interpreter_with_memory::(setup).unwrap(); let output: Vec = interpreter.stack().iter().rev().cloned().collect(); Fp6::::from_stack(&output) } @@ -63,7 +64,7 @@ fn run_bn_mul_fp12(f: Fp12, g: Fp12, label: &str) -> Fp12 { segment: BnPairing, memory: vec![(in0, f.to_stack().to_vec()), (in1, g.to_stack().to_vec())], }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter = run_interpreter_with_memory::(setup).unwrap(); let output = interpreter.extract_kernel_memory(BnPairing, out..out + 12); Fp12::::from_stack(&output) } @@ -93,7 +94,7 @@ fn run_bn_frob_fp6(n: usize, f: Fp6) -> Fp6 { segment: BnPairing, memory: vec![], }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); let output: Vec = interpreter.stack().iter().rev().cloned().collect(); Fp6::::from_stack(&output) } @@ -117,7 +118,7 @@ fn run_bn_frob_fp12(f: Fp12, n: usize) -> Fp12 { segment: BnPairing, memory: vec![(ptr, f.to_stack().to_vec())], }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); Fp12::::from_stack(&output) } @@ -147,7 +148,7 @@ fn test_bn_inv_fp12() -> Result<()> { segment: BnPairing, memory: vec![(ptr, f.to_stack().to_vec())], }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); let output = Fp12::::from_stack(&output); @@ -175,7 +176,7 @@ fn test_bn_final_exponent() -> Result<()> { memory: vec![(ptr, f.to_stack().to_vec())], }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); let expected: Vec = bn_final_exponent(f).to_stack(); @@ -202,7 +203,7 @@ fn test_bn_miller() -> Result<()> { segment: BnPairing, memory: vec![(ptr, input)], }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter = run_interpreter_with_memory::(setup).unwrap(); let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); let expected = bn_miller_loop(p, q).to_stack(); @@ -246,7 +247,7 @@ fn test_bn_pairing() -> Result<()> { segment: BnPairing, memory: vec![(ptr, input)], }; - let interpreter = run_interpreter_with_memory(setup).unwrap(); + let interpreter = run_interpreter_with_memory::(setup).unwrap(); assert_eq!(interpreter.stack()[0], U256::one()); Ok(()) } diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs index 69dd2d27d4..4ee38e92c6 100644 --- a/evm/src/cpu/kernel/tests/core/access_lists.rs +++ b/evm/src/cpu/kernel/tests/core/access_lists.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use anyhow::Result; use ethereum_types::{Address, U256}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -33,7 +34,8 @@ fn test_insert_accessed_addresses() -> Result<()> { // Test for address already in list. let initial_stack = vec![retaddr, U256::from(addr_in_list.0.as_slice())]; - let mut interpreter = Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); for i in 0..n { let addr = U256::from(addresses[i].0.as_slice()); interpreter @@ -57,7 +59,8 @@ fn test_insert_accessed_addresses() -> Result<()> { // Test for address not in list. let initial_stack = vec![retaddr, U256::from(addr_not_in_list.0.as_slice())]; - let mut interpreter = Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); for i in 0..n { let addr = U256::from(addresses[i].0.as_slice()); interpreter @@ -115,7 +118,8 @@ fn test_insert_accessed_storage_keys() -> Result<()> { storage_key_in_list.1, U256::from(storage_key_in_list.0 .0.as_slice()), ]; - let mut interpreter = Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); for i in 0..n { let addr = U256::from(storage_keys[i].0 .0.as_slice()); interpreter @@ -152,7 +156,8 @@ fn test_insert_accessed_storage_keys() -> Result<()> { storage_key_not_in_list.1, U256::from(storage_key_not_in_list.0 .0.as_slice()), ]; - let mut interpreter = Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); for i in 0..n { let addr = U256::from(storage_keys[i].0 .0.as_slice()); interpreter diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs index 3f31657891..339e2182ef 100644 --- a/evm/src/cpu/kernel/tests/core/create_addresses.rs +++ b/evm/src/cpu/kernel/tests/core/create_addresses.rs @@ -4,6 +4,7 @@ use anyhow::Result; use ethereum_types::{H256, U256}; use hex_literal::hex; use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -19,7 +20,8 @@ fn test_get_create_address() -> Result<()> { let expected_addr = U256::from_big_endian(&hex!("3f09c73a5ed19289fb9bdc72f1742566df146f56")); let initial_stack = vec![retaddr, nonce, sender]; - let mut interpreter = Interpreter::new_with_kernel(get_create_address, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(get_create_address, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), &[expected_addr]); @@ -105,7 +107,8 @@ fn test_get_create2_address() -> Result<()> { } in create2_test_cases() { let initial_stack = vec![retaddr, salt, U256::from(code_hash.0), sender]; - let mut interpreter = Interpreter::new_with_kernel(get_create2_address, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(get_create2_address, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), &[expected_addr]); diff --git a/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs b/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs index d8badef9db..ee9db0dfe2 100644 --- a/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs +++ b/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -15,13 +16,15 @@ fn test_intrinsic_gas() -> Result<()> { // Contract creation transaction. let initial_stack = vec![0xdeadbeefu32.into()]; - let mut interpreter = Interpreter::new_with_kernel(intrinsic_gas, initial_stack.clone()); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(intrinsic_gas, initial_stack.clone()); interpreter.set_global_metadata_field(GlobalMetadata::ContractCreation, U256::one()); interpreter.run()?; assert_eq!(interpreter.stack(), vec![(GAS_TX + GAS_TXCREATE).into()]); // Message transaction. - let mut interpreter = Interpreter::new_with_kernel(intrinsic_gas, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(intrinsic_gas, initial_stack); interpreter.set_txn_field(NormalizedTxnField::To, 123.into()); interpreter.run()?; assert_eq!(interpreter.stack(), vec![GAS_TX.into()]); diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index d704cc198d..7923997d7a 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -2,6 +2,8 @@ use std::collections::{BTreeSet, HashMap}; use anyhow::Result; use ethereum_types::U256; +use itertools::Itertools; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -10,7 +12,10 @@ use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_jumpdest_analysis() -> Result<()> { - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; + // By default the interpreter will skip jumpdest analysis asm and compute + // the jumpdest table bits natively. We avoid that starting 1 line after + // performing the missing first PROVER_INPUT "by hand" + let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"] + 1; const CONTEXT: usize = 3; // arbitrary let add = get_opcode("ADD"); @@ -18,7 +23,7 @@ fn test_jumpdest_analysis() -> Result<()> { let jumpdest = get_opcode("JUMPDEST"); #[rustfmt::skip] - let code: Vec = vec![ + let mut code: Vec = vec![ add, jumpdest, push2, @@ -28,16 +33,24 @@ fn test_jumpdest_analysis() -> Result<()> { add, jumpdest, ]; + code.extend( + (0..32) + .rev() + .map(get_push_opcode) + .chain(std::iter::once(jumpdest)), + ); - let jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + let mut jumpdest_bits = vec![false, true, false, false, false, true, false, true]; + // Add 32 falses and 1 true + jumpdest_bits.extend( + std::iter::repeat(false) + .take(32) + .chain(std::iter::once(true)), + ); + + let mut interpreter: Interpreter = Interpreter::new_with_kernel(jumpdest_analysis, vec![]); + let code_len = code.len(); - // Contract creation transaction. - let initial_stack = vec![ - 0xDEADBEEFu32.into(), - code.len().into(), - U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, - ]; - let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); interpreter.set_code(CONTEXT, code); interpreter.set_jumpdest_analysis_inputs(HashMap::from([( 3, @@ -50,31 +63,47 @@ fn test_jumpdest_analysis() -> Result<()> { ), )])); + // The `set_jumpdest_analysis_inputs` method is never used. assert_eq!( interpreter.generation_state.jumpdest_table, // Context 3 has jumpdest 1, 5, 7. All have proof 0 and hence - // the list [proof_0, jumpdest_0, ... ] is [0, 1, 0, 5, 0, 7] - Some(HashMap::from([(3, vec![0, 1, 0, 5, 0, 7])])) + // the list [proof_0, jumpdest_0, ... ] is [0, 1, 0, 5, 0, 7, 8, 40] + Some(HashMap::from([(3, vec![0, 1, 0, 5, 0, 7, 8, 40])])) ); + // Run jumpdest analysis with context = 3 + interpreter.generation_state.registers.context = CONTEXT; + interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(code_len.into()); + interpreter.push(U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR); + + // We need to manually pop the jumpdest_table and push its value on the top of the stack + interpreter + .generation_state + .jumpdest_table + .as_mut() + .unwrap() + .get_mut(&CONTEXT) + .unwrap() + .pop(); + interpreter.push(U256::one()); + interpreter.run()?; assert_eq!(interpreter.stack(), vec![]); - assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(3)); + assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(CONTEXT)); Ok(()) } #[test] fn test_packed_verification() -> Result<()> { - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"]; + let write_table_if_jumpdest = KERNEL.global_labels["write_table_if_jumpdest"]; const CONTEXT: usize = 3; // arbitrary let add = get_opcode("ADD"); let jumpdest = get_opcode("JUMPDEST"); - // The last push(i=0) is 0x5f which is not a valid opcode. However, this - // is still meaningful for the test and makes things easier let mut code: Vec = std::iter::once(add) .chain( (0..=31) @@ -92,10 +121,12 @@ fn test_packed_verification() -> Result<()> { // Contract creation transaction. let initial_stack = vec![ 0xDEADBEEFu32.into(), - code.len().into(), U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, + 33.into(), + U256::one(), ]; - let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack.clone()); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(write_table_if_jumpdest, initial_stack.clone()); interpreter.set_code(CONTEXT, code.clone()); interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); @@ -106,8 +137,8 @@ fn test_packed_verification() -> Result<()> { // If we add 1 to each opcode the jumpdest at position 32 is never a valid jumpdest for i in 1..=32 { code[i] += 1; - let mut interpreter = - Interpreter::new_with_kernel(jumpdest_analysis, initial_stack.clone()); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(write_table_if_jumpdest, initial_stack.clone()); interpreter.set_code(CONTEXT, code.clone()); interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); diff --git a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs index f107d8becf..ed37401f02 100644 --- a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs +++ b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs @@ -2,6 +2,7 @@ mod bn { use anyhow::Result; use ethereum_types::U256; + use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter}; @@ -43,76 +44,110 @@ mod bn { // Standard addition #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard addition #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard doubling #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #2 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_interpreter(ec_double, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_double, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #3 let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Addition with identity #1 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #3 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Addition with invalid point(s) #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, invalid.1, invalid.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #2 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, point0.1, point0.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #3 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, identity.1, identity.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #4 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, invalid.1, invalid.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Scalar multiplication #1 let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); // Scalar multiplication #2 let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #3 let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point0.1, point0.0])?); // Scalar multiplication #4 let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #5 let initial_stack = u256ify(["0xdeadbeef", s, invalid.1, invalid.0])?; - let stack = run_interpreter(ec_mul, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Multiple calls @@ -126,7 +161,9 @@ mod bn { point0.1, point0.0, ])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); Ok(()) @@ -147,7 +184,8 @@ mod bn { let mut initial_stack = u256ify(["0xdeadbeef"])?; initial_stack.push(k); - let mut int = Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); + let mut int: Interpreter = + Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); int.run()?; assert_eq!(line, int.stack()); @@ -165,7 +203,7 @@ mod bn { "0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171", "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", ])?; - let mut int = Interpreter::new( + let mut int: Interpreter = Interpreter::new( &KERNEL.code, precompute, initial_stack, @@ -227,6 +265,7 @@ mod bn { mod secp { use anyhow::Result; use ethereum_types::U256; + use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::{combined_kernel, KERNEL}; use crate::cpu::kernel::interpreter::{run, run_interpreter, Interpreter}; @@ -260,36 +299,48 @@ mod secp { // Standard addition #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard addition #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_add, initial_stack, &kernel.prover_inputs)? + let stack = run::(&kernel.code, ec_add, initial_stack, &kernel.prover_inputs)? .stack() .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard doubling #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #2 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_interpreter(ec_double, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_double, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Addition with identity #1 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #3 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_interpreter(ec_add, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); Ok(()) @@ -310,7 +361,8 @@ mod secp { let mut initial_stack = u256ify(["0xdeadbeef"])?; initial_stack.push(k); - let mut int = Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); + let mut int: Interpreter = + Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); int.run()?; assert_eq!(line, int.stack()); diff --git a/evm/src/cpu/kernel/tests/ecc/ecrecover.rs b/evm/src/cpu/kernel/tests/ecc/ecrecover.rs index 2453ab1a22..baf003d993 100644 --- a/evm/src/cpu/kernel/tests/ecc/ecrecover.rs +++ b/evm/src/cpu/kernel/tests/ecc/ecrecover.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::run_interpreter; @@ -10,7 +11,9 @@ use crate::cpu::kernel::tests::u256ify; fn test_valid_ecrecover(hash: &str, v: &str, r: &str, s: &str, expected: &str) -> Result<()> { let ecrecover = KERNEL.global_labels["ecrecover"]; let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_interpreter(ecrecover, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ecrecover, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack[0], U256::from_str(expected).unwrap()); Ok(()) @@ -19,7 +22,9 @@ fn test_valid_ecrecover(hash: &str, v: &str, r: &str, s: &str, expected: &str) - fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str) -> Result<()> { let ecrecover = KERNEL.global_labels["ecrecover"]; let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_interpreter(ecrecover, initial_stack)?.stack().to_vec(); + let stack = run_interpreter::(ecrecover, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX]); Ok(()) diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs index 482c6b7216..28d840f85a 100644 --- a/evm/src/cpu/kernel/tests/exp.rs +++ b/evm/src/cpu/kernel/tests/exp.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -15,16 +16,16 @@ fn test_exp() -> Result<()> { // Random input let initial_stack = vec![0xDEADBEEFu32.into(), b, a]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack.clone()); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack.clone()); - let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack(); + let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); let expected_exp = a.overflowing_pow(b).0; assert_eq!(stack_with_kernel, vec![expected_exp]); // 0 base let initial_stack = vec![0xDEADBEEFu32.into(), b, U256::zero()]; - let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack(); + let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); let expected_exp = U256::zero().overflowing_pow(b).0; assert_eq!(stack_with_kernel, vec![expected_exp]); @@ -33,7 +34,7 @@ fn test_exp() -> Result<()> { let initial_stack = vec![0xDEADBEEFu32.into(), U256::zero(), a]; interpreter.set_is_kernel(true); interpreter.set_context(0); - let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack(); + let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); let expected_exp = 1.into(); assert_eq!(stack_with_kernel, vec![expected_exp]); diff --git a/evm/src/cpu/kernel/tests/hash.rs b/evm/src/cpu/kernel/tests/hash.rs index 6371f0a8a3..672aa5d1ad 100644 --- a/evm/src/cpu/kernel/tests/hash.rs +++ b/evm/src/cpu/kernel/tests/hash.rs @@ -1,12 +1,13 @@ use anyhow::Result; // use blake2::Blake2b512; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use ripemd::{Digest, Ripemd160}; use sha2::Sha256; use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, InterpreterMemoryInitialization, + run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, }; use crate::memory::segments::Segment::KernelGeneral; @@ -66,7 +67,7 @@ fn prepare_test( let interpreter_setup = make_interpreter_setup(message, hash_fn_label, hash_input_virt); // Run the interpreter - let result = run_interpreter_with_memory(interpreter_setup).unwrap(); + let result: Interpreter = run_interpreter_with_memory(interpreter_setup).unwrap(); Ok((expected, result.stack().to_vec())) } diff --git a/evm/src/cpu/kernel/tests/log.rs b/evm/src/cpu/kernel/tests/log.rs index 406fba0c5b..9c80b42614 100644 --- a/evm/src/cpu/kernel/tests/log.rs +++ b/evm/src/cpu/kernel/tests/log.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::{Address, U256}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -25,7 +26,7 @@ fn test_log_0() -> Result<()> { U256::from_big_endian(&address.to_fixed_bytes()), ]; - let mut interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 0.into()); interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 0.into()); @@ -68,7 +69,7 @@ fn test_log_2() -> Result<()> { U256::from_big_endian(&address.to_fixed_bytes()), ]; - let mut interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 2.into()); interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 5.into()); @@ -129,7 +130,7 @@ fn test_log_4() -> Result<()> { U256::from_big_endian(&address.to_fixed_bytes()), ]; - let mut interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 2.into()); interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 5.into()); @@ -189,7 +190,7 @@ fn test_log_5() -> Result<()> { U256::from_big_endian(&address.to_fixed_bytes()), ]; - let mut interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 0.into()); interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 0.into()); diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs index 0d4d5e71f7..34bc0d66ba 100644 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm/src/cpu/kernel/tests/mpt/delete.rs @@ -2,6 +2,7 @@ use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{BigEndianHash, H256, U512}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::random; use crate::cpu::kernel::aggregator::KERNEL; @@ -98,7 +99,7 @@ fn test_state_trie( let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs index a06dd2a0b5..e9a7ebde86 100644 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm/src/cpu/kernel/tests/mpt/hash.rs @@ -1,6 +1,7 @@ use anyhow::Result; use eth_trie_utils::partial_trie::PartialTrie; use ethereum_types::{BigEndianHash, H256}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -111,7 +112,7 @@ fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); diff --git a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs index e51e60ab46..37077e4022 100644 --- a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs +++ b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -15,7 +16,7 @@ fn hex_prefix_even_nonterminated() -> Result<()> { let num_nibbles = 6.into(); let rlp_pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(5)]); @@ -43,7 +44,7 @@ fn hex_prefix_odd_terminated() -> Result<()> { let num_nibbles = 5.into(); let rlp_pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(4)]); @@ -70,7 +71,7 @@ fn hex_prefix_odd_terminated_tiny() -> Result<()> { let num_nibbles = 1.into(); let rlp_pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; assert_eq!( interpreter.stack(), diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index 19b82f74a2..cbb13b9b40 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -2,6 +2,7 @@ use anyhow::Result; use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; use ethereum_types::{BigEndianHash, H256}; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -173,7 +174,7 @@ fn test_state_trie( let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs index bff1d8cb39..85c023f090 100644 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ b/evm/src/cpu/kernel/tests/mpt/load.rs @@ -5,6 +5,7 @@ use eth_trie_utils::nibbles::Nibbles; use eth_trie_utils::partial_trie::HashedPartialTrie; use ethereum_types::{BigEndianHash, H256, U256}; use hex_literal::hex; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; @@ -24,7 +25,7 @@ fn load_all_mpts_empty() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); @@ -61,7 +62,7 @@ fn load_all_mpts_leaf() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); @@ -107,7 +108,7 @@ fn load_all_mpts_hash() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); @@ -145,7 +146,7 @@ fn load_all_mpts_empty_branch() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); @@ -197,7 +198,7 @@ fn load_all_mpts_ext_to_leaf() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); @@ -243,7 +244,7 @@ fn load_mpt_txn_trie() -> Result<()> { }; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); diff --git a/evm/src/cpu/kernel/tests/mpt/read.rs b/evm/src/cpu/kernel/tests/mpt/read.rs index 16206d1390..a86bab85bf 100644 --- a/evm/src/cpu/kernel/tests/mpt/read.rs +++ b/evm/src/cpu/kernel/tests/mpt/read.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::BigEndianHash; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -20,7 +21,7 @@ fn mpt_read() -> Result<()> { let mpt_read = KERNEL.global_labels["mpt_read"]; let initial_stack = vec![]; - let mut interpreter = Interpreter::new_with_kernel(0, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs index 0eb09cf7a6..ba72f658a2 100644 --- a/evm/src/cpu/kernel/tests/packing.rs +++ b/evm/src/cpu/kernel/tests/packing.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -15,7 +16,8 @@ fn test_mstore_unpacking() -> Result<()> { let addr = (Segment::TxnData as u64).into(); let initial_stack = vec![retdest, len, value, addr]; - let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(mstore_unpacking, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![addr + U256::from(4)]); diff --git a/evm/src/cpu/kernel/tests/receipt.rs b/evm/src/cpu/kernel/tests/receipt.rs index 7d00cb2746..cf9f63896e 100644 --- a/evm/src/cpu/kernel/tests/receipt.rs +++ b/evm/src/cpu/kernel/tests/receipt.rs @@ -2,6 +2,7 @@ use anyhow::Result; use ethereum_types::{Address, U256}; use hex_literal::hex; use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField as F; use rand::{thread_rng, Rng}; use crate::cpu::kernel::aggregator::KERNEL; @@ -47,7 +48,8 @@ fn test_process_receipt() -> Result<()> { leftover_gas, success, ]; - let mut interpreter = Interpreter::new_with_kernel(process_receipt, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(process_receipt, initial_stack); interpreter.set_memory_segment( Segment::LogsData, vec![ @@ -128,7 +130,8 @@ fn test_receipt_encoding() -> Result<()> { let expected_rlp = rlp::encode(&rlp::encode(&receipt_1)); let initial_stack: Vec = vec![retdest, 0.into(), 0.into(), 0.into()]; - let mut interpreter = Interpreter::new_with_kernel(encode_receipt, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(encode_receipt, initial_stack); // Write data to memory. let expected_bloom_bytes = vec![ @@ -248,7 +251,7 @@ fn test_receipt_bloom_filter() -> Result<()> { // Set logs memory and initialize TxnBloom and BlockBloom segments. let initial_stack: Vec = vec![retdest]; - let mut interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); let mut logs = vec![ 0.into(), // unused addr, @@ -408,7 +411,7 @@ fn test_mpt_insert_receipt() -> Result<()> { receipt.push(num_logs.into()); // num_logs receipt.extend(logs_0.clone()); - let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); initialize_mpts(&mut interpreter, &trie_inputs); // If TrieData is empty, we need to push 0 because the first value is always 0. @@ -562,7 +565,7 @@ fn test_bloom_two_logs() -> Result<()> { ] .into(), ]; - let mut interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); // Initialize transaction Bloom filter. interpreter.set_memory_segment(Segment::LogsData, logs); interpreter.set_memory_segment(Segment::Logs, vec![0.into(), 4.into()]); diff --git a/evm/src/cpu/kernel/tests/rlp/decode.rs b/evm/src/cpu/kernel/tests/rlp/decode.rs index 1f3260e56f..6a749f5cb8 100644 --- a/evm/src/cpu/kernel/tests/rlp/decode.rs +++ b/evm/src/cpu/kernel/tests/rlp/decode.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -13,7 +14,8 @@ fn test_decode_rlp_string_len_short() -> Result<()> { 0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize + 2), ]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by "0x70" which is its own encoding. interpreter.set_rlp_memory(vec![123, 234, 0x70]); @@ -33,7 +35,8 @@ fn test_decode_rlp_string_len_medium() -> Result<()> { 0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize + 2), ]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by the RLP encoding of "1 2 3 4 5". interpreter.set_rlp_memory(vec![123, 234, 0x85, 1, 2, 3, 4, 5]); @@ -53,7 +56,8 @@ fn test_decode_rlp_string_len_long() -> Result<()> { 0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize + 2), ]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // The RLP encoding of the string "1 2 3 ... 56". interpreter.set_rlp_memory(vec![ @@ -74,7 +78,8 @@ fn test_decode_rlp_list_len_short() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, 2, [3, 4]]. interpreter.set_rlp_memory(vec![0xc5, 1, 2, 0xc2, 3, 4]); @@ -91,7 +96,8 @@ fn test_decode_rlp_list_len_long() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, ..., 56]. interpreter.set_rlp_memory(vec![ @@ -112,7 +118,8 @@ fn test_decode_rlp_scalar() -> Result<()> { let decode_rlp_scalar = KERNEL.global_labels["decode_rlp_scalar"]; let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter = Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); // The RLP encoding of "12 34 56". interpreter.set_rlp_memory(vec![0x83, 0x12, 0x34, 0x56]); diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs index d28a763fe8..75464235b7 100644 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -1,5 +1,6 @@ use anyhow::Result; use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -13,7 +14,8 @@ fn test_encode_rlp_scalar_small() -> Result<()> { let scalar = 42.into(); let pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(1)]; // pos' = pos + rlp_len = 2 + 1 @@ -32,7 +34,8 @@ fn test_encode_rlp_scalar_medium() -> Result<()> { let scalar = 0x12345.into(); let pos = U256::from(Segment::RlpRaw as usize + 2); let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(4)]; // pos' = pos + rlp_len = 2 + 4 @@ -51,7 +54,8 @@ fn test_encode_rlp_160() -> Result<()> { let string = 0x12345.into(); let pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, string, pos, U256::from(20)]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(1 + 20)]; // pos' @@ -71,7 +75,8 @@ fn test_encode_rlp_256() -> Result<()> { let string = 0x12345.into(); let pos = U256::from(Segment::RlpRaw as usize); let initial_stack = vec![retdest, string, pos, U256::from(32)]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); interpreter.run()?; let expected_stack = vec![pos + U256::from(1 + 32)]; // pos' @@ -91,7 +96,8 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> { let start_pos = U256::from(Segment::RlpRaw as usize + 9); let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 5); let initial_stack = vec![retdest, start_pos, end_pos]; - let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); interpreter.set_rlp_memory(vec![ // Nine 0s to leave room for the longest possible RLP list prefix. 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -120,7 +126,8 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { let start_pos = U256::from(Segment::RlpRaw as usize + 9); let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 60); let initial_stack = vec![retdest, start_pos, end_pos]; - let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); #[rustfmt::skip] interpreter.set_rlp_memory(vec![ diff --git a/evm/src/cpu/kernel/tests/rlp/num_bytes.rs b/evm/src/cpu/kernel/tests/rlp/num_bytes.rs index fa4066fe55..b02175d055 100644 --- a/evm/src/cpu/kernel/tests/rlp/num_bytes.rs +++ b/evm/src/cpu/kernel/tests/rlp/num_bytes.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -10,7 +11,7 @@ fn test_num_bytes_0() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let x = 0.into(); let initial_stack = vec![retdest, x]; - let mut interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![1.into()]); @@ -24,7 +25,7 @@ fn test_num_bytes_small() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let x = 42.into(); let initial_stack = vec![retdest, x]; - let mut interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![1.into()]); @@ -38,7 +39,7 @@ fn test_num_bytes_medium() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let x = 0xAABBCCDDu32.into(); let initial_stack = vec![retdest, x]; - let mut interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); interpreter.run()?; assert_eq!(interpreter.stack(), vec![4.into()]); diff --git a/evm/src/cpu/kernel/tests/signed_syscalls.rs b/evm/src/cpu/kernel/tests/signed_syscalls.rs index 74b3524b00..993b8e03f2 100644 --- a/evm/src/cpu/kernel/tests/signed_syscalls.rs +++ b/evm/src/cpu/kernel/tests/signed_syscalls.rs @@ -1,4 +1,5 @@ use ethereum_types::U256; +use plonky2::field::goldilocks_field::GoldilocksField as F; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -117,7 +118,7 @@ fn run_test(fn_label: &str, expected_fn: fn(U256, U256) -> U256, opname: &str) { for &x in &inputs { for &y in &inputs { let stack = vec![retdest, y, x]; - let mut interpreter = Interpreter::new_with_kernel(fn_label, stack); + let mut interpreter: Interpreter = Interpreter::new_with_kernel(fn_label, stack); interpreter.run().unwrap(); assert_eq!(interpreter.stack_len(), 1usize, "unexpected stack size"); let output = interpreter diff --git a/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs b/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs index 5976acaf65..8415b47bd5 100644 --- a/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs +++ b/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs @@ -1,6 +1,7 @@ use anyhow::Result; use ethereum_types::U256; use hex_literal::hex; +use plonky2::field::goldilocks_field::GoldilocksField as F; use NormalizedTxnField::*; use crate::cpu::kernel::aggregator::KERNEL; @@ -13,7 +14,8 @@ fn process_type_0_txn() -> Result<()> { let process_normalized_txn = KERNEL.global_labels["process_normalized_txn"]; let retaddr = 0xDEADBEEFu32.into(); - let mut interpreter = Interpreter::new_with_kernel(process_type_0_txn, vec![retaddr]); + let mut interpreter: Interpreter = + Interpreter::new_with_kernel(process_type_0_txn, vec![retaddr]); // When we reach process_normalized_txn, we're done with parsing and normalizing. // Processing normalized transactions is outside the scope of this test. diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 6da0e38b7b..105fb6a906 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -333,87 +333,3 @@ fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> transition(state)?; } } - -fn simulate_cpu_between_labels_and_get_user_jumps( - initial_label: &str, - final_label: &str, - state: &mut GenerationState, -) -> Option>> { - if state.jumpdest_table.is_some() { - None - } else { - const JUMP_OPCODE: u8 = 0x56; - const JUMPI_OPCODE: u8 = 0x57; - - let halt_pc = KERNEL.global_labels[final_label]; - let mut jumpdest_addresses: HashMap<_, BTreeSet> = HashMap::new(); - - state.registers.program_counter = KERNEL.global_labels[initial_label]; - let initial_clock = state.traces.clock(); - let initial_context = state.registers.context; - - log::debug!("Simulating CPU for jumpdest analysis."); - - loop { - // skip jumpdest table validations in simulations - if state.registers.is_kernel - && state.registers.program_counter == KERNEL.global_labels["jumpdest_analysis"] - { - state.registers.program_counter = KERNEL.global_labels["jumpdest_analysis_end"] - } - let pc = state.registers.program_counter; - let context = state.registers.context; - let halt = state.registers.is_kernel - && pc == halt_pc - && state.registers.context == initial_context; - let Ok(opcode) = u256_to_u8(state.memory.get(MemoryAddress::new( - context, - Segment::Code, - state.registers.program_counter, - ))) else { - log::debug!( - "Simulated CPU for jumpdest analysis halted after {} cycles", - state.traces.clock() - initial_clock - ); - return Some(jumpdest_addresses); - }; - let cond = if let Ok(cond) = stack_peek(state, 1) { - cond != U256::zero() - } else { - false - }; - if !state.registers.is_kernel - && (opcode == JUMP_OPCODE || (opcode == JUMPI_OPCODE && cond)) - { - // Avoid deeper calls to abort - let Ok(jumpdest) = u256_to_usize(state.registers.stack_top) else { - log::debug!( - "Simulated CPU for jumpdest analysis halted after {} cycles", - state.traces.clock() - initial_clock - ); - return Some(jumpdest_addresses); - }; - state.memory.set( - MemoryAddress::new(context, Segment::JumpdestBits, jumpdest), - U256::one(), - ); - let jumpdest_opcode = - state - .memory - .get(MemoryAddress::new(context, Segment::Code, jumpdest)); - if let Some(ctx_addresses) = jumpdest_addresses.get_mut(&context) { - ctx_addresses.insert(jumpdest); - } else { - jumpdest_addresses.insert(context, BTreeSet::from([jumpdest])); - } - } - if halt || transition(state).is_err() { - log::debug!( - "Simulated CPU for jumpdest analysis halted after {} cycles", - state.traces.clock() - initial_clock - ); - return Some(jumpdest_addresses); - } - } - } -} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 9662d6b6b7..e6fb4dbf8d 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -10,12 +10,14 @@ use plonky2::field::types::Field; use serde::{Deserialize, Serialize}; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::interpreter::simulate_cpu_and_get_user_jumps; +use crate::cpu::kernel::opcodes::get_push_opcode; use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; use crate::generation::prover_input::EvmField::{ Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, }; use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; -use crate::generation::simulate_cpu_between_labels_and_get_user_jumps; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::memory::segments::Segment::BnPairing; @@ -250,6 +252,7 @@ impl GenerationState { if self.jumpdest_table.is_none() { self.generate_jumpdest_table()?; + log::debug!("jdt = {:?}", self.jumpdest_table); } let Some(jumpdest_table) = &mut self.jumpdest_table else { @@ -258,12 +261,19 @@ impl GenerationState { )); }; + let jd_len = jumpdest_table.len(); + if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop() { + log::debug!( + "jumpdest_table_len = {:?}, ctx_jumpdest_table.len = {:?}", + jd_len, + ctx_jumpdest_table.len() + ); Ok((next_jumpdest_address + 1).into()) } else { - self.jumpdest_table = None; + jumpdest_table.remove(&context); Ok(U256::zero()) } } @@ -276,9 +286,17 @@ impl GenerationState { ProverInputError::InvalidJumpdestSimulation, )); }; + + let jd_len = jumpdest_table.len(); + if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) && let Some(next_jumpdest_proof) = ctx_jumpdest_table.pop() { + log::debug!( + "jumpdest_table_len = {:?}, ctx_jumpdest_table.len = {:?}", + jd_len, + ctx_jumpdest_table.len() + ); Ok(next_jumpdest_proof.into()) } else { Err(ProgramError::ProverInputError( @@ -292,24 +310,9 @@ impl GenerationState { /// Simulate the user's code and store all the jump addresses with their respective contexts. fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { let checkpoint = self.checkpoint(); - let memory = self.memory.clone(); // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - let Some(jumpdest_table) = simulate_cpu_between_labels_and_get_user_jumps( - "jumpdest_analysis_end", - "terminate_common", - self, - ) else { - self.jumpdest_table = Some(HashMap::new()); - return Ok(()); - }; - - // Return to the state before starting the simulation - self.rollback(checkpoint); - self.memory = memory; - - // Find proofs for all contexts - self.set_jumpdest_analysis_inputs(jumpdest_table); + self.jumpdest_table = simulate_cpu_and_get_user_jumps("terminate_common", self); Ok(()) } @@ -333,6 +336,10 @@ impl GenerationState { ))); } + pub(crate) fn get_current_code(&self) -> Result, ProgramError> { + self.get_code(self.registers.context) + } + fn get_code(&self, context: usize) -> Result, ProgramError> { let code_len = self.get_code_len(context)?; let code = (0..code_len) @@ -354,12 +361,28 @@ impl GenerationState { )))?; Ok(code_len) } + + fn get_current_code_len(&self) -> Result { + self.get_code_len(self.registers.context) + } + + pub(crate) fn set_jumpdest_bits(&mut self, code: &[u8]) { + const JUMPDEST_OPCODE: u8 = 0x5b; + for (pos, opcode) in CodeIterator::new(code) { + if opcode == JUMPDEST_OPCODE { + self.memory.set( + MemoryAddress::new(self.registers.context, Segment::JumpdestBits, pos), + U256::one(), + ); + } + } + } } -/// For all address in `jumpdest_table`, each bounded by `largest_address`, +/// For all address in `jumpdest_table` smaller than `largest_address`, /// this function searches for a proof. A proof is the closest address /// for which none of the previous 32 bytes in the code (including opcodes -/// and pushed bytes) are PUSHXX and the address is in its range. It returns +/// and pushed bytes) is a PUSHXX and the address is in its range. It returns /// a vector of even size containing proofs followed by their addresses. fn get_proofs_and_jumpdests( code: &[u8], @@ -370,30 +393,24 @@ fn get_proofs_and_jumpdests( const PUSH32_OPCODE: u8 = 0x7f; let (proofs, _) = CodeIterator::until(code, largest_address + 1).fold( (vec![], 0), - |(mut proofs, acc), (pos, _opcode)| { - let has_prefix = if let Some(prefix_start) = pos.checked_sub(32) { - code[prefix_start..pos] + |(mut proofs, last_proof), (addr, opcode)| { + let has_prefix = if let Some(prefix_start) = addr.checked_sub(32) { + code[prefix_start..addr] .iter() - .enumerate() - .fold(true, |acc, (prefix_pos, &byte)| { - let cond1 = byte > PUSH32_OPCODE; - let cond2 = (prefix_start + prefix_pos) as i32 - + (byte as i32 - PUSH1_OPCODE as i32) - + 1 - < pos as i32; - acc && (cond1 || cond2) - }) + .rev() + .zip(0..32) + .all(|(&byte, i)| byte > PUSH32_OPCODE || byte < PUSH1_OPCODE + i) } else { false }; - let acc = if has_prefix { pos - 32 } else { acc }; - if jumpdest_table.contains(&pos) { + let last_proof = if has_prefix { addr - 32 } else { last_proof }; + if jumpdest_table.contains(&addr) { // Push the proof - proofs.push(acc); + proofs.push(last_proof); // Push the address - proofs.push(pos); + proofs.push(addr); } - (proofs, acc) + (proofs, last_proof) }, ); proofs From 6f42ed5ca389fb372823458cb94b37a14e6c9ccd Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 15 Feb 2024 08:52:34 -0500 Subject: [PATCH 144/175] Add additional methods for Poseidon with PackedField (#1526) * Add additional methods for Poseidon with PackedField * Apply comments * Comment --------- Co-authored-by: Linda Guiga --- plonky2/src/hash/poseidon.rs | 172 ++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/plonky2/src/hash/poseidon.rs b/plonky2/src/hash/poseidon.rs index 204f04ba94..ae5a26c14e 100644 --- a/plonky2/src/hash/poseidon.rs +++ b/plonky2/src/hash/poseidon.rs @@ -5,6 +5,7 @@ use alloc::{vec, vec::Vec}; use core::fmt::Debug; +use plonky2_field::packed::PackedField; use unroll::unroll_for_loops; use crate::field::extension::{Extendable, FieldExtension}; @@ -160,8 +161,9 @@ pub trait Poseidon: PrimeField64 { // times number of rounds. const N_ROUND_CONSTANTS: usize = SPONGE_WIDTH * N_ROUNDS; - // The MDS matrix we use is C + D, where C is the circulant matrix whose first row is given by - // `MDS_MATRIX_CIRC`, and D is the diagonal matrix whose diagonal is given by `MDS_MATRIX_DIAG`. + // The MDS matrix we use is C + D, where C is the circulant matrix whose first + // row is given by `MDS_MATRIX_CIRC`, and D is the diagonal matrix whose + // diagonal is given by `MDS_MATRIX_DIAG`. const MDS_MATRIX_CIRC: [u64; SPONGE_WIDTH]; const MDS_MATRIX_DIAG: [u64; SPONGE_WIDTH]; @@ -213,6 +215,33 @@ pub trait Poseidon: PrimeField64 { res } + /// Same as `mds_row_shf` for `PackedField`. + fn mds_row_shf_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + r: usize, + v: &[P; SPONGE_WIDTH], + ) -> P + where + FE: FieldExtension, + P: PackedField, + { + debug_assert!(r < SPONGE_WIDTH); + let mut res = P::ZEROS; + + for i in 0..SPONGE_WIDTH { + res += + v[(i + r) % SPONGE_WIDTH] * P::Scalar::from_canonical_u64(Self::MDS_MATRIX_CIRC[i]); + } + res += v[r] * P::Scalar::from_canonical_u64(Self::MDS_MATRIX_DIAG[r]); + + res + } + /// Recursive version of `mds_row_shf`. fn mds_row_shf_circuit( builder: &mut CircuitBuilder, @@ -273,6 +302,29 @@ pub trait Poseidon: PrimeField64 { result } + /// Same as `mds_layer` for `PackedField`. + fn mds_layer_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + state: &[P; SPONGE_WIDTH], + ) -> [P; SPONGE_WIDTH] + where + FE: FieldExtension, + P: PackedField, + { + let mut result = [P::ZEROS; SPONGE_WIDTH]; + + for r in 0..SPONGE_WIDTH { + result[r] = Self::mds_row_shf_packed_field(r, state); + } + + result + } + /// Recursive version of `mds_layer`. fn mds_layer_circuit( builder: &mut CircuitBuilder, @@ -320,6 +372,29 @@ pub trait Poseidon: PrimeField64 { } } + /// Same as `partial_first_constant_layer` for `PackedField`. + #[inline(always)] + #[unroll_for_loops] + fn partial_first_constant_layer_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + state: &mut [P; SPONGE_WIDTH], + ) where + FE: FieldExtension, + P: PackedField, + { + for i in 0..12 { + if i < SPONGE_WIDTH { + state[i] += + P::Scalar::from_canonical_u64(Self::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i]); + } + } + } + /// Recursive version of `partial_first_constant_layer`. fn partial_first_constant_layer_circuit( builder: &mut CircuitBuilder, @@ -365,6 +440,46 @@ pub trait Poseidon: PrimeField64 { result } + /// Same as `mds_partial_layer_init` for `PackedField`. + #[inline(always)] + #[unroll_for_loops] + fn mds_partial_layer_init_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + state: &[P; SPONGE_WIDTH], + ) -> [P; SPONGE_WIDTH] + where + FE: FieldExtension, + P: PackedField, + { + let mut result = [P::ZEROS; SPONGE_WIDTH]; + + // Initial matrix has first row/column = [1, 0, ..., 0]; + + // c = 0 + result[0] = state[0]; + + for r in 1..12 { + if r < SPONGE_WIDTH { + for c in 1..12 { + if c < SPONGE_WIDTH { + // NB: FAST_PARTIAL_ROUND_INITIAL_MATRIX is stored in + // row-major order so that this dot product is cache + // friendly. + let t = P::Scalar::from_canonical_u64( + Self::FAST_PARTIAL_ROUND_INITIAL_MATRIX[r - 1][c - 1], + ); + result[c] += state[r] * t; + } + } + } + } + result + } /// Recursive version of `mds_partial_layer_init`. fn mds_partial_layer_init_circuit( builder: &mut CircuitBuilder, @@ -449,6 +564,39 @@ pub trait Poseidon: PrimeField64 { result } + /// Same as `mds_partial_layer_fast` for `PackedField. + fn mds_partial_layer_fast_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + state: &[P; SPONGE_WIDTH], + r: usize, + ) -> [P; SPONGE_WIDTH] + where + FE: FieldExtension, + P: PackedField, + { + let s0 = state[0]; + let mds0to0 = Self::MDS_MATRIX_CIRC[0] + Self::MDS_MATRIX_DIAG[0]; + let mut d = s0 * P::Scalar::from_canonical_u64(mds0to0); + for i in 1..SPONGE_WIDTH { + let t = P::Scalar::from_canonical_u64(Self::FAST_PARTIAL_ROUND_W_HATS[r][i - 1]); + d += state[i] * t; + } + + // result = [d] concat [state[0] * v + state[shift up by 1]] + let mut result = [P::ZEROS; SPONGE_WIDTH]; + result[0] = d; + for i in 1..SPONGE_WIDTH { + let t = P::Scalar::from_canonical_u64(Self::FAST_PARTIAL_ROUND_VS[r][i - 1]); + result[i] = state[0] * t + state[i]; + } + result + } + /// Recursive version of `mds_partial_layer_fast`. fn mds_partial_layer_fast_circuit( builder: &mut CircuitBuilder, @@ -502,6 +650,26 @@ pub trait Poseidon: PrimeField64 { } } + /// Same as `constant_layer` for PackedFields. + fn constant_layer_packed_field< + F: RichField + Extendable, + const D: usize, + FE, + P, + const D2: usize, + >( + state: &mut [P; SPONGE_WIDTH], + round_ctr: usize, + ) where + FE: FieldExtension, + P: PackedField, + { + for i in 0..SPONGE_WIDTH { + state[i] += + P::Scalar::from_canonical_u64(ALL_ROUND_CONSTANTS[i + SPONGE_WIDTH * round_ctr]); + } + } + /// Recursive version of `constant_layer`. fn constant_layer_circuit( builder: &mut CircuitBuilder, From 8753162b77190f02ec6bbf1c3e31d6c3c843eb8e Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Thu, 15 Feb 2024 18:14:44 +0400 Subject: [PATCH 145/175] Fix build for wasm32/64-unknown-unknown without std (#1528) * adjust to no_std build * disable default features of serde_json * cargo fmt & clippy * fix review remarks --- maybe_rayon/src/lib.rs | 23 +++++++++++++++-------- plonky2/Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/maybe_rayon/src/lib.rs b/maybe_rayon/src/lib.rs index c4bfb2e93b..8e8d071862 100644 --- a/maybe_rayon/src/lib.rs +++ b/maybe_rayon/src/lib.rs @@ -1,8 +1,7 @@ +#![cfg_attr(not(std), no_std)] + #[cfg(not(feature = "parallel"))] -use core::{ - iter::{FlatMap, IntoIterator, Iterator}, - slice::{Chunks, ChunksExact, ChunksExactMut, ChunksMut}, -}; +extern crate alloc; #[cfg(feature = "parallel")] pub use rayon::{ @@ -20,6 +19,14 @@ use rayon::{ ChunksMut as ParChunksMut, ParallelSlice, ParallelSliceMut, }, }; +#[cfg(not(feature = "parallel"))] +use { + alloc::vec::Vec, + core::{ + iter::{FlatMap, IntoIterator, Iterator}, + slice::{self, Chunks, ChunksExact, ChunksExactMut, ChunksMut}, + }, +}; pub trait MaybeParIter<'data> { #[cfg(feature = "parallel")] @@ -53,7 +60,7 @@ where #[cfg(not(feature = "parallel"))] impl<'data, T: 'data> MaybeParIter<'data> for Vec { type Item = &'data T; - type Iter = std::slice::Iter<'data, T>; + type Iter = slice::Iter<'data, T>; fn par_iter(&'data self) -> Self::Iter { self.iter() @@ -63,7 +70,7 @@ impl<'data, T: 'data> MaybeParIter<'data> for Vec { #[cfg(not(feature = "parallel"))] impl<'data, T: 'data> MaybeParIter<'data> for [T] { type Item = &'data T; - type Iter = std::slice::Iter<'data, T>; + type Iter = slice::Iter<'data, T>; fn par_iter(&'data self) -> Self::Iter { self.iter() @@ -102,7 +109,7 @@ where #[cfg(not(feature = "parallel"))] impl<'data, T: 'data> MaybeParIterMut<'data> for Vec { type Item = &'data mut T; - type Iter = std::slice::IterMut<'data, T>; + type Iter = slice::IterMut<'data, T>; fn par_iter_mut(&'data mut self) -> Self::Iter { self.iter_mut() @@ -112,7 +119,7 @@ impl<'data, T: 'data> MaybeParIterMut<'data> for Vec { #[cfg(not(feature = "parallel"))] impl<'data, T: 'data> MaybeParIterMut<'data> for [T] { type Item = &'data mut T; - type Iter = std::slice::IterMut<'data, T>; + type Iter = slice::IterMut<'data, T>; fn par_iter_mut(&'data mut self) -> Self::Iter { self.iter_mut() diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 4cc44cccd3..f20932a594 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -31,7 +31,6 @@ plonky2_util = { path = "../util", default-features = false } rand = { version = "0.8.4", default-features = false } rand_chacha = { version = "0.3.1", optional = true, default-features = false } serde = { version = "1.0", default-features = false, features = ["derive", "rc"] } -serde_json = "1.0" static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } web-time = { version = "1.0.0", optional = true } @@ -46,6 +45,7 @@ num_cpus = { version = "1.14.0", default-features = false } rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } rand_chacha = { version = "0.3.1", default-features = false } serde_cbor = { version = "0.11.2" } +serde_json = { version = "1.0" } structopt = { version = "0.3.26", default-features = false } tynm = { version = "0.1.6", default-features = false } From a7b985ce392f49e0b778af043109338ca187f3de Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Fri, 16 Feb 2024 17:55:46 +0100 Subject: [PATCH 146/175] Add hash public input methods (#1529) * Add hash public input methods * Change vecs to arrays * :: * Fix method name --- plonky2/src/plonk/circuit_builder.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 6df692fbe4..b9c13f8147 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -337,10 +337,15 @@ impl, const D: usize> CircuitBuilder { [0; N].map(|_| self.add_virtual_target()) } - /// Adds a new `HashOutTarget`. `NUM_HASH_OUT_ELTS` being hardcoded to 4, it internally - /// adds 4 virtual targets in a vector fashion. + /// Adds a new `HashOutTarget`. pub fn add_virtual_hash(&mut self) -> HashOutTarget { - HashOutTarget::from_vec(self.add_virtual_targets(4)) + HashOutTarget::from(self.add_virtual_target_arr::<4>()) + } + + /// Registers a new `HashOutTarget` as a public input, adding + /// internally `NUM_HASH_OUT_ELTS` virtual targets. + pub fn add_virtual_hash_public_input(&mut self) -> HashOutTarget { + HashOutTarget::from(self.add_virtual_public_input_arr::<4>()) } /// Adds a new `MerkleCapTarget`, consisting in `1 << cap_height` `HashOutTarget`. @@ -353,6 +358,13 @@ impl, const D: usize> CircuitBuilder { (0..n).map(|_i| self.add_virtual_hash()).collect() } + /// Registers `n` new `HashOutTarget` as public inputs, in a vector fashion. + pub fn add_virtual_hashes_public_input(&mut self, n: usize) -> Vec { + (0..n) + .map(|_i| self.add_virtual_hash_public_input()) + .collect() + } + pub(crate) fn add_virtual_merkle_proof(&mut self, len: usize) -> MerkleProofTarget { MerkleProofTarget { siblings: self.add_virtual_hashes(len), From 3e579b6d433f245d4f28cd1213a73c8485ef4829 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:04:07 -0500 Subject: [PATCH 147/175] Remove plonky2_evm post-migration to zk_evm monorepo (#1530) --- .../continuous-integration-workflow.yml | 8 - Cargo.toml | 2 +- README.md | 16 +- evm/.cargo/katex-header.html | 1 - evm/Cargo.toml | 71 - evm/LICENSE-APACHE | 176 - evm/LICENSE-MIT | 19 - evm/README.md | 36 - evm/benches/stack_manipulation.rs | 75 - evm/spec/.gitignore | 7 - evm/spec/Makefile | 20 - evm/spec/bibliography.bib | 30 - evm/spec/cpulogic.tex | 285 -- evm/spec/framework.tex | 159 - evm/spec/introduction.tex | 3 - evm/spec/mpts.tex | 94 - evm/spec/tables.tex | 10 - evm/spec/tables/arithmetic.tex | 54 - evm/spec/tables/byte-packing.tex | 59 - evm/spec/tables/cpu.tex | 73 - evm/spec/tables/keccak-f.tex | 65 - evm/spec/tables/keccak-sponge.tex | 66 - evm/spec/tables/logic.tex | 18 - evm/spec/tables/memory.tex | 87 - evm/spec/zkevm.pdf | Bin 296911 -> 0 bytes evm/spec/zkevm.tex | 61 - evm/src/all_stark.rs | 300 -- evm/src/arithmetic/addcy.rs | 355 -- evm/src/arithmetic/arithmetic_stark.rs | 511 --- evm/src/arithmetic/byte.rs | 502 --- evm/src/arithmetic/columns.rs | 119 - evm/src/arithmetic/divmod.rs | 378 -- evm/src/arithmetic/mod.rs | 350 -- evm/src/arithmetic/modular.rs | 1004 ------ evm/src/arithmetic/mul.rs | 320 -- evm/src/arithmetic/shift.rs | 338 -- evm/src/arithmetic/utils.rs | 343 -- evm/src/bin/assemble.rs | 12 - evm/src/byte_packing/byte_packing_stark.rs | 440 --- evm/src/byte_packing/columns.rs | 42 - evm/src/byte_packing/mod.rs | 10 - evm/src/cpu/byte_unpacking.rs | 94 - evm/src/cpu/clock.rs | 37 - evm/src/cpu/columns/general.rs | 157 - evm/src/cpu/columns/mod.rs | 168 - evm/src/cpu/columns/ops.rs | 89 - evm/src/cpu/contextops.rs | 344 -- evm/src/cpu/control_flow.rs | 166 - evm/src/cpu/cpu_stark.rs | 574 --- evm/src/cpu/decode.rs | 405 --- evm/src/cpu/dup_swap.rs | 343 -- evm/src/cpu/gas.rs | 324 -- evm/src/cpu/halt.rs | 104 - evm/src/cpu/jumps.rs | 390 -- evm/src/cpu/kernel/aggregator.rs | 184 - evm/src/cpu/kernel/asm/account_code.asm | 136 - evm/src/cpu/kernel/asm/balance.asm | 56 - evm/src/cpu/kernel/asm/bignum/add.asm | 73 - evm/src/cpu/kernel/asm/bignum/addmul.asm | 116 - evm/src/cpu/kernel/asm/bignum/cmp.asm | 93 - evm/src/cpu/kernel/asm/bignum/isone.asm | 35 - evm/src/cpu/kernel/asm/bignum/iszero.asm | 40 - evm/src/cpu/kernel/asm/bignum/modexp.asm | 192 - evm/src/cpu/kernel/asm/bignum/modmul.asm | 178 - evm/src/cpu/kernel/asm/bignum/mul.asm | 70 - evm/src/cpu/kernel/asm/bignum/shr.asm | 74 - evm/src/cpu/kernel/asm/bignum/util.asm | 21 - evm/src/cpu/kernel/asm/bloom_filter.asm | 166 - evm/src/cpu/kernel/asm/core/access_lists.asm | 203 -- evm/src/cpu/kernel/asm/core/call.asm | 447 --- evm/src/cpu/kernel/asm/core/call_gas.asm | 92 - evm/src/cpu/kernel/asm/core/create.asm | 291 -- .../cpu/kernel/asm/core/create_addresses.asm | 76 - .../asm/core/create_contract_account.asm | 62 - .../cpu/kernel/asm/core/create_receipt.asm | 249 -- evm/src/cpu/kernel/asm/core/exception.asm | 436 --- evm/src/cpu/kernel/asm/core/gas.asm | 129 - evm/src/cpu/kernel/asm/core/intrinsic_gas.asm | 84 - .../cpu/kernel/asm/core/jumpdest_analysis.asm | 344 -- evm/src/cpu/kernel/asm/core/log.asm | 272 -- evm/src/cpu/kernel/asm/core/nonce.asm | 49 - .../kernel/asm/core/precompiles/blake2_f.asm | 139 - .../kernel/asm/core/precompiles/bn_add.asm | 63 - .../kernel/asm/core/precompiles/bn_mul.asm | 58 - .../cpu/kernel/asm/core/precompiles/ecrec.asm | 60 - .../kernel/asm/core/precompiles/expmod.asm | 470 --- .../cpu/kernel/asm/core/precompiles/id.asm | 47 - .../cpu/kernel/asm/core/precompiles/main.asm | 69 - .../kernel/asm/core/precompiles/rip160.asm | 50 - .../kernel/asm/core/precompiles/sha256.asm | 50 - .../kernel/asm/core/precompiles/snarkv.asm | 130 - evm/src/cpu/kernel/asm/core/process_txn.asm | 472 --- .../cpu/kernel/asm/core/selfdestruct_list.asm | 78 - evm/src/cpu/kernel/asm/core/syscall.asm | 155 - evm/src/cpu/kernel/asm/core/terminate.asm | 225 -- .../cpu/kernel/asm/core/touched_addresses.asm | 112 - evm/src/cpu/kernel/asm/core/transfer.asm | 112 - evm/src/cpu/kernel/asm/core/util.asm | 88 - evm/src/cpu/kernel/asm/core/withdrawals.asm | 25 - evm/src/cpu/kernel/asm/curve/bls381/util.asm | 101 - .../bn254/curve_arithmetic/constants.asm | 88 - .../bn254/curve_arithmetic/curve_add.asm | 268 -- .../bn254/curve_arithmetic/curve_mul.asm | 41 - .../bn254/curve_arithmetic/final_exponent.asm | 326 -- .../asm/curve/bn254/curve_arithmetic/glv.asm | 116 - .../bn254/curve_arithmetic/miller_loop.asm | 325 -- .../asm/curve/bn254/curve_arithmetic/msm.asm | 73 - .../curve/bn254/curve_arithmetic/pairing.asm | 194 - .../bn254/curve_arithmetic/precomputation.asm | 35 - .../bn254/curve_arithmetic/twisted_curve.asm | 94 - .../bn254/field_arithmetic/degree_12_mul.asm | 303 -- .../bn254/field_arithmetic/degree_6_mul.asm | 435 --- .../bn254/field_arithmetic/frobenius.asm | 272 -- .../curve/bn254/field_arithmetic/inverse.asm | 66 - .../asm/curve/bn254/field_arithmetic/util.asm | 1100 ------ evm/src/cpu/kernel/asm/curve/common.asm | 25 - .../kernel/asm/curve/secp256k1/curve_add.asm | 287 -- .../kernel/asm/curve/secp256k1/ecrecover.asm | 186 - .../cpu/kernel/asm/curve/secp256k1/glv.asm | 104 - .../asm/curve/secp256k1/inverse_scalar.asm | 31 - .../cpu/kernel/asm/curve/secp256k1/lift_x.asm | 73 - .../cpu/kernel/asm/curve/secp256k1/moddiv.asm | 39 - .../asm/curve/secp256k1/precomputation.asm | 74 - evm/src/cpu/kernel/asm/curve/wnaf.asm | 74 - evm/src/cpu/kernel/asm/exp.asm | 102 - evm/src/cpu/kernel/asm/halt.asm | 2 - .../cpu/kernel/asm/hash/blake2/addresses.asm | 31 - .../cpu/kernel/asm/hash/blake2/blake2_f.asm | 141 - .../cpu/kernel/asm/hash/blake2/blake2b.asm | 14 - .../kernel/asm/hash/blake2/compression.asm | 265 -- .../kernel/asm/hash/blake2/g_functions.asm | 175 - evm/src/cpu/kernel/asm/hash/blake2/hash.asm | 55 - evm/src/cpu/kernel/asm/hash/blake2/iv.asm | 95 - evm/src/cpu/kernel/asm/hash/blake2/ops.asm | 21 - .../kernel/asm/hash/blake2/permutations.asm | 85 - evm/src/cpu/kernel/asm/hash/ripemd/box.asm | 96 - .../kernel/asm/hash/ripemd/compression.asm | 160 - .../cpu/kernel/asm/hash/ripemd/constants.asm | 117 - .../cpu/kernel/asm/hash/ripemd/functions.asm | 150 - evm/src/cpu/kernel/asm/hash/ripemd/main.asm | 131 - evm/src/cpu/kernel/asm/hash/ripemd/update.asm | 134 - .../cpu/kernel/asm/hash/sha2/compression.asm | 159 - .../cpu/kernel/asm/hash/sha2/constants.asm | 65 - evm/src/cpu/kernel/asm/hash/sha2/main.asm | 56 - .../kernel/asm/hash/sha2/message_schedule.asm | 219 -- evm/src/cpu/kernel/asm/hash/sha2/ops.asm | 143 - .../cpu/kernel/asm/hash/sha2/temp_words.asm | 32 - .../cpu/kernel/asm/hash/sha2/write_length.asm | 35 - .../kernel/asm/journal/account_created.asm | 13 - .../kernel/asm/journal/account_destroyed.asm | 32 - .../cpu/kernel/asm/journal/account_loaded.asm | 19 - .../kernel/asm/journal/account_touched.asm | 19 - .../kernel/asm/journal/balance_transfer.asm | 24 - .../cpu/kernel/asm/journal/code_change.asm | 18 - evm/src/cpu/kernel/asm/journal/journal.asm | 210 -- evm/src/cpu/kernel/asm/journal/log.asm | 21 - .../cpu/kernel/asm/journal/nonce_change.asm | 17 - evm/src/cpu/kernel/asm/journal/refund.asm | 15 - evm/src/cpu/kernel/asm/journal/revert.asm | 91 - .../cpu/kernel/asm/journal/storage_change.asm | 57 - .../cpu/kernel/asm/journal/storage_loaded.asm | 12 - evm/src/cpu/kernel/asm/main.asm | 96 - evm/src/cpu/kernel/asm/memory/core.asm | 474 --- evm/src/cpu/kernel/asm/memory/memcpy.asm | 106 - evm/src/cpu/kernel/asm/memory/memset.asm | 49 - evm/src/cpu/kernel/asm/memory/metadata.asm | 436 --- evm/src/cpu/kernel/asm/memory/packing.asm | 321 -- evm/src/cpu/kernel/asm/memory/syscalls.asm | 256 -- evm/src/cpu/kernel/asm/memory/txn_fields.asm | 39 - evm/src/cpu/kernel/asm/mpt/accounts.asm | 21 - evm/src/cpu/kernel/asm/mpt/delete/delete.asm | 45 - .../kernel/asm/mpt/delete/delete_branch.asm | 130 - .../asm/mpt/delete/delete_extension.asm | 79 - evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 288 -- .../asm/mpt/hash/hash_trie_specific.asm | 355 -- evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 131 - evm/src/cpu/kernel/asm/mpt/insert/insert.asm | 89 - .../asm/mpt/insert/insert_extension.asm | 213 -- .../cpu/kernel/asm/mpt/insert/insert_leaf.asm | 205 -- .../asm/mpt/insert/insert_trie_specific.asm | 95 - evm/src/cpu/kernel/asm/mpt/read.asm | 152 - .../kernel/asm/mpt/storage/storage_read.asm | 56 - .../kernel/asm/mpt/storage/storage_write.asm | 144 - evm/src/cpu/kernel/asm/mpt/util.asm | 232 -- evm/src/cpu/kernel/asm/rlp/decode.asm | 147 - evm/src/cpu/kernel/asm/rlp/encode.asm | 265 -- .../cpu/kernel/asm/rlp/encode_rlp_scalar.asm | 108 - .../cpu/kernel/asm/rlp/encode_rlp_string.asm | 79 - .../kernel/asm/rlp/increment_bounded_rlp.asm | 38 - evm/src/cpu/kernel/asm/rlp/num_bytes.asm | 30 - evm/src/cpu/kernel/asm/rlp/read_to_memory.asm | 38 - evm/src/cpu/kernel/asm/shift.asm | 20 - evm/src/cpu/kernel/asm/signed.asm | 216 -- .../asm/transactions/common_decoding.asm | 252 -- .../cpu/kernel/asm/transactions/router.asm | 64 - .../cpu/kernel/asm/transactions/type_0.asm | 173 - .../cpu/kernel/asm/transactions/type_1.asm | 138 - .../cpu/kernel/asm/transactions/type_2.asm | 145 - evm/src/cpu/kernel/asm/util/assertions.asm | 116 - evm/src/cpu/kernel/asm/util/basic_macros.asm | 485 --- evm/src/cpu/kernel/asm/util/keccak.asm | 64 - evm/src/cpu/kernel/asm/util/math.asm | 37 - evm/src/cpu/kernel/assembler.rs | 731 ---- evm/src/cpu/kernel/ast.rs | 84 - .../cpu/kernel/constants/context_metadata.rs | 87 - evm/src/cpu/kernel/constants/exc_bitfields.rs | 53 - .../cpu/kernel/constants/global_metadata.rs | 208 -- evm/src/cpu/kernel/constants/journal_entry.rs | 51 - evm/src/cpu/kernel/constants/mod.rs | 286 -- evm/src/cpu/kernel/constants/trie_type.rs | 49 - evm/src/cpu/kernel/constants/txn_fields.rs | 88 - evm/src/cpu/kernel/cost_estimator.rs | 36 - evm/src/cpu/kernel/evm_asm.pest | 47 - evm/src/cpu/kernel/interpreter.rs | 1806 ---------- evm/src/cpu/kernel/keccak_util.rs | 59 - evm/src/cpu/kernel/mod.rs | 28 - evm/src/cpu/kernel/opcodes.rs | 167 - evm/src/cpu/kernel/optimizer.rs | 285 -- evm/src/cpu/kernel/parser.rs | 210 -- evm/src/cpu/kernel/stack/mod.rs | 2 - evm/src/cpu/kernel/stack/permutations.rs | 278 -- .../cpu/kernel/stack/stack_manipulation.rs | 373 -- evm/src/cpu/kernel/tests/account_code.rs | 474 --- evm/src/cpu/kernel/tests/add11.rs | 312 -- evm/src/cpu/kernel/tests/balance.rs | 133 - evm/src/cpu/kernel/tests/bignum/mod.rs | 593 ---- .../kernel/tests/bignum/test_data/add_outputs | 225 -- .../tests/bignum/test_data/addmul_outputs | 1350 ------- .../tests/bignum/test_data/bignum_inputs | 15 - .../kernel/tests/bignum/test_data/cmp_outputs | 225 -- .../tests/bignum/test_data/iszero_outputs | 15 - .../tests/bignum/test_data/modexp_outputs | 486 --- .../bignum/test_data/modexp_outputs_full | 1575 --------- .../tests/bignum/test_data/modmul_outputs | 3150 ----------------- .../kernel/tests/bignum/test_data/mul_outputs | 225 -- .../kernel/tests/bignum/test_data/shr_outputs | 15 - .../kernel/tests/bignum/test_data/u128_inputs | 6 - evm/src/cpu/kernel/tests/blake2_f.rs | 135 - evm/src/cpu/kernel/tests/block_hash.rs | 130 - evm/src/cpu/kernel/tests/bls381.rs | 33 - evm/src/cpu/kernel/tests/bn254.rs | 253 -- evm/src/cpu/kernel/tests/core/access_lists.rs | 217 -- .../cpu/kernel/tests/core/create_addresses.rs | 118 - .../cpu/kernel/tests/core/intrinsic_gas.rs | 33 - .../kernel/tests/core/jumpdest_analysis.rs | 153 - evm/src/cpu/kernel/tests/core/mod.rs | 4 - evm/src/cpu/kernel/tests/ecc/bn_glv_test_data | 1049 ------ evm/src/cpu/kernel/tests/ecc/curve_ops.rs | 373 -- evm/src/cpu/kernel/tests/ecc/ecrecover.rs | 101 - .../cpu/kernel/tests/ecc/ecrecover_test_data | 184 - evm/src/cpu/kernel/tests/ecc/mod.rs | 2 - .../cpu/kernel/tests/ecc/secp_glv_test_data | 1048 ------ evm/src/cpu/kernel/tests/exp.rs | 43 - evm/src/cpu/kernel/tests/hash.rs | 137 - .../cpu/kernel/tests/kernel_consistency.rs | 13 - evm/src/cpu/kernel/tests/log.rs | 199 -- evm/src/cpu/kernel/tests/mod.rs | 32 - evm/src/cpu/kernel/tests/mpt/delete.rs | 177 - evm/src/cpu/kernel/tests/mpt/hash.rs | 141 - evm/src/cpu/kernel/tests/mpt/hex_prefix.rs | 93 - evm/src/cpu/kernel/tests/mpt/insert.rs | 241 -- evm/src/cpu/kernel/tests/mpt/load.rs | 265 -- evm/src/cpu/kernel/tests/mpt/mod.rs | 71 - evm/src/cpu/kernel/tests/mpt/read.rs | 54 - evm/src/cpu/kernel/tests/packing.rs | 30 - evm/src/cpu/kernel/tests/receipt.rs | 613 ---- evm/src/cpu/kernel/tests/rlp/decode.rs | 132 - evm/src/cpu/kernel/tests/rlp/encode.rs | 166 - evm/src/cpu/kernel/tests/rlp/mod.rs | 3 - evm/src/cpu/kernel/tests/rlp/num_bytes.rs | 47 - evm/src/cpu/kernel/tests/signed_syscalls.rs | 169 - .../kernel/tests/transaction_parsing/mod.rs | 1 - .../transaction_parsing/parse_type_0_txn.rs | 68 - evm/src/cpu/kernel/utils.rs | 73 - evm/src/cpu/membus.rs | 84 - evm/src/cpu/memio.rs | 367 -- evm/src/cpu/mod.rs | 21 - evm/src/cpu/modfp254.rs | 53 - evm/src/cpu/pc.rs | 46 - evm/src/cpu/push0.rs | 36 - evm/src/cpu/shift.rs | 123 - evm/src/cpu/simple_logic/eq_iszero.rs | 188 - evm/src/cpu/simple_logic/mod.rs | 32 - evm/src/cpu/simple_logic/not.rs | 66 - evm/src/cpu/stack.rs | 718 ---- evm/src/cpu/syscalls_exceptions.rs | 308 -- evm/src/curve_pairings.rs | 513 --- evm/src/extension_tower.rs | 1321 ------- evm/src/fixed_recursive_verifier.rs | 1653 --------- evm/src/generation/mod.rs | 335 -- evm/src/generation/mpt.rs | 427 --- evm/src/generation/prover_input.rs | 627 ---- evm/src/generation/rlp.rs | 22 - evm/src/generation/state.rs | 206 -- evm/src/generation/trie_extractor.rs | 313 -- evm/src/get_challenges.rs | 223 -- evm/src/keccak/columns.rs | 134 - evm/src/keccak/constants.rs | 157 - evm/src/keccak/keccak_stark.rs | 777 ---- evm/src/keccak/logic.rs | 65 - evm/src/keccak/mod.rs | 5 - evm/src/keccak/round_flags.rs | 74 - evm/src/keccak_sponge/columns.rs | 156 - evm/src/keccak_sponge/keccak_sponge_stark.rs | 879 ----- evm/src/keccak_sponge/mod.rs | 6 - evm/src/lib.rs | 211 -- evm/src/logic.rs | 398 --- evm/src/memory/columns.rs | 49 - evm/src/memory/memory_stark.rs | 612 ---- evm/src/memory/mod.rs | 13 - evm/src/memory/segments.rs | 212 -- evm/src/proof.rs | 814 ----- evm/src/prover.rs | 362 -- evm/src/recursive_verifier.rs | 828 ----- evm/src/util.rs | 252 -- evm/src/verifier.rs | 421 --- evm/src/witness/errors.rs | 41 - evm/src/witness/gas.rs | 56 - evm/src/witness/memory.rs | 282 -- evm/src/witness/mod.rs | 8 - evm/src/witness/operation.rs | 1003 ------ evm/src/witness/state.rs | 45 - evm/src/witness/traces.rs | 242 -- evm/src/witness/transition.rs | 504 --- evm/src/witness/util.rs | 393 -- evm/tests/add11_yml.rs | 177 - evm/tests/basic_smart_contract.rs | 214 -- evm/tests/empty_txn_list.rs | 150 - evm/tests/erc20.rs | 285 -- evm/tests/erc721.rs | 312 -- evm/tests/log_opcode.rs | 771 ---- evm/tests/self_balance_gas_cost.rs | 196 - evm/tests/selfdestruct.rs | 153 - evm/tests/simple_transfer.rs | 169 - evm/tests/withdrawals.rs | 94 - 335 files changed, 12 insertions(+), 69991 deletions(-) delete mode 100644 evm/.cargo/katex-header.html delete mode 100644 evm/Cargo.toml delete mode 100644 evm/LICENSE-APACHE delete mode 100644 evm/LICENSE-MIT delete mode 100644 evm/README.md delete mode 100644 evm/benches/stack_manipulation.rs delete mode 100644 evm/spec/.gitignore delete mode 100644 evm/spec/Makefile delete mode 100644 evm/spec/bibliography.bib delete mode 100644 evm/spec/cpulogic.tex delete mode 100644 evm/spec/framework.tex delete mode 100644 evm/spec/introduction.tex delete mode 100644 evm/spec/mpts.tex delete mode 100644 evm/spec/tables.tex delete mode 100644 evm/spec/tables/arithmetic.tex delete mode 100644 evm/spec/tables/byte-packing.tex delete mode 100644 evm/spec/tables/cpu.tex delete mode 100644 evm/spec/tables/keccak-f.tex delete mode 100644 evm/spec/tables/keccak-sponge.tex delete mode 100644 evm/spec/tables/logic.tex delete mode 100644 evm/spec/tables/memory.tex delete mode 100644 evm/spec/zkevm.pdf delete mode 100644 evm/spec/zkevm.tex delete mode 100644 evm/src/all_stark.rs delete mode 100644 evm/src/arithmetic/addcy.rs delete mode 100644 evm/src/arithmetic/arithmetic_stark.rs delete mode 100644 evm/src/arithmetic/byte.rs delete mode 100644 evm/src/arithmetic/columns.rs delete mode 100644 evm/src/arithmetic/divmod.rs delete mode 100644 evm/src/arithmetic/mod.rs delete mode 100644 evm/src/arithmetic/modular.rs delete mode 100644 evm/src/arithmetic/mul.rs delete mode 100644 evm/src/arithmetic/shift.rs delete mode 100644 evm/src/arithmetic/utils.rs delete mode 100644 evm/src/bin/assemble.rs delete mode 100644 evm/src/byte_packing/byte_packing_stark.rs delete mode 100644 evm/src/byte_packing/columns.rs delete mode 100644 evm/src/byte_packing/mod.rs delete mode 100644 evm/src/cpu/byte_unpacking.rs delete mode 100644 evm/src/cpu/clock.rs delete mode 100644 evm/src/cpu/columns/general.rs delete mode 100644 evm/src/cpu/columns/mod.rs delete mode 100644 evm/src/cpu/columns/ops.rs delete mode 100644 evm/src/cpu/contextops.rs delete mode 100644 evm/src/cpu/control_flow.rs delete mode 100644 evm/src/cpu/cpu_stark.rs delete mode 100644 evm/src/cpu/decode.rs delete mode 100644 evm/src/cpu/dup_swap.rs delete mode 100644 evm/src/cpu/gas.rs delete mode 100644 evm/src/cpu/halt.rs delete mode 100644 evm/src/cpu/jumps.rs delete mode 100644 evm/src/cpu/kernel/aggregator.rs delete mode 100644 evm/src/cpu/kernel/asm/account_code.asm delete mode 100644 evm/src/cpu/kernel/asm/balance.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/add.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/addmul.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/cmp.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/isone.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/iszero.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/modexp.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/modmul.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/mul.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/shr.asm delete mode 100644 evm/src/cpu/kernel/asm/bignum/util.asm delete mode 100644 evm/src/cpu/kernel/asm/bloom_filter.asm delete mode 100644 evm/src/cpu/kernel/asm/core/access_lists.asm delete mode 100644 evm/src/cpu/kernel/asm/core/call.asm delete mode 100644 evm/src/cpu/kernel/asm/core/call_gas.asm delete mode 100644 evm/src/cpu/kernel/asm/core/create.asm delete mode 100644 evm/src/cpu/kernel/asm/core/create_addresses.asm delete mode 100644 evm/src/cpu/kernel/asm/core/create_contract_account.asm delete mode 100644 evm/src/cpu/kernel/asm/core/create_receipt.asm delete mode 100644 evm/src/cpu/kernel/asm/core/exception.asm delete mode 100644 evm/src/cpu/kernel/asm/core/gas.asm delete mode 100644 evm/src/cpu/kernel/asm/core/intrinsic_gas.asm delete mode 100644 evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm delete mode 100644 evm/src/cpu/kernel/asm/core/log.asm delete mode 100644 evm/src/cpu/kernel/asm/core/nonce.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/expmod.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/id.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/main.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/rip160.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/sha256.asm delete mode 100644 evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm delete mode 100644 evm/src/cpu/kernel/asm/core/process_txn.asm delete mode 100644 evm/src/cpu/kernel/asm/core/selfdestruct_list.asm delete mode 100644 evm/src/cpu/kernel/asm/core/syscall.asm delete mode 100644 evm/src/cpu/kernel/asm/core/terminate.asm delete mode 100644 evm/src/cpu/kernel/asm/core/touched_addresses.asm delete mode 100644 evm/src/cpu/kernel/asm/core/transfer.asm delete mode 100644 evm/src/cpu/kernel/asm/core/util.asm delete mode 100644 evm/src/cpu/kernel/asm/core/withdrawals.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bls381/util.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/constants.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/glv.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_12_mul.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_6_mul.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/frobenius.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/common.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/inverse_scalar.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/lift_x.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/moddiv.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm delete mode 100644 evm/src/cpu/kernel/asm/curve/wnaf.asm delete mode 100644 evm/src/cpu/kernel/asm/exp.asm delete mode 100644 evm/src/cpu/kernel/asm/halt.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/addresses.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/blake2b.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/compression.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/hash.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/iv.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/ops.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/blake2/permutations.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/box.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/compression.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/constants.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/functions.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/main.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/ripemd/update.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/compression.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/constants.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/main.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/ops.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/temp_words.asm delete mode 100644 evm/src/cpu/kernel/asm/hash/sha2/write_length.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/account_created.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/account_destroyed.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/account_loaded.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/account_touched.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/balance_transfer.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/code_change.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/journal.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/log.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/nonce_change.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/refund.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/revert.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/storage_change.asm delete mode 100644 evm/src/cpu/kernel/asm/journal/storage_loaded.asm delete mode 100644 evm/src/cpu/kernel/asm/main.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/core.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/memcpy.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/memset.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/metadata.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/packing.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/syscalls.asm delete mode 100644 evm/src/cpu/kernel/asm/memory/txn_fields.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/accounts.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/delete/delete.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/hash/hash.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/hex_prefix.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/insert/insert.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/read.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/util.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/decode.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/encode.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/num_bytes.asm delete mode 100644 evm/src/cpu/kernel/asm/rlp/read_to_memory.asm delete mode 100644 evm/src/cpu/kernel/asm/shift.asm delete mode 100644 evm/src/cpu/kernel/asm/signed.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/common_decoding.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/router.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/type_0.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/type_1.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/type_2.asm delete mode 100644 evm/src/cpu/kernel/asm/util/assertions.asm delete mode 100644 evm/src/cpu/kernel/asm/util/basic_macros.asm delete mode 100644 evm/src/cpu/kernel/asm/util/keccak.asm delete mode 100644 evm/src/cpu/kernel/asm/util/math.asm delete mode 100644 evm/src/cpu/kernel/assembler.rs delete mode 100644 evm/src/cpu/kernel/ast.rs delete mode 100644 evm/src/cpu/kernel/constants/context_metadata.rs delete mode 100644 evm/src/cpu/kernel/constants/exc_bitfields.rs delete mode 100644 evm/src/cpu/kernel/constants/global_metadata.rs delete mode 100644 evm/src/cpu/kernel/constants/journal_entry.rs delete mode 100644 evm/src/cpu/kernel/constants/mod.rs delete mode 100644 evm/src/cpu/kernel/constants/trie_type.rs delete mode 100644 evm/src/cpu/kernel/constants/txn_fields.rs delete mode 100644 evm/src/cpu/kernel/cost_estimator.rs delete mode 100644 evm/src/cpu/kernel/evm_asm.pest delete mode 100644 evm/src/cpu/kernel/interpreter.rs delete mode 100644 evm/src/cpu/kernel/keccak_util.rs delete mode 100644 evm/src/cpu/kernel/mod.rs delete mode 100644 evm/src/cpu/kernel/opcodes.rs delete mode 100644 evm/src/cpu/kernel/optimizer.rs delete mode 100644 evm/src/cpu/kernel/parser.rs delete mode 100644 evm/src/cpu/kernel/stack/mod.rs delete mode 100644 evm/src/cpu/kernel/stack/permutations.rs delete mode 100644 evm/src/cpu/kernel/stack/stack_manipulation.rs delete mode 100644 evm/src/cpu/kernel/tests/account_code.rs delete mode 100644 evm/src/cpu/kernel/tests/add11.rs delete mode 100644 evm/src/cpu/kernel/tests/balance.rs delete mode 100644 evm/src/cpu/kernel/tests/bignum/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/add_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/addmul_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/bignum_inputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/cmp_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/iszero_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs_full delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/modmul_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/mul_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/shr_outputs delete mode 100644 evm/src/cpu/kernel/tests/bignum/test_data/u128_inputs delete mode 100644 evm/src/cpu/kernel/tests/blake2_f.rs delete mode 100644 evm/src/cpu/kernel/tests/block_hash.rs delete mode 100644 evm/src/cpu/kernel/tests/bls381.rs delete mode 100644 evm/src/cpu/kernel/tests/bn254.rs delete mode 100644 evm/src/cpu/kernel/tests/core/access_lists.rs delete mode 100644 evm/src/cpu/kernel/tests/core/create_addresses.rs delete mode 100644 evm/src/cpu/kernel/tests/core/intrinsic_gas.rs delete mode 100644 evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs delete mode 100644 evm/src/cpu/kernel/tests/core/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/ecc/bn_glv_test_data delete mode 100644 evm/src/cpu/kernel/tests/ecc/curve_ops.rs delete mode 100644 evm/src/cpu/kernel/tests/ecc/ecrecover.rs delete mode 100644 evm/src/cpu/kernel/tests/ecc/ecrecover_test_data delete mode 100644 evm/src/cpu/kernel/tests/ecc/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/ecc/secp_glv_test_data delete mode 100644 evm/src/cpu/kernel/tests/exp.rs delete mode 100644 evm/src/cpu/kernel/tests/hash.rs delete mode 100644 evm/src/cpu/kernel/tests/kernel_consistency.rs delete mode 100644 evm/src/cpu/kernel/tests/log.rs delete mode 100644 evm/src/cpu/kernel/tests/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/delete.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/hash.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/hex_prefix.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/insert.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/load.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/mpt/read.rs delete mode 100644 evm/src/cpu/kernel/tests/packing.rs delete mode 100644 evm/src/cpu/kernel/tests/receipt.rs delete mode 100644 evm/src/cpu/kernel/tests/rlp/decode.rs delete mode 100644 evm/src/cpu/kernel/tests/rlp/encode.rs delete mode 100644 evm/src/cpu/kernel/tests/rlp/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/rlp/num_bytes.rs delete mode 100644 evm/src/cpu/kernel/tests/signed_syscalls.rs delete mode 100644 evm/src/cpu/kernel/tests/transaction_parsing/mod.rs delete mode 100644 evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs delete mode 100644 evm/src/cpu/kernel/utils.rs delete mode 100644 evm/src/cpu/membus.rs delete mode 100644 evm/src/cpu/memio.rs delete mode 100644 evm/src/cpu/mod.rs delete mode 100644 evm/src/cpu/modfp254.rs delete mode 100644 evm/src/cpu/pc.rs delete mode 100644 evm/src/cpu/push0.rs delete mode 100644 evm/src/cpu/shift.rs delete mode 100644 evm/src/cpu/simple_logic/eq_iszero.rs delete mode 100644 evm/src/cpu/simple_logic/mod.rs delete mode 100644 evm/src/cpu/simple_logic/not.rs delete mode 100644 evm/src/cpu/stack.rs delete mode 100644 evm/src/cpu/syscalls_exceptions.rs delete mode 100644 evm/src/curve_pairings.rs delete mode 100644 evm/src/extension_tower.rs delete mode 100644 evm/src/fixed_recursive_verifier.rs delete mode 100644 evm/src/generation/mod.rs delete mode 100644 evm/src/generation/mpt.rs delete mode 100644 evm/src/generation/prover_input.rs delete mode 100644 evm/src/generation/rlp.rs delete mode 100644 evm/src/generation/state.rs delete mode 100644 evm/src/generation/trie_extractor.rs delete mode 100644 evm/src/get_challenges.rs delete mode 100644 evm/src/keccak/columns.rs delete mode 100644 evm/src/keccak/constants.rs delete mode 100644 evm/src/keccak/keccak_stark.rs delete mode 100644 evm/src/keccak/logic.rs delete mode 100644 evm/src/keccak/mod.rs delete mode 100644 evm/src/keccak/round_flags.rs delete mode 100644 evm/src/keccak_sponge/columns.rs delete mode 100644 evm/src/keccak_sponge/keccak_sponge_stark.rs delete mode 100644 evm/src/keccak_sponge/mod.rs delete mode 100644 evm/src/lib.rs delete mode 100644 evm/src/logic.rs delete mode 100644 evm/src/memory/columns.rs delete mode 100644 evm/src/memory/memory_stark.rs delete mode 100644 evm/src/memory/mod.rs delete mode 100644 evm/src/memory/segments.rs delete mode 100644 evm/src/proof.rs delete mode 100644 evm/src/prover.rs delete mode 100644 evm/src/recursive_verifier.rs delete mode 100644 evm/src/util.rs delete mode 100644 evm/src/verifier.rs delete mode 100644 evm/src/witness/errors.rs delete mode 100644 evm/src/witness/gas.rs delete mode 100644 evm/src/witness/memory.rs delete mode 100644 evm/src/witness/mod.rs delete mode 100644 evm/src/witness/operation.rs delete mode 100644 evm/src/witness/state.rs delete mode 100644 evm/src/witness/traces.rs delete mode 100644 evm/src/witness/transition.rs delete mode 100644 evm/src/witness/util.rs delete mode 100644 evm/tests/add11_yml.rs delete mode 100644 evm/tests/basic_smart_contract.rs delete mode 100644 evm/tests/empty_txn_list.rs delete mode 100644 evm/tests/erc20.rs delete mode 100644 evm/tests/erc721.rs delete mode 100644 evm/tests/log_opcode.rs delete mode 100644 evm/tests/self_balance_gas_cost.rs delete mode 100644 evm/tests/selfdestruct.rs delete mode 100644 evm/tests/simple_transfer.rs delete mode 100644 evm/tests/withdrawals.rs diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 9da841bca7..48b06cfd9d 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -51,14 +51,6 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 - - name: Check in evm subdirectory - run: cargo check --manifest-path evm/Cargo.toml - env: - RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 - RUST_LOG: 1 - CARGO_INCREMENTAL: 1 - RUST_BACKTRACE: 1 - - name: Run cargo test run: cargo test --workspace env: diff --git a/Cargo.toml b/Cargo.toml index 7a51417582..ede92a6228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["evm", "field", "maybe_rayon", "plonky2", "starky", "util"] +members = ["field", "maybe_rayon", "plonky2", "starky", "util"] resolver = "2" [profile.release] diff --git a/README.md b/README.md index 6ee6b82a00..f6d4e7f0e9 100644 --- a/README.md +++ b/README.md @@ -179,8 +179,14 @@ Plonky2's default hash function is Poseidon, configured with 8 full rounds, 22 p ## Links -- [System Zero](https://github.com/0xPolygonZero/system-zero), a zkVM built on top of Starky (no longer maintained) -- [Waksman](https://github.com/0xPolygonZero/plonky2-waksman), Plonky2 gadgets for permutation checking using Waksman networks (no longer maintained) -- [Insertion](https://github.com/0xPolygonZero/plonky2-insertion), Plonky2 gadgets for insertion into a list (no longer maintained) -- [u32](https://github.com/0xPolygonZero/plonky2-u32), Plonky2 gadgets for u32 arithmetic (no longer actively maintained) -- [ECDSA](https://github.com/0xPolygonZero/plonky2-ecdsa), Plonky2 gadgets for the ECDSA algorithm (no longer actively maintained) +#### Actively maintained + +- [Polygon Zero's zkEVM](https://github.com/0xPolygonZero/zk_evm), an efficient Type 1 zkEVM built on top of Starky and plonky2 + +#### No longer maintained + +- [System Zero](https://github.com/0xPolygonZero/system-zero), a zkVM built on top of Starky +- [Waksman](https://github.com/0xPolygonZero/plonky2-waksman), Plonky2 gadgets for permutation checking using Waksman networks +- [Insertion](https://github.com/0xPolygonZero/plonky2-insertion), Plonky2 gadgets for insertion into a list +- [u32](https://github.com/0xPolygonZero/plonky2-u32), Plonky2 gadgets for u32 arithmetic +- [ECDSA](https://github.com/0xPolygonZero/plonky2-ecdsa), Plonky2 gadgets for the ECDSA algorithm diff --git a/evm/.cargo/katex-header.html b/evm/.cargo/katex-header.html deleted file mode 100644 index 20723b5d27..0000000000 --- a/evm/.cargo/katex-header.html +++ /dev/null @@ -1 +0,0 @@ -../../.cargo/katex-header.html \ No newline at end of file diff --git a/evm/Cargo.toml b/evm/Cargo.toml deleted file mode 100644 index df8401b059..0000000000 --- a/evm/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "plonky2_evm" -description = "Implementation of STARKs for the Ethereum Virtual Machine" -version = "0.1.1" -license = "MIT or Apache-2.0" -authors = ["Daniel Lubarov ", "William Borgeaud "] -readme = "README.md" -repository = "https://github.com/0xPolygonZero/plonky2" -keywords = ["EVM", "STARK", "Ethereum"] -categories = ["cryptography"] -edition = "2021" - -[dependencies] -anyhow = "1.0.40" -bytes = "1.4.0" -env_logger = "0.10.0" -eth_trie_utils = { git = "https://github.com/0xPolygonZero/eth_trie_utils.git", rev = "7fc3c3f54b3cec9c6fc5ffc5230910bd1cb77f76" } -ethereum-types = "0.14.0" -hex = { version = "0.4.3", optional = true } -hex-literal = "0.4.1" -itertools = "0.11.0" -keccak-hash = "0.10.0" -log = "0.4.14" -plonky2_maybe_rayon = { path = "../maybe_rayon" } -num = "0.4.0" -num-bigint = "0.4.3" -once_cell = "1.13.0" -pest = "2.1.3" -pest_derive = "2.1.0" -plonky2 = { path = "../plonky2", features = ["timing"] } -plonky2_util = { path = "../util" } -starky = { path = "../starky" } -rand = "0.8.5" -rand_chacha = "0.3.1" -rlp = "0.5.1" -rlp-derive = "0.1.0" -serde = { version = "1.0.144", features = ["derive"] } -static_assertions = "1.1.0" -hashbrown = { version = "0.14.0" } -tiny-keccak = "2.0.2" -serde_json = "1.0" - -[target.'cfg(not(target_env = "msvc"))'.dependencies] -jemallocator = "0.5.0" - -[dev-dependencies] -criterion = "0.5.1" -hex = "0.4.3" -ripemd = "0.1.3" -sha2 = "0.10.6" - -[features] -default = ["parallel"] -asmtools = ["hex"] -parallel = [ - "plonky2/parallel", - "plonky2_maybe_rayon/parallel", - "starky/parallel" -] - -[[bin]] -name = "assemble" -required-features = ["asmtools"] - -[[bench]] -name = "stack_manipulation" -harness = false - -# Display math equations properly in documentation -[package.metadata.docs.rs] -rustdoc-args = ["--html-in-header", ".cargo/katex-header.html"] diff --git a/evm/LICENSE-APACHE b/evm/LICENSE-APACHE deleted file mode 100644 index 1b5ec8b78e..0000000000 --- a/evm/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/evm/LICENSE-MIT b/evm/LICENSE-MIT deleted file mode 100644 index 72dc60d84b..0000000000 --- a/evm/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/evm/README.md b/evm/README.md deleted file mode 100644 index a5c201550b..0000000000 --- a/evm/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Provable Stateless ZK-EVM - -Included here is an implementation of a stateless, recursive ZK-EVM client implemented using Plonky2. It currently supports the full Merkle-Patricia Trie and has all Shanghai opcodes implemented. - -## Performance - -This implementation is able to provide transaction level proofs which are then recursively aggregated into a block proof. This means that proofs for a block can be efficiently distributed across a cluster of computers. As these proofs use Plonky2 they are CPU and Memory bound. The ability to scale horizontally across transactions increases the total performance of the system dramatically. End-to-end workflows are currently in progress to support this proving mode against live evm networks. - -Furthermore the implementation itself is highly optimized to provide fast proving times on generally available cloud instances and does not require GPUs or special hardware. - -## Ethereum Compatibility - -The aim of this module is to initially provide full ethereum compatibility. Today, all [EVM tests](https://github.com/0xPolygonZero/evm-tests) for the Shanghai hardfork are implemented. Work is progressing on supporting the upcoming [Cancun](https://github.com/0xPolygonZero/plonky2/labels/cancun) EVM changes. Furthermore, this prover uses the full ethereum state tree and hashing modes. - -## Audits - -Audits for the ZK-EVM will begin on November 27th, 2023. See the [Audit RC1 Milestone](https://github.com/0xPolygonZero/plonky2/milestone/2?closed=1). This README will be updated with the proper branches and hashes when the audit has commenced. - -## Documentation / Specification - -The current specification is located in the [/spec](/spec) directory, with the most currently up-to-date PDF [available here](https://github.com/0xPolygonZero/plonky2/blob/main/evm/spec/zkevm.pdf). Further documentation will be made over the coming months. - -## License -Copyright (c) 2023 PT Services DMCC - -Licensed under either of: -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -The SPDX license identifier for this project is `MIT OR Apache-2.0`. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/evm/benches/stack_manipulation.rs b/evm/benches/stack_manipulation.rs deleted file mode 100644 index 20f865120f..0000000000 --- a/evm/benches/stack_manipulation.rs +++ /dev/null @@ -1,75 +0,0 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use plonky2_evm::cpu::kernel::assemble_to_bytes; - -fn criterion_benchmark(c: &mut Criterion) { - rotl_group(c); - rotr_group(c); - insert_group(c); - delete_group(c); - replace_group(c); - shuffle_group(c); - misc_group(c); -} - -fn rotl_group(c: &mut Criterion) { - let mut group = c.benchmark_group("rotl"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (b, c, d, e, f, g, h, a)")) - }); -} - -fn rotr_group(c: &mut Criterion) { - let mut group = c.benchmark_group("rotr"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (h, a, b, c, d, e, f, g)")) - }); -} - -fn insert_group(c: &mut Criterion) { - let mut group = c.benchmark_group("insert"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (a, b, c, d, 123, e, f, g, h)")) - }); -} - -fn delete_group(c: &mut Criterion) { - let mut group = c.benchmark_group("delete"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (a, b, c, e, f, g, h)")) - }); -} - -fn replace_group(c: &mut Criterion) { - let mut group = c.benchmark_group("replace"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (a, b, c, 5, e, f, g, h)")) - }); -} - -fn shuffle_group(c: &mut Criterion) { - let mut group = c.benchmark_group("shuffle"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, d, e, f, g, h) -> (g, d, h, a, f, e, b, c)")) - }); -} - -fn misc_group(c: &mut Criterion) { - let mut group = c.benchmark_group("misc"); - group.sample_size(10); - group.bench_function(BenchmarkId::from_parameter(8), |b| { - b.iter(|| assemble("%stack (a, b, c, a, e, f, g, h) -> (g, 1, h, g, f, 3, b, b)")) - }); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); - -fn assemble(code: &str) { - assemble_to_bytes(&[code.into()]); -} diff --git a/evm/spec/.gitignore b/evm/spec/.gitignore deleted file mode 100644 index ba6d400798..0000000000 --- a/evm/spec/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -## Files generated by pdflatex, bibtex, etc. -*.aux -*.log -*.out -*.toc -*.bbl -*.blg diff --git a/evm/spec/Makefile b/evm/spec/Makefile deleted file mode 100644 index 979545288e..0000000000 --- a/evm/spec/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -DOCNAME=zkevm - -all: pdf - -.PHONY: clean - -quick: - pdflatex $(DOCNAME).tex - -pdf: - pdflatex $(DOCNAME).tex - bibtex $(DOCNAME).aux - pdflatex $(DOCNAME).tex - pdflatex $(DOCNAME).tex - -view: pdf - open $(DOCNAME).pdf - -clean: - rm -f *.blg *.bbl *.aux *.log diff --git a/evm/spec/bibliography.bib b/evm/spec/bibliography.bib deleted file mode 100644 index 1d83d297e9..0000000000 --- a/evm/spec/bibliography.bib +++ /dev/null @@ -1,30 +0,0 @@ -@misc{stark, - author = {Eli Ben-Sasson and - Iddo Bentov and - Yinon Horesh and - Michael Riabzev}, - title = {Scalable, transparent, and post-quantum secure computational integrity}, - howpublished = {Cryptology ePrint Archive, Report 2018/046}, - year = {2018}, - note = {\url{https://ia.cr/2018/046}}, -} - -@misc{plonk, - author = {Ariel Gabizon and - Zachary J. Williamson and - Oana Ciobotaru}, - title = {PLONK: Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge}, - howpublished = {Cryptology ePrint Archive, Report 2019/953}, - year = {2019}, - note = {\url{https://ia.cr/2019/953}}, -} - -@article{yellowpaper, - title={Ethereum: A secure decentralised generalised transaction ledger}, - author={Wood, Gavin and others}, - journal={Ethereum project yellow paper}, - volume={151}, - number={2014}, - pages={1--32}, - year={2014} -} diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex deleted file mode 100644 index 318e2db487..0000000000 --- a/evm/spec/cpulogic.tex +++ /dev/null @@ -1,285 +0,0 @@ -\section{CPU logic} -\label{cpulogic} - -The CPU is in charge of coordinating the different STARKs, proving the correct execution of the instructions it reads and guaranteeing -that the final state of the EVM corresponds to the starting state after executing the input transaction. All design choices were made -to make sure these properties can be adequately translated into constraints of degree at most 3 while minimizing the size of the different -table traces (number of columns and number of rows). - -In this section, we will detail some of these choices. - -\subsection{Kernel} -The kernel is in charge of the proving logic. This section aims at providing a high level overview of this logic. For details about any specific part of the logic, one can consult the various ``asm'' files in the \href{https://github.com/0xPolygonZero/plonky2/tree/main/evm/src/cpu/kernel}{``kernel'' folder}. - -We prove one transaction at a time. These proofs can later be aggregated recursively to prove a block. Proof aggregation is however not in the scope of this section. Here, we assume that we have an initial state of the EVM, and we wish to prove that a single transaction was correctly executed, leading to a correct update of the state. - -Since we process one transaction at a time, a few intermediary values need to be provided by the prover. Indeed, to prove that the registers in the EVM state are correctly updated, we need to have access to their initial values. When aggregating proofs, we can also constrain those values to match from one transaction to the next. Let us consider the example of the transaction number. Let $n$ be the number of transactions executed so far in the current block. If the current proof is not a dummy one (we are indeed executing a transaction), then the transaction number should be updated: $n := n+1$. Otherwise, the number remains unchanged. We can easily constrain this update. When aggregating the previous transaction proof ($lhs$) with the current one ($rhs$), we also need to check that the output transaction number of $lhs$ is the same as the input transaction number of $rhs$. - -Those prover provided values are stored in memory prior to entering the kernel, and are used in the kernel to assert correct updates. The list of prover provided values necessary to the kernel is the following: -\begin{enumerate} - \item the previous transaction number: $t_n$, - \item the gas used before executing the current transaction: $g\_u_0$, - \item the gas used after executing the current transaction: $g\_u_1$, - \item the state, transaction and receipts MPTs before executing the current transaction: $\texttt{tries}_0$, - \item the hash of all MPTs before executing the current transaction: $\texttt{digests}_0$, - \item the hash of all MPTs after executing the current transaction: $\texttt{digests}_1$, - \item the RLP encoding of the transaction. -\end{enumerate} - -\paragraph*{Initialization:} The first step consists in initializing: -\begin{itemize} - \item The shift table: it maps the number of bit shifts $s$ with its shifted value $1 << s$. Note that $0 \leq s \leq 255$. - \item The initial MPTs: the initial state, transaction and receipt tries $\texttt{tries}_0$ are loaded from memory and hashed. The hashes are then compared to $\texttt{digests}\_0$. - \item We load the transaction number $t\_n$ and the current gas used $g\_u_0$ from memory. -\end{itemize} - -If no transaction is provided, we can halt after this initialization. Otherwise, we start processing the transaction. The transaction is provided as its RLP encoding. We can deduce the various transaction fields (such as its type or the transfer value) from its encoding. Based on this, the kernel updates the state trie by executing the transaction. Processing the transaction also includes updating the transactions MPT with the transaction at hand. - -The processing of the transaction returns a boolean ``success'' that indicates whether the transaction was executed successfully, along with the leftover gas. - -The following step is then to update the receipts MPT. Here, we update the transaction's bloom filter. We store ``success'', the leftover gas, the transaction bloom filter and the logs in memory. We also store some additional information that facilitates the RLP encoding of the receipts later. - -If there are any withdrawals, they are performed at this stage. - -Finally, once the three MPTs have been updated, we need to carry out final checks: -\begin{itemize} - \item the gas used after the execution is equal to $g\_u_1$, - \item the new transaction number is $n+1$ if there was a transaction, - \item the three MPTs are hashed and checked against $\texttt{digests}_1$. -\end{itemize} -Once those final checks are performed, the program halts. - -\subsection{Simple opcodes VS Syscalls} -For simplicity and efficiency, EVM opcodes are categorized into two groups: ``simple opcodes'' and ``syscalls''. Simple opcodes are generated directly in Rust, in \href{https://github.com/0xPolygonZero/plonky2/blob/main/evm/src/witness/operation.rs}{operation.rs}. Every call to a simple opcode adds exactly one row to the \href{https://github.com/0xPolygonZero/plonky2/blob/main/evm/spec/tables/cpu.tex}{cpu table}. Syscalls are more complex structures written with simple opcodes, in the kernel. - -Whenever we encounter a syscall, we switch to kernel mode and execute its associated code. At the end of each syscall, we run EXIT\_KERNEL, which resets the kernel mode to its state right before the syscall. It also sets the PC to point to the opcode right after the syscall. - -Exceptions are handled differently for simple opcodes and syscalls. When necessary, simple opcodes throw an exception (see \ref{exceptions}). This activates the ``exception flag'' in the CPU and runs the exception operations. On the other hand, syscalls handle exceptions in the kernel directly. - -\subsection{Privileged instructions} - -To ease and speed-up proving time, the zkEVM supports custom, privileged instructions that can only be executed by the kernel. -Any appearance of those privileged instructions in a contract bytecode for instance would result in an unprovable state. - -In what follows, we denote by $p_{BN}$ the characteristic of the BN254 curve base field, curve for which Ethereum supports the -ecAdd, ecMul and ecPairing precompiles. - -\begin{enumerate}[align=left] - \item[0x0C.] \texttt{ADDFP254}. Pops 2 elements from the stack interpreted as BN254 base field elements, and pushes their addition modulo $p_{BN}$ onto the stack. - - \item[0x0D.] \texttt{MULFP254}. Pops 2 elements from the stack interpreted as BN254 base field elements, and pushes their product modulo $p_{BN}$ onto the stack. - - \item[0x0E.] \texttt{SUBFP254}. Pops 2 elements from the stack interpreted as BN254 base field elements, and pushes their difference modulo $p_{BN}$ onto the stack. - This instruction behaves similarly to the SUB (0x03) opcode, in that we subtract the second element of the stack from the initial (top) one. - - \item[0x0F.] \texttt{SUBMOD}. Pops 3 elements from the stack, and pushes the modular difference of the first two elements of the stack by the third one. - It is similar to the SUB instruction, with an extra pop for the custom modulus. - - \item[0x21.] \texttt{KECCAK\_GENERAL}. Pops 2 elements (a Memory address, followed by a length $\ell$) and pushes the hash of the memory portion starting at the - constructed address and of length $\ell$. It is similar to KECCAK256 (0x20) instruction, but can be applied to any memory section (i.e. even privileged ones). - - \item[0x49.] \texttt{PROVER\_INPUT}. Pushes a single prover input onto the stack. - - \item[0xC0-0xDF.] \texttt{MSTORE\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a value), and pushes - a new address' onto the stack. The value is being decomposed into bytes and written to memory, starting from the fetched address. The new address being pushed is computed as the - initial address + the length of the byte sequence being written to memory. Note that similarly to PUSH (0x60-0x7F) instructions, there are 32 MSTORE\_32BYTES instructions, each - corresponding to a target byte length (length 0 is ignored, for the same reasons as MLOAD\_32BYTES, see below). Writing to memory an integer fitting in $n$ bytes with a length $\ell < n$ will - result in the integer being truncated. On the other hand, specifying a length $\ell$ greater than the byte size of the value being written will result in padding with zeroes. This - process is heavily used when resetting memory sections (by calling MSTORE\_32BYTES\_32 with the value 0). - - \item[0xF6.] \texttt{GET\_CONTEXT}. Pushes the current context onto the stack. The kernel always has context 0. - - \item[0xF7.] \texttt{SET\_CONTEXT}. Pops the top element of the stack and updates the current context to this value. It is usually used when calling another contract or precompile, - to distinguish the caller from the callee. - - \item[0xF8.] \texttt{MLOAD\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a length $\ell$), and pushes - a value onto the stack. The pushed value corresponds to the U256 integer read from the big-endian sequence of length $\ell$ from the memory address being fetched. Note that an - empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. - - \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. - The popped element, \textit{kexit\_info}, contains several pieces of information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. - - \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 1 elements (a Memory address), and pushes the value stored at this memory - address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. - - \item[0xFC.] \texttt{MSTORE\_GENERAL}. Pops 2 elements (a value and a Memory address), and writes the popped value from - the stack at the fetched address. It can write to any memory location, general (similarly to MSTORE (0x52) / MSTORE8 (0x53) instructions) or privileged. -\end{enumerate} - - -\subsection{Memory addresses} -\label{memoryaddresses} - -Kernel operations deal with memory addresses as single U256 elements. -However, when processing the operations to generate the proof witness, the CPU will decompose these into three components: - -\begin{itemize} - \item[context.] The context of the memory address. The Kernel context is special, and has value 0. - - \item[segment.] The segment of the memory address, corresponding to a specific section given a context (eg. MPT data, global metadata, etc.). - - \item[virtual.] The offset of the memory address, within a segment given a context. -\end{itemize} - -To easily retrieve these components, we scale them so that they can represent a memory address as: - -$$ \mathrm{addr} = 2^{64} \cdot \mathrm{context} + 2^{32} \cdot \mathrm{segment} + \mathrm{offset}$$ - -This allows to easily retrieve each component individually once a Memory address has been decomposed into 32-bit limbs. - - -\subsection{Stack handling} -\label{stackhandling} - -\subsubsection{Top of the stack} - -The majority of memory operations involve the stack. The stack is a segment in memory, and stack operations (popping or pushing) use the memory channels. -Every CPU instruction performs between 0 and 3 pops, and may push at most once. However, for efficiency purposes, we hold the top of the stack in -the first memory channel \texttt{current\_row.mem\_channels[0]}, only writing it in memory if necessary. - -\paragraph*{Motivation:} - -See \href{https://github.com/0xPolygonZero/plonky2/issues/1149}{this issue}. - -\paragraph*{Top reading and writing:} - -When a CPU instruction modifies the stack, it must update the top of the stack accordingly. There are three cases. - -\begin{itemize} - \item \textbf{The instruction pops and pushes:} The new top of the stack is stored in \texttt{next\_row.mem\_channels[0]}; it may be computed by the instruction, -or it could be read from memory. In either case, the instruction is responsible for setting \texttt{next\_row.mem\_channels[0]}'s flags and address columns correctly. -After use, the previous top of the stack is discarded and doesn't need to be written in memory. - \item \textbf{The instruction pushes, but doesn't pop:} The new top of the stack is stored in \texttt{next\_row.mem\_channels[0]}; it may be computed by the instruction, -or it could be read from memory. In either case, the instruction is responsible for setting \texttt{next\_row.mem\_channels[0]}'s flags and address columns correctly. -If the stack wasn't empty (\texttt{current\_row.stack\_len > 0}), the instruction performs a memory read in \texttt{current\_row.partial\_ channel}. \texttt{current\_row.partial\_channel} -shares its values with \texttt{current\_ row.mem\_channels[0]} (which holds the current top of the stack). If the stack was empty, \texttt{current\_row.partial\_channel} -is disabled. - \item \textbf{The instruction pops, but doesn't push:} After use, the current top of the stack is discarded and doesn't need to be written in memory. -If the stack isn't empty now (\texttt{current\_row.stack\_len > num\_pops}), the new top of the stack is set in \texttt{next\_row.mem\_channels[0]} -with a memory read from the stack segment. If the stack is now empty, \texttt{next\_row.mem\_channels[0]} is disabled. -\end{itemize} - -In the last two cases, there is an edge case if \texttt{current\_row.stack\_len} is equal to a \texttt{special\_len}. For a strictly pushing instruction, -this happens if the stack is empty, and \texttt{special\_len = 0}. For a strictly popping instruction, this happens if the next stack is empty, i.e. if -all remaining elements are popped, and \texttt{special\_len = num\_pops}. Note that we do not need to check for values below \texttt{num\_pops}, since this -would be a stack underflow exception which is handled separately. -The edge case is detected with the compound flag -$$\texttt{1 - not\_special\_len * stack\_inv\_aux,}$$ -where $$\texttt{not\_special\_len = current\_row - special\_len}$$ - - -and \texttt{stack\_inv\_aux} is constrained to be the modular inverse of \texttt{not\_special\_ len} if it's non-zero, or 0 otherwise. The flag is 1 -if \texttt{stack\_len} is equal to \texttt{special\_len}, and 0 otherwise. - -This logic can be found in code in the \texttt{eval\_packed\_one} function of \href{https://github.com/0xPolygonZero/plonky2/blob/main/evm/src/cpu/stack.rs}{stack.rs}. -The function multiplies all of the stack constraints with the degree 1 filter associated with the current instruction. - -\paragraph*{Operation flag merging:} - -To reduce the total number of columns, many operation flags are merged together (e.g. \texttt{DUP} and \texttt{SWAP}) and are distinguished with the binary decomposition of their opcodes. -The filter for a merged operation is now of degree 2: for example, \texttt{is\_swap = dup\_swap * opcode\_bits[4]} since the 4th bit is set to 1 for a \texttt{SWAP} and 0 for a \texttt{DUP}. -If the two instructions have different stack behaviors, this can be a problem: \texttt{eval\_packed\_one}'s constraints are already of degree 3 and it can't support degree 2 filters. - -When this happens, stack constraints are defined manually in the operation's dedicated file (e.g. \texttt{dup\_swap.rs}). Implementation details vary case-by-case and can be found in the files. - -\subsubsection{Stack length checking} - -The CPU must make sure that the stack length never goes below zero and, in user mode, never grows beyond the maximum stack size. When this happens, an honest prover should trigger the -corresponding exception. If a malicious prover doesn't trigger the exception, constraints must fail the proof. - -\paragraph*{Stack underflow:} -There is no explicit constraint checking for stack underflow. An underflow happens when the CPU tries to pop the empty stack, which would perform a memory read at virtual address \texttt{-1}. -Such a read cannot succeed: in Memory, the range-check argument requires the gap between two consecutive addresses to be lower than the length of the Memory trace. Since the prime of the Plonky2 field is 64-bit long, -this would require a Memory trace longer than $2^{32}$. - -\paragraph*{Stack overflow:} -An instruction can only push at most once, meaning that an overflow occurs whenever the stack length is exactly one more than the maximum stack size ($1024+1$) in user mode. -To constrain this, the column \texttt{stack\_len\_bounds\_aux} contains: - -\begin{itemize} - \item[--] the modular inverse of \texttt{stack\_len - 1025} if we're in user mode and \texttt{stack\_len $\neq$ 1025}, - \item[--] 0 if \texttt{stack\_len = 1025} or if we're in kernel mode. -\end{itemize} -Then overflow can be checked with the flag -$$\texttt{(1 - is\_kernel\_mode) - stack\_len * stack\_len\_bounds\_aux}.$$ -The flag is 1 if \texttt{stack\_len = 1025} and we're in user mode, and 0 otherwise. - -Because \texttt{stack\_len\_bounds\_aux} is a shared general column, we only check this constraint after an instruction that can actually trigger an overflow, -i.e. a pushing, non-popping instruction. - -\subsection{Gas handling} - -\subsubsection{Out of gas errors} - -The CPU table has a ``gas'' register that keeps track of the gas used by the transaction so far. - -The crucial invariant in our out-of-gas checking method is that at any point in the program's execution, we have not used more gas than we have available; that is ``gas'' is at most the gas allocation for the transaction (which is stored separately by the kernel). We assume that the gas allocation will never be $2^{32}$ or more, so if ``gas'' does not fit in one limb, then we've run out of gas. - -When a native instruction (one that is not a syscall) is executed, a constraint ensures that the ``gas'' register is increased by the correct amount. This is not automatic for syscalls; the syscall handler itself must calculate and charge the appropriate amount. - -If everything goes smoothly and we have not run out of gas, ``gas'' should be no more than the gas allowance at the point that we STOP, REVERT, stack overflow, or whatever. Indeed, because we assume that the gas overflow handler is invoked \textit{as soon as} we've run out of gas, all these termination methods verify that $\texttt{gas} \leq \texttt{allowance}$, and jump to \texttt{exc\_out\_of\_gas} if this is not the case. This is also true for the out-of-gas handler, which checks that: -\begin{enumerate} - \item we have not yet run out of gas - \item we are about to run out of gas -\end{enumerate} -and ``PANIC'' if either of those statements does not hold. - -When we do run out of gas, however, this event must be handled. Syscalls are responsible for checking that their execution would not cause the transaction to run out of gas. If the syscall detects that it would need to charge more gas than available, it aborts the transaction (or the current code) by jumping to \texttt{fault\_exception}. In fact, \texttt{fault\_exception} is in charge of handling all exceptional halts in the kernel. - -Native instructions do this differently. If the prover notices that execution of the instruction would cause an out-of-gas error, it must jump to the appropriate handler instead of executing the instruction. (The handler contains special code that PANICs if the prover invoked it incorrectly.) - -\subsubsection{Overflow} - -We must be careful to ensure that ``gas'' does not overflow to prevent denial of service attacks. - -Note that a syscall cannot be the instruction that causes an overflow. This is because every syscall is required to verify that its execution does not cause us to exceed the gas limit. Upon entry into a syscall, a constraint verifies that $\texttt{gas} < 2^{32}$. Some syscalls may have to be careful to ensure that the gas check is performed correctly (for example, that overflow modulo $2^{256}$ does not occur). So we can assume that upon entry and exit out of a syscall, $\texttt{gas} < 2^{32}$. - -Similarly, native instructions alone cannot cause wraparound. The most expensive instruction, JUMPI, costs 10 gas. Even if we were to execute $2^{32}$ consecutive JUMPI instructions, the maximum length of a trace, we are nowhere close to consuming $2^{64} - 2^{32} + 1$ (= Goldilocks prime) gas. - -The final scenario we must tackle is an expensive syscall followed by many expensive native instructions. Upon exit from a syscall, $\texttt{gas} < 2^{32}$. Again, even if that syscall is followed by $2^{32}$ native instructions of cost 10, we do not see wraparound modulo Goldilocks. - - -\subsection{Exceptions} -\label{exceptions} - -Sometimes, when executing user code (i.e. contract or transaction code), the EVM halts exceptionally (i.e. outside of a STOP, a RETURN or a REVERT). -When this happens, the CPU table invokes a special instruction with a dedicated operation flag \texttt{exception}. -Exceptions can only happen in user mode; triggering an exception in kernel mode would make the proof unverifiable. -No matter the exception, the handling is the same: - --- The opcode which would trigger the exception is not executed. The operation flag set is \texttt{exception} instead of the opcode's flag. - --- We push a value to the stack which contains: the current program counter (to retrieve the faulty opcode), and the current value of \texttt{gas\_used}. -The program counter is then set to the corresponding exception handler in the kernel (e.g. \texttt{exc\_out\_of\_gas}). - --- The exception handler verifies that the given exception would indeed be triggered by the faulty opcode. If this is not the case (if the exception has already happened or if it doesn't happen after executing -the faulty opcode), then the kernel panics: there was an issue during witness generation. - --- The kernel consumes the remaining gas and returns from the current context with \texttt{success} set to 0 to indicate an execution failure. - -Here is the list of the possible exceptions: - -\begin{enumerate}[align=left] - \item[\textbf{Out of gas:}] Raised when a native instruction (i.e. not a syscall) in user mode pushes the amount of gas used over the current gas limit. -When this happens, the EVM jumps to \texttt{exc\_out\_of\_gas}. The kernel then checks that the consumed gas is currently below the gas limit, -and that adding the gas cost of the faulty instruction pushes it over it. -If the exception is not raised, the prover will panic when returning from the execution: the remaining gas is checked to be positive after STOP, RETURN or REVERT. - \item[\textbf{Invalid opcode:}] Raised when the read opcode is invalid. It means either that it doesn't exist, or that it's a privileged instruction and -thus not available in user mode. When this happens, the EVM jumps to \texttt{exc\_invalid\_opcode}. The kernel then checks that the given opcode is indeed invalid. -If the exception is not raised, decoding constraints ensure no operation flag is set to 1, which would make it a padding row. Halting constraints would then make the proof -unverifiable. - \item[\textbf{Stack underflow:}] Raised when an instruction which pops from the stack is called when the stack doesn't have enough elements. -When this happens, the EVM jumps to \texttt{exc\_stack\_overflow}. The kernel then checks that the current stack length is smaller than the minimum -stack length required by the faulty opcode. -If the exception is not raised, the popping memory operation's address offset would underflow, and the Memory range check would require the Memory trace to be too -large ($>2^{32}$). - \item[\textbf{Invalid JUMP destination:}] Raised when the program counter jumps to an invalid location (i.e. not a JUMPDEST). When this happens, the EVM jumps to -\texttt{exc\_invalid\_jump\_destination}. The kernel then checks that the opcode is a JUMP, and that the destination is not a JUMPDEST by checking the -JUMPDEST segment. -If the exception is not raised, jumping constraints will fail the proof. - \item[\textbf{Invalid JUMPI destination:}] Same as the above, for JUMPI. - \item[\textbf{Stack overflow:}] Raised when a pushing instruction in user mode pushes the stack over 1024. When this happens, the EVM jumps -to \texttt{exc\_stack\_overflow}. The kernel then checks that the current stack length is exactly equal to 1024 (since an instruction can only -push once at most), and that the faulty instruction is pushing. -If the exception is not raised, stack constraints ensure that a stack length of 1025 in user mode will fail the proof. -\end{enumerate} diff --git a/evm/spec/framework.tex b/evm/spec/framework.tex deleted file mode 100644 index c20e46db67..0000000000 --- a/evm/spec/framework.tex +++ /dev/null @@ -1,159 +0,0 @@ -\section{STARK framework} -\label{framework} - - -\subsection{Cost model} - -Our zkEVM is designed for efficient verification by STARKs \cite{stark}, particularly by an AIR with degree 3 constraints. In this model, the prover bottleneck is typically constructing Merkle trees, particularly constructing the tree containing low-degree extensions of witness polynomials. - - -\subsection{Field selection} -\label{field} -Our zkEVM is designed to have its execution traces encoded in a particular prime field $\mathbb{F}_p$, with $p = 2^{64} - 2^{32} + 1$. A nice property of this field is that it can represent the results of many common \texttt{u32} operations. For example, (widening) \texttt{u32} multiplication has a maximum value of $(2^{32} - 1)^2$, which is less than $p$. In fact a \texttt{u32} multiply-add has a maximum value of $p - 1$, so the result can be represented with a single field element, although if we were to add a carry in bit, this no longer holds. - -This field also enables a very efficient reduction method. Observe that -$$ -2^{64} \equiv 2^{32} - 1 \pmod p -$$ -and consequently -\begin{align*} - 2^{96} &\equiv 2^{32} (2^{32} - 1) \pmod p \\ - &\equiv 2^{64} - 2^{32} \pmod p \\ - &\equiv -1 \pmod p. -\end{align*} -To reduce a 128-bit number $n$, we first rewrite $n$ as $n_0 + 2^{64} n_1 + 2^{96} n_2$, where $n_0$ is 64 bits and $n_1, n_2$ are 32 bits each. Then -\begin{align*} - n &\equiv n_0 + 2^{64} n_1 + 2^{96} n_2 \pmod p \\ - &\equiv n_0 + (2^{32} - 1) n_1 - n_2 \pmod p -\end{align*} -After computing $(2^{32} - 1) n_1$, which can be done with a shift and subtraction, we add the first two terms, subtracting $p$ if overflow occurs. We then subtract $n_2$, adding $p$ if underflow occurs. - -At this point we have reduced $n$ to a \texttt{u64}. This partial reduction is adequate for most purposes, but if we needed the result in canonical form, we would perform a final conditional subtraction. - -\subsection{Cross-table lookups} -\label{ctl} -The various STARK tables carry out independent operations, but on shared values. We need to check that the shared values are identical in all the STARKs that require them. This is where cross-table lookups (CTLs) come in handy. - -Suppose STARK $S_1$ requires an operation -- say $Op$ -- that is carried out by another STARK $S_2$. Then $S_1$ writes the input and output of $Op$ in its own table, and provides the inputs to $S_2$. $S_2$ also writes the inputs and outputs in its rows, and the table's constraints check that $Op$ is carried out correctly. We then need to ensure that the inputs and outputs are the same in $S_1$ and $S_2$. - -In other words, we need to ensure that the rows -- reduced to the input and output columns -- of $S_1$ calling $Op$ are permutations of the rows of $S_2$ that carry out $Op$. Our CTL protocol is based on logUp and is similar to our range-checks. - -To prove this, the first step is to only select the rows of interest in $S_1$ and $S_2$, and filter out the rest. Let $f^1$ be the filter for $S_1$ and $f^2$ the filter for $S_2$. $f^1$ and $f^2$ are constrained to be in $\{0, 1\}$. $f^1 = 1$ (resp. $f^2 = 1$) whenever the row at hand carries out $Op$ in $S_1$ (resp. in $S_2$), and 0 otherwise. Let also $(\alpha, \beta)$ be two random challenges. - -The idea is to create subtables $S_1'$ and $S_2'$ of $S_1$ and $S_2$ respectively, such that $f^1 = 1$ and $f^2 = 1$ for all their rows. The columns in the subtables are limited to the ones whose values must be identical (the inputs and outputs of $Op$ in our example). - -Note that for design and constraint reasons, filters are limited to (at most) degree 2 combinations of columns. - -Let $\{c^{1, i}\}_{i=1}^m$ be the columns in $S_1'$ an $\{c^{2,i}\}_{i=1}^m$ be the columns in $S_2'$. - -The prover defines a ``running sum'' $Z$ for $S_1'$ such that: -\begin{gather*} - Z^{S_1}_{n-1} = \frac{1}{\sum_{j=0}^{m-1} \alpha^j \cdot c^{1, j}_{n-1} + \beta} \\ - Z^{S_1}_{i+1} = Z^{S_1}_i + f^1_i \cdot \frac{1}{\sum_{j=0}^{m-1} \alpha^j \cdot c^{1, j}_i + \beta} -\end{gather*} -The second equation ``selects'' the terms of interest thanks to $f^1$ and filters out the rest. - -Similarly, the prover constructs a running sum $Z^{S_2}$for $S_2$. Note that $Z$ is computed ``upside down'': we start with $Z_{n-1}$ and the final sum is in $Z_0$. - -On top of the constraints to check that the running sums were correctly constructed, the verifier checks that $Z^{S_1}_0 = Z^{S_2}_0$. -This ensures that the columns in $S_1'$ and the columns in $S_2'$ are permutations of each other. - -In other words, the CTL argument is a logUp lookup argument where $S_1'$ is the looking table, $S_2'$ is the looked table, and $S_1' = S_2'$ (all the multiplicities are 1). -For more details about logUp, see the next section. - -To sum up, for each STARK $S$, the prover: -\begin{enumerate} - \item constructs a running sum $Z_i^l$ for each table looking into $S$ (called looking sums here), - \item constructs a running sum $Z^S$ for $S$ (called looked sum here), - \item sends the final value for each running sum $Z_{i, 0}^l$ and $Z^S_0$ to the verifier, - \item sends a commitment to $Z_i^l$ and $Z^S$ to the verifier. -\end{enumerate} -Then, for each STARK $S$, the verifier: -\begin{enumerate} - \item computes the sum $Z = \sum_i Z_{i, 0}^l$, - \item checks that $Z = Z^S_0$, - \item checks that each $Z_i^l$ and $Z^S$ was correctly constructed. -\end{enumerate} - - -\subsection{Range-checks} -\label{rc} -In most cases, tables deal with U256 words, split into 32-bit limbs (to avoid overflowing the field). To prevent a malicious prover from cheating, it is crucial to range-check those limbs. -\subsubsection{What to range-check?} -One can note that every element that ever appears on the stack has been pushed. Therefore, enforcing a range-check on pushed elements is enough to range-check all elements on the stack. Similarly, all elements in memory must have been written prior, and therefore it is enough to range-check memory writes. However, range-checking the PUSH and MSTORE opcodes is not sufficient. -\begin{enumerate} - \item Pushes and memory writes for ``MSTORE\_32BYTES'' are range-checked in ``BytePackingStark''. - \item Syscalls, exceptions and prover inputs are range-checked in ``ArithmeticStark''. - \item The inputs and outputs of binary and ternary arithmetic operations are range-checked in ``ArithmeticStark''. - \item The inputs' bits of logic operations are checked to be either 1 or 0 in ``LogicStark''. Since ``LogicStark'' only deals with bitwise operations, this is enough to have range-checked outputs as well. - \item The inputs of Keccak operations are range-checked in ``KeccakStark''. The output digest is written as bytes in ``KeccakStark''. Those bytes are used to reconstruct the associated 32-bit limbs checked against the limbs in ``CpuStark''. This implicitly ensures that the output is range-checked. -\end{enumerate} -Note that some operations do not require a range-check: -\begin{enumerate} - \item ``MSTORE\_GENERAL'' read the value to write from the stack. Thus, the written value was already range-checked by a previous push. - \item ``EQ'' reads two -- already range-checked -- elements on the stack, and checks they are equal. The output is either 0 or 1, and does therefore not need to be checked. - \item ``NOT'' reads one -- already range-checked -- element. The result is constrained to be equal to $\texttt{0xFFFFFFFF} - \texttt{input}$, which implicitly enforces the range check. - \item ``PC'': the program counter cannot be greater than $2^{32}$ in user mode. Indeed, the user code cannot be longer than $2^{32}$, and jumps are constrained to be JUMPDESTs. Moreover, in kernel mode, every jump is towards a location within the kernel, and the kernel code is smaller than $2^{32}$. These two points implicitly enforce $PC$'s range check. - \item ``GET\_CONTEXT'', ``DUP'' and ``SWAP'' all read and push values that were already written in memory. The pushed values were therefore already range-checked. -\end{enumerate} -Range-checks are performed on the range $[0, 2^{16} - 1]$, to limit the trace length. - -\subsubsection{Lookup Argument} -To enforce the range-checks, we leverage \href{https://eprint.iacr.org/2022/1530.pdf}{logUp}, a lookup argument by Ulrich Häbock. Given a looking table $s = (s_1, ..., s_n)$ and a looked table $t = (t_1, ..., t_m)$, the goal is to prove that -$$\forall 1 \leq i \leq n, \exists 1 \leq j \leq r \texttt{ such that } s_i = t_j$$ -In our case, $t = (0, .., 2^{16} - 1)$ and $s$ is composed of all the columns in each STARK that must be range-checked. - -The logUp paper explains that proving the previous assertion is actually equivalent to proving that there exists a sequence $l$ such that: -$$ \sum_{i=1}^n \frac{1}{X - s_i} = \sum_{j=1}^r \frac{l_j}{X-t_j}$$ - -The values of $s$ can be stored in $c$ different columns of length $n$ each. In that case, the equality becomes: -$$\sum_{k=1}^c \sum_{i=1}^n \frac{1}{X - s_i^k} = \sum_{j=1}^r \frac{l_j}{X-t_j}$$ - -The `multiplicity' $m_i$ of value $t_i$ is defined as the number of times $t_i$ appears in $s$. In other words: -$$m_i = |s_j \in s; s_j = t_i|$$ - -Multiplicities provide a valid sequence of values in the previously stated equation. Thus, if we store the multiplicities, and are provided with a challenge $\alpha$, we can prove the lookup argument by ensuring: -$$\sum_{k=1}^c \sum_{i=1}^n \frac{1}{\alpha - s_i^k} = \sum_{j=1}^r \frac{m_j}{\alpha-t_j}$$ -However, the equation is too high degree. To circumvent this issue, Häbock suggests providing helper columns $h_i$ and $d$ such that at a given row $i$: -\begin{gather*} - h_i^k = \frac{1}{\alpha + s_i^k } \forall 1 \leq k \leq c \\ - d_i = \frac{1}{\alpha + t_i} -\end{gather*} - -The $h$ helper columns can be batched together to save columns. We can batch at most $\texttt{constraint\_degree} - 1$ helper functions together. In our case, we batch them 2 by 2. At row $i$, we now have: -\begin{align*} - h_i^k = \frac{1}{\alpha + s_i^{2k}} + \frac{1}{\alpha + s_i^{2k+1}} \forall 1 \leq k \leq c/2 \\ -\end{align*} -If $c$ is odd, then we have one extra helper column: -$$h_i^{c/2+1} = \frac{1}{\alpha + s_i^{c}}$$ - -For clarity, we will assume that $c$ is even in what follows. - -Let $g$ be a generator of a subgroup of order $n$. We extrapolate $h, m$ and $d$ to get polynomials such that, for $f \in \{h^k, m, g\}$: $f(g^i) = f_i$. -We can define the following polynomial: -$$ Z(x) := \sum_{i=1}^n \big[\sum_{k=1}^{c/2} h^k(x) - m(x) * d(x)\big]$$ - - -\subsubsection{Constraints} -With these definitions and a challenge $\alpha$, we can finally check that the assertion holds with the following constraints: -\begin{gather*} - Z(1) = 0 \\ - Z(g \alpha) = Z(\alpha) + \sum_{k=1}^{c/2} h^k(\alpha) - m(\alpha) d(\alpha) -\end{gather*} -These ensure that -We also need to ensure that $h^k$ is well constructed for all $1 \leq k \leq c/2$: -$$ - h(\alpha)^k \cdot (\alpha + s_{2k}) \cdot (\alpha + s_{2k+1}) = (\alpha + s_{2k}) + (\alpha + s_{2k+1}) -$$ - -Note: if $c$ is odd, we have one unbatched helper column $h^{c/2+1}$ for which we need a last constraint: -$$ - h(\alpha)^{c/2+1} \cdot (\alpha + s_{c}) = 1 -$$ - -Finally, the verifier needs to ensure that the table $t$ was also correctly computed. In each STARK, $t$ is computed starting from 0 and adding at most 1 at each row. This construction is constrained as follows: -\begin{enumerate} - \item $t(1) = 0$ - \item $(t(g^{i+1}) - t(g^{i})) \cdot ((t(g^{i+1}) - t(g^{i})) - 1) = 0$ - \item $t(g^{n-1}) = 2^{16} - 1$ -\end{enumerate} diff --git a/evm/spec/introduction.tex b/evm/spec/introduction.tex deleted file mode 100644 index cb969a168d..0000000000 --- a/evm/spec/introduction.tex +++ /dev/null @@ -1,3 +0,0 @@ -\section{Introduction} - -TODO diff --git a/evm/spec/mpts.tex b/evm/spec/mpts.tex deleted file mode 100644 index 3f6733a535..0000000000 --- a/evm/spec/mpts.tex +++ /dev/null @@ -1,94 +0,0 @@ -\section{Merkle Patricia Tries} -\label{tries} -The \emph{EVM World state} is a representation of the different accounts at a particular time, as well as the last processed transactions together with their receipts. The world state is represented using \emph{Merkle Patricia Tries} (MPTs) \cite[App.~D]{yellowpaper}, and there are three different tries: the state trie, the transaction trie and the receipt trie. - -For each transaction we need to show that the prover knows preimages of the hashed initial and final EVM states. When the kernel starts execution, it stores these three tries within the {\tt Segment::TrieData} segment. The prover loads the initial tries from the inputs into memory. Subsequently, the tries are modified during transaction execution, inserting new nodes or deleting existing nodes. - -An MPT is composed of five different nodes: branch, extension, leaf, empty and digest nodes. Branch and leaf nodes might contain a payload whose format depends on the particular trie. The nodes are encoded, primarily using RLP encoding and Hex-prefix encoding (see \cite{yellowpaper} App. B and C, respectively). The resulting encoding is then hashed, following a strategy similar to that of normal Merkle trees, to generate the trie hashes. - -Insertion and deletion is performed in the same way as other MPTs implementations. The only difference is for inserting extension nodes where we create a new node with the new data, instead of modifying the existing one. In the rest of this section we describe how the MPTs are represented in memory, how they are given as input, and how MPTs are hashed. - -\subsection{Internal memory format} - -The tries are stored in kernel memory, specifically in the {\tt Segment:TrieData} segment. Each node type is stored as -\begin{enumerate} - \item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$. - \item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, c_1, \dots, c_{16}, v)$, where each $c_i$ is a pointer to a child node, and $v$ is a pointer to a value. If a branch node has no associated value, then $v = 0$, i.e. the null pointer. - \item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node. - \item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, v)$, where $k$ is a 2-tuple as above, and $v$ is a pointer to a value. - \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest. -\end{enumerate} - -On the other hand the values or payloads are represented differently depending on the particular trie. - -\subsubsection{State trie} -The state trie payload contains the account data. Each account is stored in 4 contiguous memory addresses containing -\begin{enumerate} - \item the nonce, - \item the balance, - \item a pointer to the account's storage trie, - \item a hash of the account's code. -\end{enumerate} -The storage trie payload in turn is a single word. - -\subsubsection{Transaction Trie} -The transaction trie nodes contain the length of the RLP encoded transaction, followed by the bytes of the RLP encoding of the transaction. - -\subsubsection{Receipt Trie} -The payload of the receipts trie is a receipt. Each receipt is stored as -\begin{enumerate} - \item the length in words of the payload, - \item the status, - \item the cumulative gas used, - \item the bloom filter, stored as 256 words. - \item the number of topics, - \item the topics - \item the data length, - \item the data. -\end{enumerate} - - -\subsection{Prover input format} - -The initial state of each trie is given by the prover as a nondeterministic input tape. This tape has a slightly different format: -\begin{enumerate} - \item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$. - \item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, v_?, c_1, \dots, c_{16})$. Here $v_?$ consists of a flag indicating whether a value is present, followed by the actual value payload if one is present. Each $c_i$ is the encoding of a child node. - \item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, where $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node. - \item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, v)$, where $k$ is a 2-tuple as above, and $v$ is a value payload. - \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest. -\end{enumerate} -Nodes are thus given in depth-first order, enabling natural recursive methods for encoding and decoding this format. -The payload of state and receipt tries is given in the natural sequential way. The transaction an receipt payloads contain variable size data, thus the input is slightly different. The prover input for for the transactions is the transaction RLP encoding preceded by its length. For the receipts is in the natural sequential way, except that topics and data are preceded by their lengths, respectively. - -\subsection{Encoding and Hashing} - -Encoding is done recursively starting from the trie root. Leaf, branch and extension nodes are encoded as the RLP encoding of list containing the hex prefix encoding of the node key as well as - -\begin{description} - \item[Leaf Node:] the encoding of the the payload, - \item[Branch Node:] the hash or encoding of the 16 children and the encoding of the payload, - \item[Extension Node:] the hash or encoding of the child and the encoding of the payload. -\end{description} -For the rest of the nodes we have: -\begin{description} - \item[Empty Node:] the encoding of an empty node is {\tt 0x80}, - \item[Digest Node:] the encoding of a digest node stored as $({\tt MPT\_HASH\_NODE}, d)$ is $d$. -\end{description} - -The payloads in turn are RLP encoded as follows -\begin{description} - \item[State Trie:] Encoded as a list containing nonce, balance, storage trie hash and code hash. - \item[Storage Trie:] The RLP encoding of the value (thus the double RLP encoding) - \item[Transaction Trie:] The RLP encoded transaction. - \item[Receipt Trie:] Depending on the transaction type it's encoded as ${\sf RLP}({\sf RLP}({\tt receipt}))$ for Legacy transactions or ${\sf RLP}({\tt txn\_type}||{\sf RLP}({\tt receipt}))$ for transactions of type 1 or 2. Each receipt is encoded as a list containing: - \begin{enumerate} - \item the status, - \item the cumulative gas used, - \item the bloom filter, stored as a list of length 256. - \item the list of topics - \item the data string. - \end{enumerate} -\end{description} - -Once a node is encoded it is written to the {\tt Segment::RlpRaw} segment as a sequence of bytes. Then the RLP encoded data is hashed if the length of the data is more than 32 bytes. Otherwise we return the encoding. Further details can be found in the \href{https://github.com/0xPolygonZero/plonky2/tree/main/evm/src/cpu/mpt/hash}{mpt hash folder}. \ No newline at end of file diff --git a/evm/spec/tables.tex b/evm/spec/tables.tex deleted file mode 100644 index 43b45eb584..0000000000 --- a/evm/spec/tables.tex +++ /dev/null @@ -1,10 +0,0 @@ -\section{Tables} -\label{tables} - -\input{tables/cpu} -\input{tables/arithmetic} -\input{tables/byte-packing} -\input{tables/logic} -\input{tables/memory} -\input{tables/keccak-f} -\input{tables/keccak-sponge} diff --git a/evm/spec/tables/arithmetic.tex b/evm/spec/tables/arithmetic.tex deleted file mode 100644 index 19be4638f6..0000000000 --- a/evm/spec/tables/arithmetic.tex +++ /dev/null @@ -1,54 +0,0 @@ -\subsection{Arithmetic} -\label{arithmetic} - -Each row of the arithmetic table corresponds to a binary or ternary arithmetic operation. Each of these operations has an associated flag $f_{op}$ in the table, such that $f_{\texttt{op}} = 1$ whenever the operation is $\texttt{op}$ and 0 otherwise. The full list of operations carried out by the table is as follows: -\paragraph*{Binary operations:} \begin{itemize} - \item basic operations: ``add'', ``mul'', ``sub'' and ``div'', - \item comparisons: ``lt'' and ``gt'', - \item shifts: ``shr'' and ``shl'', - \item ``byte'': given $x_1, x_2$, returns the $x_1$-th ``byte'' in $x_2$, - \item modular operations: ``mod'', ``AddFp254'', ``MulFp254'' and ``SubFp254'', - \item range-check: no operation is performed, as this is only used to range-check the input and output limbs in the range [$0, 2^{16} - 1$]. - \end{itemize} -For `mod', the second input is the modulus. ``AddFp254'', ``MulFp254'' and ``SubFp254'' are modular operations modulo ``Fp254'` -- the prime for the BN curve's base field. - -\paragraph*{Ternary operations:} There are three ternary operations: modular addition ``AddMod'', modular multiplication ``MulMod'' and modular subtraction ``SubMod''. - -Besides the flags, the arithmetic table needs to store the inputs, output and some auxiliary values necessary to constraints. The input and output values are range-checked to ensure their canonical representation. Inputs are 256-bits words. To avoid having too large a range-check, inputs are therefore split into sixteen 16-bits limbs, and range-checked in the range $[0, 2^{16}-1]$. - -Overall, the table comprises the following columns: -\begin{itemize} - \item 17 columns for the operation flags $f_{op}$, - \item 1 column $op$ containing the opcode, - \item 16 columns for the 16-bit limbs $x_{0, i}$ of the first input $x_{0}$, - \item 16 columns for the 16-bit limbs $x_{1, i}$ of the second input $x_{1}$, - \item 16 columns for the 16-bit limbs $x_{2, i}$ of the third input $x_{2}$, - \item 16 columns for the 16-bit limbs $r_i$ of the output $r$, - \item 32 columns for auxiliary values $\texttt{aux}_i$, - \item 1 column $\texttt{range\_counter}$ containing values in the range [$0, 2^{16}-1$], for the range-check, - \item 1 column storing the frequency of appearance of each value in the range $[0, 2^{16} - 1]$. -\end{itemize} - -\paragraph{Note on $op$:} The opcode column is only used for range-checks. For optimization purposes, we check all arithmetic operations against the cpu table together. To ensure correctness, we also check that the operation's opcode corresponds to its behavior. But range-check is not associated to a unique operation: any operation in the cpu table might require its values to be checked. Thus, the arithmetic table cannot know its opcode in advance: it needs to store the value provided by the cpu table. - -\subsubsection{Auxiliary columns} -The way auxiliary values are leveraged to efficiently check correctness is not trivial, but it is explained in detail in each dedicated file. Overall, five files explain the implementations of the various checks. Refer to: -\begin{enumerate} - \item ``mul.rs'' for details on multiplications. - \item ``addcy.rs'' for details on addition, subtraction, ``lt'' and ``gt''. - \item ``modular.rs'' for details on how modular operations are checked. Note that even though ``div'' and ``mod'' are generated and checked in a separate file, they leverage the logic for modular operations described in ``modular.rs''. - \item ``byte'' for details on how ``byte'' is checked. - \item ``shift.rs'' for details on how shifts are checked. -\end{enumerate} - -\paragraph*{Note on ``lt'' and ``gt'':} For ``lt'' and ``gt'', auxiliary columns hold the difference $d$ between the two inputs $x_1, x_2$. We can then treat them similarly to subtractions by ensuring that $x_1 - x_2 = d$ for ``lt'' and $x_2 - x_1 = d$ for ``gt''. An auxiliary column $cy$ is used for the carry in additions and subtractions. In the comparisons case, it holds the overflow flag. Contrary to subtractions, the output of ``lt'' and ``gt'' operations is not $d$ but $cy$. - -\paragraph*{Note on ``div'':} It might be unclear why ``div'' and ``mod'' are dealt with in the same file. - -Given numerator and denominator $n, d$, we compute, like for other modular operations, the quotient $q$ and remainder $\texttt{rem}$: -$$div(x_1, x_2) = q * x_2 + \texttt{rem}$$. -We then set the associated auxiliary columns to $\texttt{rem}$ and the output to $q$. - -This is why ``div'' is essentially a modulo operation, and can be addressed in almost the same way as ``mod''. The only difference is that in the ``mod'' case, the output is $\texttt{rem}$ and the auxiliary value is $q$. - -\paragraph{Note on shifts:} ``shr'' and ``shl'' are internally constrained as ``div'' and ``mul'' respectively with shifted operands. Indeed, given inputs $s, x$, the output should be $x >> s$ for ``shr'' (resp. $x << s$ for ``shl''). Since shifts are binary operations, we can use the third input columns to store $s_{\texttt{shifted}} = 1 << s$. Then, we can use the ``div'' logic (resp. ``mul'' logic) to ensure that the output is $\frac{x}{s_{\texttt{shifted}}}$ (resp. $x * s_{\texttt{shifted}}$). \ No newline at end of file diff --git a/evm/spec/tables/byte-packing.tex b/evm/spec/tables/byte-packing.tex deleted file mode 100644 index 6305b7226b..0000000000 --- a/evm/spec/tables/byte-packing.tex +++ /dev/null @@ -1,59 +0,0 @@ -\subsection{Byte Packing} -\label{byte-packing} - -The BytePacking STARK module is used for reading and writing non-empty byte sequences of length at most 32 to memory. -The "packing" term highlights that reading a sequence in memory will pack the bytes into an EVM word (i.e. U256), while -the "unpacking" operation consists in breaking down an EVM word into its byte sequence and writing it to memory. - -This allows faster memory copies between two memory locations, as well as faster memory reset -(see \href{https://github.com/0xPolygonZero/plonky2/blob/main/evm/src/cpu/kernel/asm/memory/memcpy.asm}{memcpy.asm} and -\href{https://github.com/0xPolygonZero/plonky2/blob/main/evm/src/cpu/kernel/asm/memory/memset.asm}{memset.asm} modules). - -The `BytePackingStark' table has one row per packing/unpacking operation. - -Each row contains the following columns: -\begin{enumerate} - \item 5 columns containing information on the initial memory address from which the sequence starts - (namely a flag differentiating read and write operations, address context, segment and offset values, as well as timestamp), - \item 32 columns $b_i$ indicating the length of the byte sequence ($b_i = 1$ if the length is $i+1$, and $b_i = 0$ otherwise), - \item 32 columns $v_i$ indicating the values of the bytes that have been read or written during a sequence, - \item 2 columns $r_i$ needed for range-checking the byte values. -\end{enumerate} - -\paragraph{Notes on columns generation:} -Whenever a byte unpacking operation is called, the value $\texttt{val}$ is read from the stack, but because the EVM and the STARKs use different endianness, we need to convert $\texttt{val}$ to a little-endian byte sequence. Only then do we resize it to the appropriate length, and prune extra zeros and higher bytes in the process. Finally, we reverse the byte order and write this new sequence into the $v_i$ columns of the table. - -Whenever the operation is a byte packing, the bytes are read one by one from memory and stored in the $v_i$ columns of the BytePackingStark table. - -Note that because of the different endianness on the memory and EVM sides, we write bytes starting with the last one. - -The $b_i$ columns hold a boolean value. $b_i = 1$ whenever we are currently reading or writing the i-th element in the byte sequence. $b_i = 0$ otherwise. - -\paragraph{Cross-table lookups:} -The read or written bytes need to be checked against both the cpu and the memory tables. Whenever we call $\texttt{MSTORE\_32BYTES}$, $\texttt{MLOAD\_32BYTES}$ or $\texttt{PUSH}$ on the cpu side, we make use of `BytePackingStark' to make sure we are carrying out the correct operation on the correct values. For this, we check that the following values correspond: -\begin{enumerate} - \item the address (comprising the context, the segment, and the virtual address), - \item the length of the byte sequence, - \item the timestamp, - \item the value (either written to or read from the stack) -\end{enumerate} - -The address here corresponds to the address of the first byte. - -On the other hand, we need to make sure that the read and write operations correspond to the values read or stored on the memory side. We therefore need a CTL for each byte, checking that the following values are identical in `MemoryStark' and `BytePackingStark': -\begin{enumerate} - \item a flag indicating whether the operation is a read or a write, - \item the address (context, segment and virtual address), - \item the byte (followed by 0s to make sure the memory address contains a byte and not a U256 word), - \item the timestamp -\end{enumerate} - -Note that the virtual address has to be recomputed based on the length of the sequence of bytes. The virtual address for the $i$-th byte is written as: -$$ \texttt{virt} + \sum_{j=0}^{31} b_j * j - i$$ -where $\sum_{j=0}^{31} b_j * j$ is equal to $\texttt{sequence\_length} - 1$. - -\paragraph*{Note on range-check:} Range-checking is necessary whenever we do a memory unpacking operation that will -write values to memory. These values are constrained by the range-check to be 8-bit values, i.e. fitting between 0 and 255 included. -While range-checking values read from memory is not necessary, because we use the same $\texttt{byte\_values}$ columns for both read -and write operations, this extra condition is enforced throughout the whole trace regardless of the operation type. - diff --git a/evm/spec/tables/cpu.tex b/evm/spec/tables/cpu.tex deleted file mode 100644 index 7bca5a9f5e..0000000000 --- a/evm/spec/tables/cpu.tex +++ /dev/null @@ -1,73 +0,0 @@ -\subsection{CPU} -\label{cpu} - -The CPU is the central component of the zkEVM. Like any CPU, it reads instructions, executes them and modifies the state (registers and the memory) -accordingly. The constraining of some complex instructions (e.g. Keccak hashing) is delegated to other tables. -This section will only briefly present the CPU and its columns. Details about the CPU logic will be provided later. - -\subsubsection{CPU flow} - -An execution run can be decomposed into two distinct parts: -\begin{itemize} - \item \textbf{CPU cycles:} The bulk of the execution. In each row, the CPU reads the current code at the program counter (PC) address, and executes it. The current code can be the kernel code, -or whichever code is being executed in the current context (transaction code or contract code). Executing an instruction consists in modifying the registers, possibly -performing some memory operations, and updating the PC. - \item \textbf{Padding:} At the end of the execution, we need to pad the length of the CPU trace to the next power of two. When the program counter reaches the special halting label -in the kernel, execution halts. Constraints ensure that every subsequent row is a padding row and that execution cannot resume. -\end{itemize} - -In the CPU cycles phase, the CPU can switch between different contexts, which correspond to the different environments of the possible calls. Context 0 is the kernel itself, which -handles initialization (input processing, transaction parsing, transaction trie updating...) and termination (receipt creation, final trie checks...) before and after executing the transaction. Subsequent contexts are created when -executing user code (transaction or contract code). In a non-zero user context, syscalls may be executed, which are specific instructions written in the kernel. They don't change the context -but change the code context, which is where the instructions are read from. - -\subsubsection{CPU columns} - -\paragraph*{Registers:} \begin{itemize} - \item \texttt{context}: Indicates which context we are in. 0 for the kernel, and a positive integer for every user context. Incremented by 1 at every call. - \item \texttt{code\_context}: Indicates in which context the code to execute resides. It's equal to \texttt{context} in user mode, but is always 0 in kernel mode. - \item \texttt{program\_counter}: The address of the instruction to be read and executed. - \item \texttt{stack\_len}: The current length of the stack. - \item \texttt{is\_kernel\_mode}: Boolean indicating whether we are in kernel (i.e. privileged) mode. This means we are executing kernel code, and we have access to -privileged instructions. - \item \texttt{gas}: The current amount of gas used in the current context. It is eventually checked to be below the current gas limit. Must fit in 32 bits. - \item \texttt{clock}: Monotonic counter which starts at 0 and is incremented by 1 at each row. Used to enforce correct ordering of memory accesses. - \item \texttt{opcode\_bits}: 8 boolean columns, which are the bit decomposition of the opcode being read at the current PC. -\end{itemize} - -\paragraph*{Operation flags:} Boolean flags. During CPU cycles phase, each row executes a single instruction, which sets one and only one operation flag. No flag is set during -padding. The decoding constraints ensure that the flag set corresponds to the opcode being read. -There isn't a 1-to-1 correspondance between instructions and flags. For efficiency, the same flag can be set by different, unrelated instructions (e.g. \texttt{eq\_iszero}, which represents -the \texttt{EQ} and the \texttt{ISZERO} instructions). When there is a need to differentiate them in constraints, we filter them with their respective opcode: since the first bit of \texttt{EQ}'s opcode -(resp. \texttt{ISZERO}'s opcode) is 0 (resp. 1), we can filter a constraint for an EQ instruction with \texttt{eq\_iszero * (1 - opcode\_bits[0])} -(resp. \texttt{eq\_iszero * opcode\_bits[0]}). - -\paragraph*{Memory columns:} The CPU interacts with the EVM memory via its memory channels. At each row, a memory channel can execute a write, a read, or be disabled. A full memory channel is composed of: -\begin{itemize} - \item \texttt{used}: Boolean flag. If it's set to 1, a memory operation is executed in this channel at this row. If it's set to 0, no operation is done but its columns might be reused for other purposes. - \item \texttt{is\_read}: Boolean flag indicating if a memory operation is a read or a write. - \item 3 \texttt{address} columns. A memory address is made of three parts: \texttt{context}, \texttt{segment} and \texttt{virtual}. - \item 8 \texttt{value} columns. EVM words are 256 bits long, and they are broken down in 8 32-bit limbs. -\end{itemize} -The last memory channel is a partial channel: it doesn't have its own \texttt{value} columns and shares them with the first full memory channel. This allows us to save eight columns. - -\paragraph*{General columns:} There are 8 shared general columns. Depending on the instruction, they are used differently: -\begin{itemize} - \item \texttt{Exceptions}: When raising an exception, the first three general columns are the bit decomposition of the exception code. -They are used to jump to the correct exception handler. - \item \texttt{Logic}: For EQ, and ISZERO operations, it's easy to check that the result is 1 if \texttt{input0} and \texttt{input1} are equal. It's more difficult -to prove that, if the result is 0, the inputs are actually unequal. To prove it, each general column contains the modular inverse of $(\texttt{input0}_i - \texttt{input1}_i)$ -for each limb $i$ (or 0 if the limbs are equal). Then the quantity $\texttt{general}_i * (\texttt{input0}_i - \texttt{input1}_i)$ will be 1 if and only if $\texttt{general}_i$ is -indeed the modular inverse, which is only possible if the difference is non-zero. - \item \texttt{Jumps}: For jumps, we use the first two columns: \texttt{should\_jump} and \texttt{cond\_sum\_pinv}. \texttt{should\_jump} conditions whether the EVM should jump: it's -1 for a JUMP, and $\texttt{condition} \neq 0$ for a JUMPI. To check if the condition is actually non-zero for a JUMPI, \texttt{cond\_sum\_pinv} stores the modular inverse of -\texttt{condition} (or 0 if it's zero). - \item \texttt{Shift}: For shifts, the logic differs depending on whether the displacement is lower than $2^{32}$, i.e. if it fits in a single value limb. -To check if this is not the case, we must check that at least one of the seven high limbs is not zero. The general column \texttt{high\_limb\_sum\_inv} holds the modular inverse -of the sum of the seven high limbs, and is used to check it's non-zero like the previous cases. -Contrary to the logic operations, we do not need to check limbs individually: each limb has been range-checked to 32 bits, meaning that it's not possible for the sum to -overflow and be zero if some of the limbs are non-zero. - \item \texttt{Stack}: \texttt{stack\_inv}, \texttt{stack\_inv\_aux} and \texttt{stack\_inv\_aux\_2} are used by popping-only (resp. pushing-only) instructions to check if the stack is empty after (resp. was empty -before) the instruction. \texttt{stack\_len\_bounds\_ aux} is used to check that the stack doesn't overflow in user mode. We use the last four columns to prevent conflicts with the other general columns. -See \ref{stackhandling} for more details. -\end{itemize} diff --git a/evm/spec/tables/keccak-f.tex b/evm/spec/tables/keccak-f.tex deleted file mode 100644 index 7eee4b53fc..0000000000 --- a/evm/spec/tables/keccak-f.tex +++ /dev/null @@ -1,65 +0,0 @@ -\subsection{Keccak-f} -\label{keccak-f} - -This table computes the Keccak-f[1600] permutation. - -\subsubsection{Keccak-f Permutation} -To explain how this table is structured, we first need to detail how the permutation is computed. \href{https://keccak.team/keccak_specs_summary.html}{This page} gives a pseudo-code for the permutation. Our implementation differs slightly -- but remains equivalent -- for optimization and constraint degree reasons. - -Let: -\begin{itemize} - \item $S$ be the sponge width ($S=25$ in our case) - \item $\texttt{NUM\_ROUNDS}$ be the number of Keccak rounds ($\texttt{NUM\_ROUNDS} = 24$) - \item $RC$ a vector of round constants of size $\texttt{NUM\_ROUNDS}$ - \item $I$ be the input of the permutation, comprised of $S$ 64-bit elements -\end{itemize} - -The first step is to reshape $I$ into a $5 \times 5$ matrix. We initialize the state $A$ of the sponge with $I$: $$A[x, y] := I[x, y] \text{ } \forall x, y \in \{0..4\}$$ - -We store $A$ in the table, and subdivide each 64-bit element into two 32-bit limbs. -Then, for each round $i$, we proceed as follows: -\begin{enumerate} - \item First, we define $C[x] := \texttt{xor}_{i=0}^4 A[x, i]$. We store $C$ as bits in the table. This is because we need to apply a rotation on its elements' bits and carry out \texttt{ xor } operations in the next step. - \item Then, we store a second vector $C'$ in bits, such that: $$C'[x, z] = C[x, z] \texttt{ xor } C[x-1, z] \texttt{ xor } C[x+1, z-1]$$. - \item We then need to store the updated value of $A$: $$A'[x, y] = A[x, y] \texttt{ xor } C[x, y] \texttt{ xor } C'[x, y]$$ Note that this is equivalent to the equation in the official Keccak-f description: $$A'[x, y] = A[x, y] \texttt{ xor } C[x-1, z] \texttt{ xor } C[x+1, z-1]$$. - \item The previous three points correspond to the $\theta$ step in Keccak-f. We can now move on to the $\rho$ and $\pi$ steps. These steps are written as: $$B[y, 2\times x + 3 \times y] := \texttt{rot}(A'[x, y], r[x, y])$$ where $\texttt{rot(a, s)}$ is the bitwise cyclic shift operation, and $r$ is the matrix of rotation offsets. We do not need to store $B$: $B$'s bits are only a permutation of $A'$'s bits. - \item The $\chi$ step updates the state once again, and we store the new values: $$A''[x, y] := B[x, y] \texttt{ xor } (\texttt{not }B[x+1, y] \texttt{ and } B[x+2, y])$$ Because of the way we carry out constraints (as explained below), we do not need to store the individual bits for $A''$: we only need the 32-bit limbs. - \item The final step, $\iota$, consists in updating the first element of the state as follows: $$A'''[0, 0] = A''[0, 0] \texttt{ xor } RC[i]$$ where $$A'''[x, y] = A''[x, y] \forall (x, y) \neq (0, 0)$$ Since only the first element is updated, we only need to store $A'''[0, 0]$ of this updated state. The remaining elements are fetched from $A''$. However, because of the bitwise $\texttt{xor}$ operation, we do need columns for the bits of $A''[0, 0]$. -\end{enumerate} - -Note that all permutation elements are 64-bit long. But they are stored as 32-bit limbs so that we do not overflow the field. - -It is also important to note that all bitwise logic operations ($\texttt{ xor }$, $\texttt{ not }$ and $\texttt{ and}$) are checked in this table. This is why we need to store the bits of most elements. The logic table can only carry out eight 32-bit logic operations per row. Thus, leveraging it here would drastically increase the number of logic rows, and incur too much overhead in proving time. - - - -\subsubsection{Columns} -Using the notations from the previous section, we can now list the columns in the table: -\begin{enumerate} - \item $\texttt{NUM\_ROUND}S = 24$ columns $c_i$ to determine which round is currently being computed. $c_i = 1$ when we are in the $i$-th round, and 0 otherwise. These columns' purpose is to ensure that the correct round constants are used at each round. - \item $1$ column $t$ which stores the timestamp at which the Keccak operation was called in the cpu. This column enables us to ensure that inputs and outputs are consistent between the cpu, keccak-sponge and keccak-f tables. - \item $5 \times 5 \times 2 = 50 $columns to store the elements of $A$. As a reminder, each 64-bit element is divided into two 32-bit limbs, and $A$ comprises $S = 25$ elements. - \item $5 \times 64 = 320$ columns to store the bits of the vector $C$. - \item $5 \times 64 = 320$ columns to store the bits of the vector $C'$. - \item $5 \times 5 \times 64 = 1600$ columns to store the bits of $A'$. - \item $5 \times 5 \times 2 = 50$ columns to store the 32-bit limbs of $A''$. - \item $64$ columns to store the bits of $A''[0, 0]$. - \item $2$ columns to store the two limbs of $A'''[0, 0]$. -\end{enumerate} - -In total, this table comprises 2,431 columns. - -\subsubsection{Constraints} -Some constraints checking that the elements are computed correctly are not straightforward. Let us detail them here. - -First, it is important to highlight the fact that a $\texttt{xor}$ between two elements is of degree 2. Indeed, for $x \texttt{ xor } y$, the constraint is $x + y - 2 \times x \times y$, which is of degree 2. This implies that a $\texttt{xor}$ between 3 elements is of degree 3, which is the maximal constraint degree for our STARKs. - -We can check that $C'[x, z] = C[x, z] \texttt{ xor } C[x - 1, z] \texttt{ xor } C[x + 1, z - 1]$. However, we cannot directly check that $C[x] = \texttt{xor}_{i=0}^4 A[x, i]$, as it would be a degree 5 constraint. Instead, we use $C'$ for this constraint. We see that: -$$\texttt{xor}_{i=0}^4 A'[x, i, z] = C'[x, z]$$ -This implies that the difference $d = \sum_{i=0}^4 A'[x, i, z] - C'[x, z]$ is either 0, 2 or 4. We can therefore enforce the following degree 3 constraint instead: -$$d \times (d - 2) \times (d - 4) = 0$$ - -Additionally, we have to check that $A'$ is well constructed. We know that $A'$ should be such that $A'[x, y, z] = A[x, y, z] \texttt{ xor } C[x, z] \texttt{ xor } C'[x, z]$. Since we do not have the bits of $A$ elements but the bits of $A'$ elements, we check the equivalent degree 3 constraint: -$$A[x, y, z] = A'[x, y, z] \texttt{ xor } C[x, z] \texttt { xor } C'[x, z]$$ - -Finally, the constraints for the remaining elements, $A''$ and $A'''$ are straightforward: $A''$ is a three-element bitwise $\texttt{xor}$ where all bits involved are already storedn and $A'''[0, 0]$ is the output of a simple bitwise $\texttt{xor}$ with a round constant. \ No newline at end of file diff --git a/evm/spec/tables/keccak-sponge.tex b/evm/spec/tables/keccak-sponge.tex deleted file mode 100644 index a712335b8f..0000000000 --- a/evm/spec/tables/keccak-sponge.tex +++ /dev/null @@ -1,66 +0,0 @@ -\subsection{KeccakSponge} -\label{keccak-sponge} - -This table computes the Keccak256 hash, a sponge-based hash built on top of the Keccak-f[1600] permutation. An instance of KeccakSponge takes as input a Memory address $a$, -a length $l$, and computes the Keccak256 digest of the memory segment starting at $a$ and of size $l$. An instance can span many rows, each individual row being a single call to -the Keccak table. Note that all the read elements must be bytes; the proof will be unverifiable if this is not the case. Following the Keccak specifications, the input string is padded to the next multiple of 136 bytes. -Each row contains the following columns: -\begin{itemize} - \item Read bytes: - \begin{itemize} - \item 3 address columns: \texttt{context}, \texttt{segment} and the offset \texttt{virt} of $a$. - \item \texttt{timestamp}: the timestamp which will be used for all memory reads of this instance. - \item \texttt{already\_absorbed\_bytes}: keeps track of how many bytes have been hashed in the current instance. At the end of an instance, we should have absorbed $l$ bytes in total. - \item \texttt{KECCAK\_RATE\_BYTES} \texttt{block\_bytes} columns: the bytes being absorbed at this row. They are read from memory and will be XORed to the rate part of the current state. - \end{itemize} - \item Input columns: - \begin{itemize} - \item \texttt{KECCAK\_RATE\_U32S} \texttt{original\_rate\_u32s} columns: hold the rate part of the state before XORing it with \texttt{block\_bytes}. At the beginning of an instance, they are initialized with 0. - \item \texttt{KECCAK\_RATE\_U32s} \texttt{xored\_rate\_u32s} columns: hold the original rate XORed with \texttt{block\_bytes}. - \item \texttt{KECCAK\_CAPACITY\_U32S} \texttt{original\_capacity\_u32s} columns: hold the capacity part of the state before applying the Keccak permutation. - \end{itemize} - \item Output columns: - \begin{itemize} - \item \texttt{KECCAK\_DIGEST\_BYTES} \texttt{updated\_digest\_state\_bytes columns}: the beginning of the output state after applying the Keccak permutation. At the last row of an instance, they hold the computed hash. -They are decomposed in bytes for endianness reasons. - \item \texttt{KECCAK\_WIDTH\_MINUS\_DIGEST\_U32S} \texttt{partial\_updated\_state\_u32s} columns: the rest of the output state. They are discarded for the final digest, but are used between instance rows. - \end{itemize} - \item Helper columns: - \begin{itemize} - \item \texttt{is\_full\_input\_block}: indicates if the current row has a full input block, i.e. \texttt{block\_bytes} contains only bytes read from memory and no padding bytes. - \item \texttt{KECCAK\_RATE\_BYTES} \texttt{is\_final\_input\_len} columns: in the final row of an instance, indicate where the final read byte is. If the $i$-th column is set to 1, it means that -all bytes after the $i$-th are padding bytes. In a full input block, all columns are set to 0. - \end{itemize} -\end{itemize} - -For each instance, constraints ensure that: -\begin{itemize} - \item at each row: - \begin{itemize} - \item \texttt{is\_full\_input\_block} and \texttt{is\_final\_input\_len} columns are all binary. - \item Only one column in \texttt{is\_full\_input\_block} and \texttt{is\_final\_input\_len} is set to 1. - \item \texttt{xored\_rate\_u32s} is \texttt{original\_rate\_u32s} XOR \texttt{block\_bytes}. - \item The CTL with Keccak ensures that (\texttt{updated\_digest\_state\_bytes columns}, \texttt{partial\_updated\_state\_u32s}) is the Keccak permutation output of (\texttt{xored\_rate\_u32s}, \texttt{original\_capacity\_u32s}). - \end{itemize} - \item at the first row: - \begin{itemize} - \item \texttt{original\_rate\_u32s} is all 0. - \item \texttt{already\_absorbed\_bytes} is 0. - \end{itemize} - \item at each full input row (i.e. \texttt{is\_full\_input\_block} is 1, all \texttt{is\_final\_input\_len} columns are 0): - \begin{itemize} - \item \texttt{context}, \texttt{segment}, \texttt{virt} and \texttt{timestamp} are unchanged in the next row. - \item Next \texttt{already\_absorbed\_bytes} is current \texttt{already\_absorbed\_bytes} + \texttt{KECCAK\_RATE\_BYTES}. - \item Next (\texttt{original\_rate\_u32s}, \texttt{original\_capacity\_u32s}) is current (\texttt{updated\_digest\_state\_bytes columns}, \texttt{partial\_updated\_state\_u32s}). - \item The CTL with Memory ensures that \texttt{block\_bytes} is filled with contiguous memory elements [$a$ + \texttt{already\_absorbed\_bytes}, $a$ + \texttt{already\_absorbed\_bytes} + \texttt{KECCAK\_RATE\_BYTES} - 1] - \end{itemize} - \item at the final row (i.e. \texttt{is\_full\_input\_block} is 0, \texttt{is\_final\_input\_len}'s $i$-th column is 1 for a certain $i$, the rest are 0): - \begin{itemize} - \item The CTL with Memory ensures that \texttt{block\_bytes} is filled with contiguous memory elements [$a$ + \texttt{already\_absorbed\_bytes}, $a$ + \texttt{already\_absorbed\_bytes} + $i$ - 1]. The rest are padding bytes. - \item The CTL with CPU ensures that \texttt{context}, \texttt{segment}, \texttt{virt} and \texttt{timestamp} match the \texttt{KECCAK\_GENERAL} call. - \item The CTL with CPU ensures that $l$ = \texttt{already\_absorbed\_bytes} + $i$. - \item The CTL with CPU ensures that \texttt{updated\_digest\_state\_bytes} is the output of the \texttt{KECCAK\_GENERAL} call. - \end{itemize} -\end{itemize} - -The trace is padded to the next power of two with dummy rows, whose \texttt{is\_full\_input\_block} and \texttt{is\_final\_input\_len} columns are all 0. diff --git a/evm/spec/tables/logic.tex b/evm/spec/tables/logic.tex deleted file mode 100644 index e2425fc4a8..0000000000 --- a/evm/spec/tables/logic.tex +++ /dev/null @@ -1,18 +0,0 @@ -\subsection{Logic} -\label{logic} - -Each row of the logic table corresponds to one bitwise logic operation: either AND, OR or XOR. Each input for these operations is represented as 256 bits, while the output is stored as eight 32-bit limbs. - -Each row therefore contains the following columns: -\begin{enumerate} - \item $f_{\texttt{and}}$, an ``is and'' flag, which should be 1 for an OR operation and 0 otherwise, - \item $f_{\texttt{or}}$, an ``is or'' flag, which should be 1 for an OR operation and 0 otherwise, - \item $f_{\texttt{xor}}$, an ``is xor'' flag, which should be 1 for a XOR operation and 0 otherwise, - \item 256 columns $x_{1, i}$ for the bits of the first input $x_1$, - \item 256 columns $x_{2, i}$ for the bits of the second input $x_2$, - \item 8 columns $r_i$ for the 32-bit limbs of the output $r$. -\end{enumerate} - -Note that we need all three flags because we need to be able to distinguish between an operation row and a padding row -- where all flags are set to 0. - -The subdivision into bits is required for the two inputs as the table carries out bitwise operations. The result, on the other hand, is represented in 32-bit limbs since we do not need individual bits and can therefore save the remaining 248 columns. Moreover, the output is checked against the cpu, which stores values in the same way. diff --git a/evm/spec/tables/memory.tex b/evm/spec/tables/memory.tex deleted file mode 100644 index d39e99b23d..0000000000 --- a/evm/spec/tables/memory.tex +++ /dev/null @@ -1,87 +0,0 @@ -\subsection{Memory} -\label{memory} - -For simplicity, let's treat addresses and values as individual field elements. The generalization to multi-element addresses and values is straightforward. - -Each row of the memory table corresponds to a single memory operation (a read or a write), and contains the following columns: - -\begin{enumerate} - \item $a$, the target address - \item $r$, an ``is read'' flag, which should be 1 for a read or 0 for a write - \item $v$, the value being read or written - \item $\tau$, the timestamp of the operation -\end{enumerate} -The memory table should be ordered by $(a, \tau)$. Note that the correctness of the memory could be checked as follows: -\begin{enumerate} - \item Verify the ordering by checking that $(a_i, \tau_i) \leq (a_{i+1}, \tau_{i+1})$ for each consecutive pair. - \item Enumerate the purportedly-ordered log while tracking the ``current'' value of $v$. - \begin{enumerate} - \item Upon observing an address which doesn't match that of the previous row, if the address is zero-initialized - and if the operation is a read, check that $v = 0$. - \item Upon observing a write, don't constrain $v$. - \item Upon observing a read at timestamp $\tau_i$ which isn't the first operation at this address, check that $v_i = v_{i-1}$. - \end{enumerate} -\end{enumerate} - -The ordering check is slightly involved since we are comparing multiple columns. To facilitate this, we add an additional column $e$, where the prover can indicate whether two consecutive addresses changed. An honest prover will set -$$ -e_i \leftarrow \begin{cases} - 1 & \text{if } a_i \neq a_{i + 1}, \\ - 0 & \text{otherwise}. -\end{cases} -$$ -We also introduce a range-check column $c$, which should hold: -$$ -c_i \leftarrow \begin{cases} - a_{i + 1} - a_i - 1 & \text{if } e_i = 1, \\ - \tau_{i+1} - \tau_i & \text{otherwise}. -\end{cases} -$$ -The extra $-1$ ensures that the address actually changed if $e_i = 1$. -We then impose the following transition constraints: -\begin{enumerate} - \item $e_i (e_i - 1) = 0$, - \item $(1 - e_i) (a_{i + 1} - a_i) = 0$, - \item $c_i < 2^{32}$. -\end{enumerate} -The third constraint emulates a comparison between two addresses or timestamps by bounding their difference; this assumes that all addresses and timestamps fit in 32 bits and that the field is larger than that. - -\subsubsection{Virtual memory} - -In the EVM, each contract call has its own address space. Within that address space, there are separate segments for code, main memory, stack memory, calldata, and returndata. Thus each address actually has three compoments: -\begin{enumerate} - \item an execution context, representing a contract call, - \item a segment ID, used to separate code, main memory, and so forth, and so on - \item a virtual address. -\end{enumerate} -The comparisons now involve several columns, which requires some minor adaptations to the technique described above; we will leave these as an exercise to the reader. - -Note that an additional constraint check is required: whenever we change the context or the segment, the virtual address must be range-checked to $2^{32}$. -Without this check, addresses could start at -1 (i.e. $p - 2$) and then increase properly. - -\subsubsection{Timestamps} - -Memory operations are sorted by address $a$ and timestamp $\tau$. For a memory operation in the CPU, we have: -$$\tau = \texttt{NUM\_CHANNELS} \times \texttt{cycle} + \texttt{channel}.$$ -Since a memory channel can only hold at most one memory operation, every CPU memory operation's timestamp is unique. - -Note that it doesn't mean that all memory operations have unique timestamps. There are two exceptions: - -\begin{itemize} - \item Before the CPU cycles, we write some global metadata in memory. These extra operations are done at timestamp $\tau = 0$. - \item Some tables other than CPU can generate memory operations, like KeccakSponge. When this happens, these operations all have the timestamp of the CPU row of the instruction which invoked the table (for KeccakSponge, KECCAK\_GENERAL). -\end{itemize} - -\subsubsection{Memory initialization} - -By default, all memory is zero-initialized. However, to save numerous writes, we allow some specific segments to be initialized with arbitrary values. - -\begin{itemize} - \item The read-only kernel code (in segment 0, context 0) is initialized with its correct values. It's checked by hashing the segment and verifying -that the hash value matches a verifier-provided one. - \item The code segment (segment 0) in other contexts is initialized with externally-provided account code, then checked against the account code hash. -If the code is meant to be executed, there is a soundness concern: if the code is malformed and ends with an incomplete PUSH, then the missing bytes must -be 0 accordingly to the Ethereum specs. To prevent the issue, we manually write 33 zeros (at most 32 bytes for the PUSH argument, and an extra one for -the post-PUSH PC value). - \item The ``TrieData'' segment is initialized with the input tries. The stored tries are hashed and checked against the provided initial hash. Note that the length of the segment and the pointers -- within the ``TrieData'' segment -- for the three tries are provided as prover inputs. The length is then checked against a value computed when hashing the tries. -\end{itemize} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf deleted file mode 100644 index 3b10fba30b89f1ad27d84f3a860fbb31286cb1e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 296911 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íXh%#o<_S^T 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%0DGz$`(c)|KN2KeysjfBfGwzdYV1NIyuX;+ z2jf8F!vV}m&haFmg_FURg~7vGIyHT^n~2;jfpvBhnMjk<^wtAi> zOGsg!jRVMwH@&WL1VPzTnd)sCYU{&RAR0*QOQU|VgGU*G)ba8Pr$HC;G2~?hi?FT^wMjOn8(mwspBrFOWcqE~2Hu-Q#cz9V3wX?Zi4 zWbWtml6Tne(Zq_7{%W?f0h27{>60J;Q!C5%Zj}$UMnKRRLnFMg^;k6@jvVsim}kay zv&w|?u&^(+AN8TOQSyvJLy2M=)95KwwbKC4DcLMUeG67s$`q+Yi^6+7tN;^jB z(Z)2T4VPP$Ux&5bvxF~*KRYwEYkJIEZE>KLXi1SFjrX#o4xFBh3>ErsZ|bK^=(H7I zmCq{W&rY5?qj*p|2;3;03lac-U7tL`Pcw_0MBYeEN7j|tK|p4D$qjE$bVwe)vZMAW zO_PZuLKh+93Yd9fo=snCewN~ZV7&7Az1QwqiIyK*tlBxTb&q#keCm67#0>e`GlMqc z11y6Q#F~<8gm}L(-|)pg6I1_Vgfje>DTJQme|}MSV@}#$wYc2YVmK?oTGo3@9(9is zi6jB&pqTF$N3}tFka&fglZY*>b-#b)3Mm$i&f)EP#q{}$D2j!heq*&1+>NqII}I#{ zGRX$-T*0aJ>Lg^JELjf_>)75?ftPG4#(*bd^mwebjmD`_>&pRuI1l``W>xk65S)if zC0=IR*xm$$M$-?A5!p^m(~a-`es9CGZT{|j9Yzvq_Pu{kLtbOf{|5FV+Is622ApD# zge%xHEbu>*O>z_{dH~tJEVH6=$iOWZT=Tv^%Wp03x9zr$c065n+Dtr>beVTHTvRS% zyCd@UltdDV_4%^)W-`eH188n{50BbRG~nciJk3!FIu+*N;V|zR8)sEgOZ}|F1q2%C z!5mWJTyNA=xe6&g`{Y01B?QTXjOqGkW{@DAIjVn~ac8-M)4Em7kOrcT zwKJ7B+tATBN%prlGEWn=4@J8s*kf|ymtmwNGun4Z0&;e=Ra7VtFHC-8Oc=vN`JjO|pc#i?g()$HE=>mvnrX?9`J<`K2Wr54w}(J_^HJ&L7=6$JF)E_rN=tqKCfq3W ze&axu*zhb+WB0@D?k~h34PZp#uy*_XXAn?DEehp4T5v*$7jr&ogi3F46J( zp`F8|n0q%-+}w>F{2yHx8>n&{BXF&y7dL#Hgfd3{n)@bQG;xOta zjoBRBN-IC*7TGddk7gP8Sp6(>GK6ILQo78)9^=;S@$t2dPP(pk2O@MH^SS7UO{j2S zzc^O~7hwl?7;@$~NCDY`-psL-`obUdl)4hU9LCo8#K6gJ*!YM6NSom67xMtHTVuwn zVT20wAc+nGy)S%E65$<}SyzD4wc15(cLK zkPVjHf;j~GdYfV0lPJ$+cO#@!Xn$cQnyC@Lc<1g2>WNeuN|{wz?+Jr>Eq>RRWVYRa3- zuMB^{Nu)>zv`KY3aUa0h1J%sffhh+YH9v!6JfW39;(^sxegGUg>oCKsKr(_-N%cUp zU2B<>fc3gMUx4ORfjn4WZg`pK4d*Bd0*dK`iNY*G@BhsPH9>bsHJSH1$!b*wiv#q3 zAJ%{M2#wH?Y_IWM^`LTU#%)P-e5zkM0!y-)LSf!lQhn=ke2>Ots1SXC@mBp}PJ?MZp6$Wj;4GDFRSaH27F0X* zYU2_uUC+z+?=|!HlZji{Bn*o!ysNB1t|CX)=PUS;Y®`rBf&`!;_MPC7%5-F<);xg` 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:yH#=ETzPW_ykTTj&YlN`uOZlz){+0fm0^1e8K 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%0Dati#}R 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

Exd1BX2Y4@s#=6?(reu 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 z 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 TiIs<)hl3S~l2SrR66rqx{m}Hk diff --git a/evm/spec/zkevm.tex b/evm/spec/zkevm.tex deleted file mode 100644 index ee2c38a54e..0000000000 --- a/evm/spec/zkevm.tex +++ /dev/null @@ -1,61 +0,0 @@ -\documentclass[12pt]{article} -\usepackage{amsmath} -\usepackage{amssymb} -\usepackage{cite} -\usepackage{draftwatermark} -\usepackage[margin=1.5in]{geometry} -\usepackage{hyperref} -\usepackage{makecell} -\usepackage{mathtools} -\usepackage{tabularx} -\usepackage{enumitem} -\usepackage[textwidth=1.25in]{todonotes} - -% Scale for DRAFT watermark. -\SetWatermarkFontSize{24cm} -\SetWatermarkScale{5} -\SetWatermarkLightness{0.92} - -% Hyperlink colors. -\hypersetup{ - colorlinks=true, - linkcolor=blue, - citecolor=blue, - urlcolor=blue, -} - -% We want all section autorefs to say "Section". -\def\sectionautorefname{Section} -\let\subsectionautorefname\sectionautorefname -\let\subsubsectionautorefname\sectionautorefname - -% \abs{...}, \floor{...} and \ceil{...} -\DeclarePairedDelimiter\abs{\lvert}{\rvert} -\DeclarePairedDelimiter\ceil{\lceil}{\rceil} -\DeclarePairedDelimiter\floor{\lfloor}{\rfloor} - -\title{The Polygon Zero zkEVM} -%\author{Polygon Zero Team} -\date{DRAFT\\\today} - -\begin{document} -\maketitle - -\begin{abstract} - We describe the design of Polygon Zero's zkEVM, ... -\end{abstract} - -\newpage -{\hypersetup{hidelinks} \tableofcontents} -\newpage - -\input{introduction} -\input{framework} -\input{tables} -\input{mpts} -\input{cpulogic} - -\bibliography{bibliography}{} -\bibliographystyle{ieeetr} - -\end{document} diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs deleted file mode 100644 index ec218ef8e7..0000000000 --- a/evm/src/all_stark.rs +++ /dev/null @@ -1,300 +0,0 @@ -use core::ops::Deref; - -use plonky2::field::extension::Extendable; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use starky::config::StarkConfig; -use starky::cross_table_lookup::{CrossTableLookup, TableIdx, TableWithColumns}; -use starky::evaluation_frame::StarkFrame; -use starky::stark::Stark; - -use crate::arithmetic::arithmetic_stark; -use crate::arithmetic::arithmetic_stark::ArithmeticStark; -use crate::byte_packing::byte_packing_stark::{self, BytePackingStark}; -use crate::cpu::cpu_stark; -use crate::cpu::cpu_stark::CpuStark; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::keccak::keccak_stark; -use crate::keccak::keccak_stark::KeccakStark; -use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; -use crate::keccak_sponge::keccak_sponge_stark; -use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; -use crate::logic; -use crate::logic::LogicStark; -use crate::memory::memory_stark; -use crate::memory::memory_stark::MemoryStark; - -/// Structure containing all STARKs and the cross-table lookups. -#[derive(Clone)] -pub struct AllStark, const D: usize> { - pub(crate) arithmetic_stark: ArithmeticStark, - pub(crate) byte_packing_stark: BytePackingStark, - pub(crate) cpu_stark: CpuStark, - pub(crate) keccak_stark: KeccakStark, - pub(crate) keccak_sponge_stark: KeccakSpongeStark, - pub(crate) logic_stark: LogicStark, - pub(crate) memory_stark: MemoryStark, - pub(crate) cross_table_lookups: Vec>, -} - -impl, const D: usize> Default for AllStark { - /// Returns an `AllStark` containing all the STARKs initialized with default values. - fn default() -> Self { - Self { - arithmetic_stark: ArithmeticStark::default(), - byte_packing_stark: BytePackingStark::default(), - cpu_stark: CpuStark::default(), - keccak_stark: KeccakStark::default(), - keccak_sponge_stark: KeccakSpongeStark::default(), - logic_stark: LogicStark::default(), - memory_stark: MemoryStark::default(), - cross_table_lookups: all_cross_table_lookups(), - } - } -} - -impl, const D: usize> AllStark { - pub(crate) fn num_lookups_helper_columns(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - [ - self.arithmetic_stark.num_lookup_helper_columns(config), - self.byte_packing_stark.num_lookup_helper_columns(config), - self.cpu_stark.num_lookup_helper_columns(config), - self.keccak_stark.num_lookup_helper_columns(config), - self.keccak_sponge_stark.num_lookup_helper_columns(config), - self.logic_stark.num_lookup_helper_columns(config), - self.memory_stark.num_lookup_helper_columns(config), - ] - } -} - -pub type EvmStarkFrame = StarkFrame; - -/// Associates STARK tables with a unique index. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Table { - Arithmetic = 0, - BytePacking = 1, - Cpu = 2, - Keccak = 3, - KeccakSponge = 4, - Logic = 5, - Memory = 6, -} - -impl Deref for Table { - type Target = TableIdx; - - fn deref(&self) -> &Self::Target { - // Hacky way to implement `Deref` for `Table` so that we don't have to - // call `Table::Foo as usize`, but perhaps too ugly to be worth it. - [&0, &1, &2, &3, &4, &5, &6][*self as TableIdx] - } -} - -/// Number of STARK tables. -pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1; - -impl Table { - /// Returns all STARK table indices. - pub(crate) const fn all() -> [Self; NUM_TABLES] { - [ - Self::Arithmetic, - Self::BytePacking, - Self::Cpu, - Self::Keccak, - Self::KeccakSponge, - Self::Logic, - Self::Memory, - ] - } -} - -/// Returns all the `CrossTableLookups` used for proving the EVM. -pub(crate) fn all_cross_table_lookups() -> Vec> { - vec![ - ctl_arithmetic(), - ctl_byte_packing(), - ctl_keccak_sponge(), - ctl_keccak_inputs(), - ctl_keccak_outputs(), - ctl_logic(), - ctl_memory(), - ] -} - -/// `CrossTableLookup` for `ArithmeticStark`, to connect it with the `Cpu` module. -fn ctl_arithmetic() -> CrossTableLookup { - CrossTableLookup::new( - vec![cpu_stark::ctl_arithmetic_base_rows()], - arithmetic_stark::ctl_arithmetic_rows(), - ) -} - -/// `CrossTableLookup` for `BytePackingStark`, to connect it with the `Cpu` module. -fn ctl_byte_packing() -> CrossTableLookup { - let cpu_packing_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_byte_packing(), - Some(cpu_stark::ctl_filter_byte_packing()), - ); - let cpu_unpacking_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_byte_unpacking(), - Some(cpu_stark::ctl_filter_byte_unpacking()), - ); - let cpu_push_packing_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_byte_packing_push(), - Some(cpu_stark::ctl_filter_byte_packing_push()), - ); - let cpu_jumptable_read_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_jumptable_read(), - Some(cpu_stark::ctl_filter_syscall_exceptions()), - ); - let byte_packing_looked = TableWithColumns::new( - *Table::BytePacking, - byte_packing_stark::ctl_looked_data(), - Some(byte_packing_stark::ctl_looked_filter()), - ); - CrossTableLookup::new( - vec![ - cpu_packing_looking, - cpu_unpacking_looking, - cpu_push_packing_looking, - cpu_jumptable_read_looking, - ], - byte_packing_looked, - ) -} - -/// `CrossTableLookup` for `KeccakStark` inputs, to connect it with the `KeccakSponge` module. -/// `KeccakStarkSponge` looks into `KeccakStark` to give the inputs of the sponge. -/// Its consistency with the 'output' CTL is ensured through a timestamp column on the `KeccakStark` side. -fn ctl_keccak_inputs() -> CrossTableLookup { - let keccak_sponge_looking = TableWithColumns::new( - *Table::KeccakSponge, - keccak_sponge_stark::ctl_looking_keccak_inputs(), - Some(keccak_sponge_stark::ctl_looking_keccak_filter()), - ); - let keccak_looked = TableWithColumns::new( - *Table::Keccak, - keccak_stark::ctl_data_inputs(), - Some(keccak_stark::ctl_filter_inputs()), - ); - CrossTableLookup::new(vec![keccak_sponge_looking], keccak_looked) -} - -/// `CrossTableLookup` for `KeccakStark` outputs, to connect it with the `KeccakSponge` module. -/// `KeccakStarkSponge` looks into `KeccakStark` to give the outputs of the sponge. -fn ctl_keccak_outputs() -> CrossTableLookup { - let keccak_sponge_looking = TableWithColumns::new( - *Table::KeccakSponge, - keccak_sponge_stark::ctl_looking_keccak_outputs(), - Some(keccak_sponge_stark::ctl_looking_keccak_filter()), - ); - let keccak_looked = TableWithColumns::new( - *Table::Keccak, - keccak_stark::ctl_data_outputs(), - Some(keccak_stark::ctl_filter_outputs()), - ); - CrossTableLookup::new(vec![keccak_sponge_looking], keccak_looked) -} - -/// `CrossTableLookup` for `KeccakSpongeStark` to connect it with the `Cpu` module. -fn ctl_keccak_sponge() -> CrossTableLookup { - let cpu_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_keccak_sponge(), - Some(cpu_stark::ctl_filter_keccak_sponge()), - ); - let keccak_sponge_looked = TableWithColumns::new( - *Table::KeccakSponge, - keccak_sponge_stark::ctl_looked_data(), - Some(keccak_sponge_stark::ctl_looked_filter()), - ); - CrossTableLookup::new(vec![cpu_looking], keccak_sponge_looked) -} - -/// `CrossTableLookup` for `LogicStark` to connect it with the `Cpu` and `KeccakSponge` modules. -fn ctl_logic() -> CrossTableLookup { - let cpu_looking = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_logic(), - Some(cpu_stark::ctl_filter_logic()), - ); - let mut all_lookers = vec![cpu_looking]; - for i in 0..keccak_sponge_stark::num_logic_ctls() { - let keccak_sponge_looking = TableWithColumns::new( - *Table::KeccakSponge, - keccak_sponge_stark::ctl_looking_logic(i), - Some(keccak_sponge_stark::ctl_looking_logic_filter()), - ); - all_lookers.push(keccak_sponge_looking); - } - let logic_looked = - TableWithColumns::new(*Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); - CrossTableLookup::new(all_lookers, logic_looked) -} - -/// `CrossTableLookup` for `MemoryStark` to connect it with all the modules which need memory accesses. -fn ctl_memory() -> CrossTableLookup { - let cpu_memory_code_read = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_code_memory(), - Some(cpu_stark::ctl_filter_code_memory()), - ); - let cpu_memory_gp_ops = (0..NUM_GP_CHANNELS).map(|channel| { - TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_gp_memory(channel), - Some(cpu_stark::ctl_filter_gp_memory(channel)), - ) - }); - let cpu_push_write_ops = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_partial_memory::(), - Some(cpu_stark::ctl_filter_partial_memory()), - ); - let cpu_set_context_write = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_memory_old_sp_write_set_context::(), - Some(cpu_stark::ctl_filter_set_context()), - ); - let cpu_set_context_read = TableWithColumns::new( - *Table::Cpu, - cpu_stark::ctl_data_memory_new_sp_read_set_context::(), - Some(cpu_stark::ctl_filter_set_context()), - ); - let keccak_sponge_reads = (0..KECCAK_RATE_BYTES).map(|i| { - TableWithColumns::new( - *Table::KeccakSponge, - keccak_sponge_stark::ctl_looking_memory(i), - Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), - ) - }); - let byte_packing_ops = (0..32).map(|i| { - TableWithColumns::new( - *Table::BytePacking, - byte_packing_stark::ctl_looking_memory(i), - Some(byte_packing_stark::ctl_looking_memory_filter(i)), - ) - }); - let all_lookers = vec![ - cpu_memory_code_read, - cpu_push_write_ops, - cpu_set_context_write, - cpu_set_context_read, - ] - .into_iter() - .chain(cpu_memory_gp_ops) - .chain(keccak_sponge_reads) - .chain(byte_packing_ops) - .collect(); - let memory_looked = TableWithColumns::new( - *Table::Memory, - memory_stark::ctl_data(), - Some(memory_stark::ctl_filter()), - ); - CrossTableLookup::new(all_lookers, memory_looked) -} diff --git a/evm/src/arithmetic/addcy.rs b/evm/src/arithmetic/addcy.rs deleted file mode 100644 index 94d2bd1697..0000000000 --- a/evm/src/arithmetic/addcy.rs +++ /dev/null @@ -1,355 +0,0 @@ -//! Support for EVM instructions ADD, SUB, LT and GT -//! -//! This crate verifies EVM instructions ADD, SUB, LT and GT (i.e. for -//! unsigned inputs). Each of these instructions can be verified using -//! the "add with carry out" equation -//! -//! X + Y = Z + CY * 2^256 -//! -//! by an appropriate assignment of "inputs" and "outputs" to the -//! variables X, Y, Z and CY. Specifically, -//! -//! ADD: X + Y, inputs X, Y, output Z, ignore CY -//! SUB: Z - X, inputs X, Z, output Y, ignore CY -//! GT: X > Z, inputs X, Z, output CY, auxiliary output Y -//! LT: Z < X, inputs Z, X, output CY, auxiliary output Y - -use ethereum_types::U256; -use itertools::Itertools; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::{Field, PrimeField64}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::arithmetic::columns::*; -use crate::arithmetic::utils::u256_to_array; - -/// Generate row for ADD, SUB, GT and LT operations. -pub(crate) fn generate( - lv: &mut [F], - filter: usize, - left_in: U256, - right_in: U256, -) { - u256_to_array(&mut lv[INPUT_REGISTER_0], left_in); - u256_to_array(&mut lv[INPUT_REGISTER_1], right_in); - u256_to_array(&mut lv[INPUT_REGISTER_2], U256::zero()); - - match filter { - IS_ADD => { - let (result, cy) = left_in.overflowing_add(right_in); - u256_to_array(&mut lv[AUX_INPUT_REGISTER_0], U256::from(cy as u32)); - u256_to_array(&mut lv[OUTPUT_REGISTER], result); - } - IS_SUB => { - let (diff, cy) = left_in.overflowing_sub(right_in); - u256_to_array(&mut lv[AUX_INPUT_REGISTER_0], U256::from(cy as u32)); - u256_to_array(&mut lv[OUTPUT_REGISTER], diff); - } - IS_LT => { - let (diff, cy) = left_in.overflowing_sub(right_in); - u256_to_array(&mut lv[AUX_INPUT_REGISTER_0], diff); - u256_to_array(&mut lv[OUTPUT_REGISTER], U256::from(cy as u32)); - } - IS_GT => { - let (diff, cy) = right_in.overflowing_sub(left_in); - u256_to_array(&mut lv[AUX_INPUT_REGISTER_0], diff); - u256_to_array(&mut lv[OUTPUT_REGISTER], U256::from(cy as u32)); - } - _ => panic!("unexpected operation filter"), - }; -} - -/// 2^-16 mod (2^64 - 2^32 + 1) -const GOLDILOCKS_INVERSE_65536: u64 = 18446462594437939201; - -/// Constrains x + y == z + cy*2^256, assuming filter != 0. -/// -/// Set `is_two_row_op=true` to allow the code to be called from the -/// two-row `modular` code (for checking that the modular output is -/// reduced). -/// -/// NB: This function ONLY verifies that cy is 0 or 1 when -/// is_two_row_op=false; when is_two_row_op=true the caller must -/// verify for itself. -/// -/// Note that the digits of `x + y` are in `[0, 2*(2^16-1)]` -/// (i.e. they are the sums of two 16-bit numbers), whereas the digits -/// of `z` can only be in `[0, 2^16-1]`. In the function we check that: -/// -/// \sum_i (x_i + y_i) * 2^(16*i) = \sum_i z_i * 2^(16*i) + given_cy*2^256. -/// -/// If `N_LIMBS = 1`, then this amounts to verifying that either `x_0 -/// + y_0 = z_0` or `x_0 + y_0 == z_0 + cy*2^16` (this is `t` on line -/// 127ff). Ok. Now assume the constraints are valid for `N_LIMBS = -/// n-1`. Then by induction, -/// -/// \sum_{i=0}^{n-1} (x_i + y_i) * 2^(16*i) + (x_n + y_n)*2^(16*n) == -/// \sum_{i=0}^{n-1} z_i * 2^(16*i) + cy_{n-1}*2^(16*n) + z_n*2^(16*n) -/// + cy_n*2^(16*n) -/// -/// is true if `(x_n + y_n)*2^(16*n) == cy_{n-1}*2^(16*n) + -/// z_n*2^(16*n) + cy_n*2^(16*n)` (again, this is `t` on line 127ff) -/// with the last `cy_n` checked against the `given_cy` given as input. -pub(crate) fn eval_packed_generic_addcy( - yield_constr: &mut ConstraintConsumer

, - filter: P, - x: &[P], - y: &[P], - z: &[P], - given_cy: &[P], - is_two_row_op: bool, -) { - debug_assert!( - x.len() == N_LIMBS && y.len() == N_LIMBS && z.len() == N_LIMBS && given_cy.len() == N_LIMBS - ); - - let overflow = P::Scalar::from_canonical_u64(1u64 << LIMB_BITS); - let overflow_inv = P::Scalar::from_canonical_u64(GOLDILOCKS_INVERSE_65536); - debug_assert!( - overflow * overflow_inv == P::Scalar::ONE, - "only works with LIMB_BITS=16 and F=Goldilocks" - ); - - let mut cy = P::ZEROS; - for ((&xi, &yi), &zi) in x.iter().zip_eq(y).zip_eq(z) { - // Verify that (xi + yi) - zi is either 0 or 2^LIMB_BITS - let t = cy + xi + yi - zi; - if is_two_row_op { - yield_constr.constraint_transition(filter * t * (overflow - t)); - } else { - yield_constr.constraint(filter * t * (overflow - t)); - } - // cy <-- 0 or 1 - // NB: this is multiplication by a constant, so doesn't - // increase the degree of the constraint. - cy = t * overflow_inv; - } - - if is_two_row_op { - // NB: Mild hack: We don't check that given_cy[0] is 0 or 1 - // when is_two_row_op is true because that's only the case - // when this function is called from - // modular::modular_constr_poly(), in which case (1) this - // condition has already been checked and (2) it exceeds the - // degree budget because given_cy[0] is already degree 2. - yield_constr.constraint_transition(filter * (cy - given_cy[0])); - for i in 1..N_LIMBS { - yield_constr.constraint_transition(filter * given_cy[i]); - } - } else { - yield_constr.constraint(filter * given_cy[0] * (given_cy[0] - P::ONES)); - yield_constr.constraint(filter * (cy - given_cy[0])); - for i in 1..N_LIMBS { - yield_constr.constraint(filter * given_cy[i]); - } - } -} - -pub(crate) fn eval_packed_generic( - lv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - let is_add = lv[IS_ADD]; - let is_sub = lv[IS_SUB]; - let is_lt = lv[IS_LT]; - let is_gt = lv[IS_GT]; - - let in0 = &lv[INPUT_REGISTER_0]; - let in1 = &lv[INPUT_REGISTER_1]; - let out = &lv[OUTPUT_REGISTER]; - let aux = &lv[AUX_INPUT_REGISTER_0]; - - // x + y = z + w*2^256 - eval_packed_generic_addcy(yield_constr, is_add, in0, in1, out, aux, false); - eval_packed_generic_addcy(yield_constr, is_sub, in1, out, in0, aux, false); - eval_packed_generic_addcy(yield_constr, is_lt, in1, aux, in0, out, false); - eval_packed_generic_addcy(yield_constr, is_gt, in0, aux, in1, out, false); -} - -#[allow(clippy::needless_collect)] -pub(crate) fn eval_ext_circuit_addcy, const D: usize>( - builder: &mut CircuitBuilder, - yield_constr: &mut RecursiveConstraintConsumer, - filter: ExtensionTarget, - x: &[ExtensionTarget], - y: &[ExtensionTarget], - z: &[ExtensionTarget], - given_cy: &[ExtensionTarget], - is_two_row_op: bool, -) { - debug_assert!( - x.len() == N_LIMBS && y.len() == N_LIMBS && z.len() == N_LIMBS && given_cy.len() == N_LIMBS - ); - - // 2^LIMB_BITS in the base field - let overflow_base = F::from_canonical_u64(1 << LIMB_BITS); - // 2^LIMB_BITS in the extension field as an ExtensionTarget - let overflow = builder.constant_extension(F::Extension::from(overflow_base)); - // 2^-LIMB_BITS in the base field. - let overflow_inv = F::from_canonical_u64(GOLDILOCKS_INVERSE_65536); - - let mut cy = builder.zero_extension(); - for ((&xi, &yi), &zi) in x.iter().zip_eq(y).zip_eq(z) { - // t0 = cy + xi + yi - let t0 = builder.add_many_extension([cy, xi, yi]); - // t = t0 - zi - let t = builder.sub_extension(t0, zi); - // t1 = overflow - t - let t1 = builder.sub_extension(overflow, t); - // t2 = t * t1 - let t2 = builder.mul_extension(t, t1); - - let filtered_limb_constraint = builder.mul_extension(filter, t2); - if is_two_row_op { - yield_constr.constraint_transition(builder, filtered_limb_constraint); - } else { - yield_constr.constraint(builder, filtered_limb_constraint); - } - - cy = builder.mul_const_extension(overflow_inv, t); - } - - let good_cy = builder.sub_extension(cy, given_cy[0]); - let cy_filter = builder.mul_extension(filter, good_cy); - - // Check given carry is one bit - let bit_constr = builder.mul_sub_extension(given_cy[0], given_cy[0], given_cy[0]); - let bit_filter = builder.mul_extension(filter, bit_constr); - - if is_two_row_op { - yield_constr.constraint_transition(builder, cy_filter); - for i in 1..N_LIMBS { - let t = builder.mul_extension(filter, given_cy[i]); - yield_constr.constraint_transition(builder, t); - } - } else { - yield_constr.constraint(builder, bit_filter); - yield_constr.constraint(builder, cy_filter); - for i in 1..N_LIMBS { - let t = builder.mul_extension(filter, given_cy[i]); - yield_constr.constraint(builder, t); - } - } -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let is_add = lv[IS_ADD]; - let is_sub = lv[IS_SUB]; - let is_lt = lv[IS_LT]; - let is_gt = lv[IS_GT]; - - let in0 = &lv[INPUT_REGISTER_0]; - let in1 = &lv[INPUT_REGISTER_1]; - let out = &lv[OUTPUT_REGISTER]; - let aux = &lv[AUX_INPUT_REGISTER_0]; - - eval_ext_circuit_addcy(builder, yield_constr, is_add, in0, in1, out, aux, false); - eval_ext_circuit_addcy(builder, yield_constr, is_sub, in1, out, in0, aux, false); - eval_ext_circuit_addcy(builder, yield_constr, is_lt, in1, aux, in0, out, false); - eval_ext_circuit_addcy(builder, yield_constr, is_gt, in0, aux, in1, out, false); -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::{Field, Sample}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::constraint_consumer::ConstraintConsumer; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - - // TODO: Should be able to refactor this test to apply to all operations. - #[test] - fn generate_eval_consistency_not_addcy() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // if the operation filters are all zero, then the constraints - // should be met even if all values are - // garbage. - lv[IS_ADD] = F::ZERO; - lv[IS_SUB] = F::ZERO; - lv[IS_LT] = F::ZERO; - lv[IS_GT] = F::ZERO; - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - F::ONE, - F::ONE, - F::ONE, - ); - eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, F::ZERO); - } - } - - #[test] - fn generate_eval_consistency_addcy() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - const N_ITERS: usize = 1000; - - for _ in 0..N_ITERS { - for op_filter in [IS_ADD, IS_SUB, IS_LT, IS_GT] { - // set entire row to random 16-bit values - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // set operation filter and ensure all constraints are - // satisfied. We have to explicitly set the other - // operation filters to zero since all are treated by - // the call. - lv[IS_ADD] = F::ZERO; - lv[IS_SUB] = F::ZERO; - lv[IS_LT] = F::ZERO; - lv[IS_GT] = F::ZERO; - lv[op_filter] = F::ONE; - - let left_in = U256::from(rng.gen::<[u8; 32]>()); - let right_in = U256::from(rng.gen::<[u8; 32]>()); - - generate(&mut lv, op_filter, left_in, right_in); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - F::ONE, - F::ONE, - F::ONE, - ); - eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, F::ZERO); - } - - let expected = match op_filter { - IS_ADD => left_in.overflowing_add(right_in).0, - IS_SUB => left_in.overflowing_sub(right_in).0, - IS_LT => U256::from((left_in < right_in) as u8), - IS_GT => U256::from((left_in > right_in) as u8), - _ => panic!("unrecognised operation"), - }; - - let mut expected_limbs = [F::ZERO; N_LIMBS]; - u256_to_array(&mut expected_limbs, expected); - assert!(expected_limbs - .iter() - .zip(&lv[OUTPUT_REGISTER]) - .all(|(x, y)| x == y)); - } - } - } -} diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs deleted file mode 100644 index 75fd9fe2a2..0000000000 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ /dev/null @@ -1,511 +0,0 @@ -use core::marker::PhantomData; -use core::ops::Range; - -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::util::transpose; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::cross_table_lookup::TableWithColumns; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter, Lookup}; -use starky::stark::Stark; -use static_assertions::const_assert; - -use super::columns::{op_flags, NUM_ARITH_COLUMNS}; -use super::shift; -use crate::all_stark::{EvmStarkFrame, Table}; -use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; -use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; - -/// Creates a vector of `Columns` to link the 16-bit columns of the arithmetic table, -/// split into groups of N_LIMBS at a time in `regs`, with the corresponding 32-bit -/// columns of the CPU table. Does this for all ops in `ops`. -/// -/// This is done by taking pairs of columns (x, y) of the arithmetic -/// table and combining them as x + y*2^16 to ensure they equal the -/// corresponding 32-bit number in the CPU table. -fn cpu_arith_data_link( - combined_ops: &[(usize, u8)], - regs: &[Range], -) -> Vec> { - let limb_base = F::from_canonical_u64(1 << columns::LIMB_BITS); - - let mut res = vec![Column::linear_combination( - combined_ops - .iter() - .map(|&(col, code)| (col, F::from_canonical_u8(code))), - )]; - - // The inner for loop below assumes N_LIMBS is even. - const_assert!(columns::N_LIMBS % 2 == 0); - - for reg_cols in regs { - // Loop below assumes we're operating on a "register" of N_LIMBS columns. - debug_assert_eq!(reg_cols.len(), columns::N_LIMBS); - - for i in 0..(columns::N_LIMBS / 2) { - let c0 = reg_cols.start + 2 * i; - let c1 = reg_cols.start + 2 * i + 1; - res.push(Column::linear_combination([(c0, F::ONE), (c1, limb_base)])); - } - } - res -} - -/// Returns the `TableWithColumns` for `ArithmeticStark` rows where one of the arithmetic operations has been called. -pub(crate) fn ctl_arithmetic_rows() -> TableWithColumns { - // We scale each filter flag with the associated opcode value. - // If an arithmetic operation is happening on the CPU side, - // the CTL will enforce that the reconstructed opcode value - // from the opcode bits matches. - // These opcodes are missing the syscall and prover_input opcodes, - // since `IS_RANGE_CHECK` can be associated to multiple opcodes. - // For `IS_RANGE_CHECK`, the opcodes are written in OPCODE_COL, - // and we use that column for scaling and the CTL checks. - // Note that we ensure in the STARK's constraints that the - // value in `OPCODE_COL` is 0 if `IS_RANGE_CHECK` = 0. - const COMBINED_OPS: [(usize, u8); 16] = [ - (columns::IS_ADD, 0x01), - (columns::IS_MUL, 0x02), - (columns::IS_SUB, 0x03), - (columns::IS_DIV, 0x04), - (columns::IS_MOD, 0x06), - (columns::IS_ADDMOD, 0x08), - (columns::IS_MULMOD, 0x09), - (columns::IS_ADDFP254, 0x0c), - (columns::IS_MULFP254, 0x0d), - (columns::IS_SUBFP254, 0x0e), - (columns::IS_SUBMOD, 0x0f), - (columns::IS_LT, 0x10), - (columns::IS_GT, 0x11), - (columns::IS_BYTE, 0x1a), - (columns::IS_SHL, 0x1b), - (columns::IS_SHR, 0x1c), - ]; - - const REGISTER_MAP: [Range; 4] = [ - columns::INPUT_REGISTER_0, - columns::INPUT_REGISTER_1, - columns::INPUT_REGISTER_2, - columns::OUTPUT_REGISTER, - ]; - - let mut filter_cols = COMBINED_OPS.to_vec(); - filter_cols.push((columns::IS_RANGE_CHECK, 0x01)); - - let filter = Some(Filter::new_simple(Column::sum( - filter_cols.iter().map(|(c, _v)| *c), - ))); - - let mut all_combined_cols = COMBINED_OPS.to_vec(); - all_combined_cols.push((columns::OPCODE_COL, 0x01)); - // Create the Arithmetic Table whose columns are those of the - // operations listed in `ops` whose inputs and outputs are given - // by `regs`, where each element of `regs` is a range of columns - // corresponding to a 256-bit input or output register (also `ops` - // is used as the operation filter). - TableWithColumns::new( - *Table::Arithmetic, - cpu_arith_data_link(&all_combined_cols, ®ISTER_MAP), - filter, - ) -} - -/// Structure representing the `Arithmetic` STARK, which carries out all the arithmetic operations. -#[derive(Copy, Clone, Default)] -pub(crate) struct ArithmeticStark { - pub f: PhantomData, -} - -pub(crate) const RANGE_MAX: usize = 1usize << 16; // Range check strict upper bound - -impl ArithmeticStark { - /// Expects input in *column*-major layout - fn generate_range_checks(&self, cols: &mut [Vec]) { - debug_assert!(cols.len() == columns::NUM_ARITH_COLUMNS); - - let n_rows = cols[0].len(); - debug_assert!(cols.iter().all(|col| col.len() == n_rows)); - - for i in 0..RANGE_MAX { - cols[columns::RANGE_COUNTER][i] = F::from_canonical_usize(i); - } - for i in RANGE_MAX..n_rows { - cols[columns::RANGE_COUNTER][i] = F::from_canonical_usize(RANGE_MAX - 1); - } - - // Generate the frequencies column. - for col in SHARED_COLS { - for i in 0..n_rows { - let x = cols[col][i].to_canonical_u64() as usize; - assert!( - x < RANGE_MAX, - "column value {} exceeds the max range value {}", - x, - RANGE_MAX - ); - cols[RC_FREQUENCIES][x] += F::ONE; - } - } - } - - pub(crate) fn generate_trace(&self, operations: Vec) -> Vec> { - // The number of rows reserved is the smallest value that's - // guaranteed to avoid a reallocation: The only ops that use - // two rows are the modular operations and DIV, so the only - // way to reach capacity is when every op is modular or DIV - // (which is obviously unlikely in normal - // circumstances). (Also need at least RANGE_MAX rows to - // accommodate range checks.) - let max_rows = std::cmp::max(2 * operations.len(), RANGE_MAX); - let mut trace_rows = Vec::with_capacity(max_rows); - - for op in operations { - let (row1, maybe_row2) = op.to_rows(); - trace_rows.push(row1); - - if let Some(row2) = maybe_row2 { - trace_rows.push(row2); - } - } - - // Pad the trace with zero rows if it doesn't have enough rows - // to accommodate the range check columns. Also make sure the - // trace length is a power of two. - let padded_len = trace_rows.len().next_power_of_two(); - for _ in trace_rows.len()..std::cmp::max(padded_len, RANGE_MAX) { - trace_rows.push(vec![F::ZERO; columns::NUM_ARITH_COLUMNS]); - } - - let mut trace_cols = transpose(&trace_rows); - self.generate_range_checks(&mut trace_cols); - - trace_cols.into_iter().map(PolynomialValues::new).collect() - } -} - -impl, const D: usize> Stark for ArithmeticStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = - EvmStarkFrame, ExtensionTarget, NUM_ARITH_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let lv: &[P; NUM_ARITH_COLUMNS] = vars.get_local_values().try_into().unwrap(); - let nv: &[P; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); - - // Flags must be boolean. - for flag_idx in op_flags() { - let flag = lv[flag_idx]; - yield_constr.constraint(flag * (flag - P::ONES)); - } - - // Only a single flag must be activated at once. - let all_flags = op_flags().map(|i| lv[i]).sum::

(); - yield_constr.constraint(all_flags * (all_flags - P::ONES)); - - // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. - let opcode_constraint = (P::ONES - lv[columns::IS_RANGE_CHECK]) * lv[columns::OPCODE_COL]; - yield_constr.constraint(opcode_constraint); - - // Check the range column: First value must be 0, last row - // must be 2^16-1, and intermediate rows must increment by 0 - // or 1. - let rc1 = lv[columns::RANGE_COUNTER]; - let rc2 = nv[columns::RANGE_COUNTER]; - yield_constr.constraint_first_row(rc1); - let incr = rc2 - rc1; - yield_constr.constraint_transition(incr * incr - incr); - let range_max = P::Scalar::from_canonical_u64((RANGE_MAX - 1) as u64); - yield_constr.constraint_last_row(rc1 - range_max); - - // Evaluate constraints for the MUL operation. - mul::eval_packed_generic(lv, yield_constr); - // Evaluate constraints for ADD, SUB, LT and GT operations. - addcy::eval_packed_generic(lv, yield_constr); - // Evaluate constraints for DIV and MOD operations. - divmod::eval_packed(lv, nv, yield_constr); - // Evaluate constraints for ADDMOD, SUBMOD, MULMOD and for FP254 modular operations. - modular::eval_packed(lv, nv, yield_constr); - // Evaluate constraints for the BYTE operation. - byte::eval_packed(lv, yield_constr); - // Evaluate constraints for SHL and SHR operations. - shift::eval_packed_generic(lv, nv, yield_constr); - } - - fn eval_ext_circuit( - &self, - builder: &mut CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let lv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = - vars.get_local_values().try_into().unwrap(); - let nv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = - vars.get_next_values().try_into().unwrap(); - - // Flags must be boolean. - for flag_idx in op_flags() { - let flag = lv[flag_idx]; - let constraint = builder.mul_sub_extension(flag, flag, flag); - yield_constr.constraint(builder, constraint); - } - - // Only a single flag must be activated at once. - let all_flags = builder.add_many_extension(op_flags().map(|i| lv[i])); - let constraint = builder.mul_sub_extension(all_flags, all_flags, all_flags); - yield_constr.constraint(builder, constraint); - - // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. - let opcode_constraint = builder.arithmetic_extension( - F::NEG_ONE, - F::ONE, - lv[columns::IS_RANGE_CHECK], - lv[columns::OPCODE_COL], - lv[columns::OPCODE_COL], - ); - yield_constr.constraint(builder, opcode_constraint); - - // Check the range column: First value must be 0, last row - // must be 2^16-1, and intermediate rows must increment by 0 - // or 1. - let rc1 = lv[columns::RANGE_COUNTER]; - let rc2 = nv[columns::RANGE_COUNTER]; - yield_constr.constraint_first_row(builder, rc1); - let incr = builder.sub_extension(rc2, rc1); - let t = builder.mul_sub_extension(incr, incr, incr); - yield_constr.constraint_transition(builder, t); - let range_max = - builder.constant_extension(F::Extension::from_canonical_usize(RANGE_MAX - 1)); - let t = builder.sub_extension(rc1, range_max); - yield_constr.constraint_last_row(builder, t); - - // Evaluate constraints for the MUL operation. - mul::eval_ext_circuit(builder, lv, yield_constr); - // Evaluate constraints for ADD, SUB, LT and GT operations. - addcy::eval_ext_circuit(builder, lv, yield_constr); - // Evaluate constraints for DIV and MOD operations. - divmod::eval_ext_circuit(builder, lv, nv, yield_constr); - // Evaluate constraints for ADDMOD, SUBMOD, MULMOD and for FP254 modular operations. - modular::eval_ext_circuit(builder, lv, nv, yield_constr); - // Evaluate constraints for the BYTE operation. - byte::eval_ext_circuit(builder, lv, yield_constr); - // Evaluate constraints for SHL and SHR operations. - shift::eval_ext_circuit(builder, lv, nv, yield_constr); - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn lookups(&self) -> Vec> { - vec![Lookup { - columns: Column::singles(SHARED_COLS).collect(), - table_column: Column::single(RANGE_COUNTER), - frequencies_column: Column::single(RC_FREQUENCIES), - filter_columns: vec![None; NUM_SHARED_COLS], - }] - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use ethereum_types::U256; - use plonky2::field::types::{Field, PrimeField64}; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use super::{columns, ArithmeticStark}; - use crate::arithmetic::columns::OUTPUT_REGISTER; - use crate::arithmetic::*; - - #[test] - fn degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = ArithmeticStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = ArithmeticStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } - - #[test] - fn basic_trace() { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = ArithmeticStark; - - let stark = S { - f: Default::default(), - }; - - // 123 + 456 == 579 - let add = Operation::binary(BinaryOperator::Add, U256::from(123), U256::from(456)); - // (123 * 456) % 1007 == 703 - let mulmod = Operation::ternary( - TernaryOperator::MulMod, - U256::from(123), - U256::from(456), - U256::from(1007), - ); - // (1234 + 567) % 1007 == 794 - let addmod = Operation::ternary( - TernaryOperator::AddMod, - U256::from(1234), - U256::from(567), - U256::from(1007), - ); - // 123 * 456 == 56088 - let mul = Operation::binary(BinaryOperator::Mul, U256::from(123), U256::from(456)); - // 128 / 13 == 9 - let div = Operation::binary(BinaryOperator::Div, U256::from(128), U256::from(13)); - - // 128 < 13 == 0 - let lt1 = Operation::binary(BinaryOperator::Lt, U256::from(128), U256::from(13)); - // 13 < 128 == 1 - let lt2 = Operation::binary(BinaryOperator::Lt, U256::from(13), U256::from(128)); - // 128 < 128 == 0 - let lt3 = Operation::binary(BinaryOperator::Lt, U256::from(128), U256::from(128)); - - // 128 % 13 == 11 - let modop = Operation::binary(BinaryOperator::Mod, U256::from(128), U256::from(13)); - - // byte(30, 0xABCD) = 0xAB - let byte = Operation::binary(BinaryOperator::Byte, U256::from(30), U256::from(0xABCD)); - - let ops: Vec = vec![add, mulmod, addmod, mul, modop, lt1, lt2, lt3, div, byte]; - - let pols = stark.generate_trace(ops); - - // Trace should always have NUM_ARITH_COLUMNS columns and - // min(RANGE_MAX, operations.len()) rows. In this case there - // are only 6 rows, so we should have RANGE_MAX rows. - assert!( - pols.len() == columns::NUM_ARITH_COLUMNS - && pols.iter().all(|v| v.len() == super::RANGE_MAX) - ); - - // Each operation has a single word answer that we can check - let expected_output = [ - // Row (some ops take two rows), expected - (0, 579), // ADD_OUTPUT - (1, 703), - (3, 794), - (5, 56088), - (6, 11), - (8, 0), - (9, 1), - (10, 0), - (11, 9), - (13, 0xAB), - ]; - - for (row, expected) in expected_output { - // First register should match expected value... - let first = OUTPUT_REGISTER.start; - let out = pols[first].values[row].to_canonical_u64(); - assert_eq!( - out, expected, - "expected column {} on row {} to be {} but it was {}", - first, row, expected, out, - ); - // ...other registers should be zero - let rest = OUTPUT_REGISTER.start + 1..OUTPUT_REGISTER.end; - assert!(pols[rest].iter().all(|v| v.values[row] == F::ZERO)); - } - } - - #[test] - fn big_traces() { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = ArithmeticStark; - - let stark = S { - f: Default::default(), - }; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - - let ops = (0..super::RANGE_MAX) - .map(|_| { - Operation::binary( - BinaryOperator::Mul, - U256::from(rng.gen::<[u8; 32]>()), - U256::from(rng.gen::<[u8; 32]>()), - ) - }) - .collect::>(); - - let pols = stark.generate_trace(ops); - - // Trace should always have NUM_ARITH_COLUMNS columns and - // min(RANGE_MAX, operations.len()) rows. In this case there - // are RANGE_MAX operations with one row each, so RANGE_MAX. - assert!( - pols.len() == columns::NUM_ARITH_COLUMNS - && pols.iter().all(|v| v.len() == super::RANGE_MAX) - ); - - let ops = (0..super::RANGE_MAX) - .map(|_| { - Operation::ternary( - TernaryOperator::MulMod, - U256::from(rng.gen::<[u8; 32]>()), - U256::from(rng.gen::<[u8; 32]>()), - U256::from(rng.gen::<[u8; 32]>()), - ) - }) - .collect::>(); - - let pols = stark.generate_trace(ops); - - // Trace should always have NUM_ARITH_COLUMNS columns and - // min(RANGE_MAX, operations.len()) rows. In this case there - // are RANGE_MAX operations with two rows each, so 2*RANGE_MAX. - assert!( - pols.len() == columns::NUM_ARITH_COLUMNS - && pols.iter().all(|v| v.len() == 2 * super::RANGE_MAX) - ); - } -} diff --git a/evm/src/arithmetic/byte.rs b/evm/src/arithmetic/byte.rs deleted file mode 100644 index 272a78431b..0000000000 --- a/evm/src/arithmetic/byte.rs +++ /dev/null @@ -1,502 +0,0 @@ -//! Support for the EVM BYTE instruction -//! -//! This crate verifies the EVM BYTE instruction, defined as follows: -//! -//! INPUTS: 256-bit values I and X = \sum_{i=0}^31 X_i B^i, -//! where B = 2^8 and 0 <= X_i < B for all i. -//! -//! OUTPUT: X_{31-I} if 0 <= I < 32, otherwise 0. -//! -//! NB: index I=0 corresponds to byte X_31, i.e. the most significant -//! byte. This is exactly the opposite of anyone would expect; who -//! knows what the EVM designers were thinking. Anyway, if anything -//! below seems confusing, first check to ensure you're counting from -//! the wrong end of X, as the spec requires. -//! -//! Wlog consider 0 <= I < 32, so I has five bits b0,...,b4. We are -//! given X as an array of 16-bit limbs; write X := \sum_{i=0}^15 Y_i -//! 2^{16i} where 0 <= Y_i < 2^16. -//! -//! The technique (hat tip to Jacqui for the idea) is to store a tree -//! of limbs of X that are selected according to the bits in I. The -//! main observation is that each bit `bi` halves the number of -//! candidate bytes that we might return: If b4 is 0, then I < 16 and -//! the possible bytes are in the top half of X: Y_8,..,Y_15 -//! (corresponding to bytes X_16,..,X_31), and if b4 is 1 then I >= 16 -//! and the possible bytes are the bottom half of X: Y_0,..,Y_7 -//! (corresponding to bytes X_0,..,X_15). -//! -//! Let Z_0,..,Z_7 be the bytes selected in the first step. Then, in -//! the next step, if b3 is 0, we select Z_4,..,Z_7 and if it's 1 we -//! select Z_0,..,Z_3. Together, b4 and b3 divide the bytes of X into -//! 4 equal-sized chunks of 4 limbs, and the byte we're after will be -//! among the limbs 4 selected limbs. -//! -//! Repeating for b2 and b1, we reduce to a single 16-bit limb -//! L=x+y*256; the desired byte will be x if b0 is 1 and y if b0 -//! is 0. -//! -//! -*- -//! -//! To prove that the bytes x and y are in the range [0, 2^8) (rather -//! than [0, 2^16), which is all the range-checker guarantees) we do -//! the following (hat tip to Jacqui for this trick too): Instead of -//! storing x and y, we store w = 256 * x and y. Then, to verify that -//! x, y < 256 and the last limb L = x + y * 256, we check that -//! L = w / 256 + y * 256. -//! -//! The proof of why verifying that L = w / 256 + y * 256 -//! suffices is as follows: -//! -//! 1. The given L, w and y are range-checked to be less than 2^16. -//! 2. y * 256 ∈ {0, 256, 512, ..., 2^24 - 512, 2^24 - 256} -//! 3. w / 256 = L - y * 256 ∈ {-2^24 + 256, -2^24 + 257, ..., 2^16 - 2, 2^16 - 1} -//! 4. By inspection, for w < 2^16, if w / 256 < 2^16 or -//! w / 256 >= P - 2^24 + 256 (i.e. if w / 256 falls in the range -//! of point 3 above), then w = 256 * m for some 0 <= m < 256. -//! 5. Hence w / 256 ∈ {0, 1, ..., 255} -//! 6. Hence y * 256 = L - w / 256 ∈ {-255, -254, ..., 2^16 - 1} -//! 7. Taking the intersection of ranges in 2. and 6. we see that -//! y * 256 ∈ {0, 256, 512, ..., 2^16 - 256} -//! 8. Hence y ∈ {0, 1, ..., 255} - -use core::ops::Range; - -use ethereum_types::U256; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::{Field, PrimeField64}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use static_assertions::const_assert; - -use crate::arithmetic::columns::*; -use crate::arithmetic::utils::u256_to_array; - -// Give meaningful names to the columns of AUX_INPUT_REGISTER_0 that -// we're using -const BYTE_IDX_DECOMP: Range = AUX_INPUT_REGISTER_0.start..AUX_INPUT_REGISTER_0.start + 6; -const BYTE_IDX_DECOMP_HI: usize = AUX_INPUT_REGISTER_0.start + 5; -const BYTE_LAST_LIMB_LO: usize = AUX_INPUT_REGISTER_0.start + 6; -const BYTE_LAST_LIMB_HI: usize = AUX_INPUT_REGISTER_0.start + 7; -const BYTE_IDX_IS_LARGE: usize = AUX_INPUT_REGISTER_0.start + 8; -const BYTE_IDX_HI_LIMB_SUM_INV_0: usize = AUX_INPUT_REGISTER_0.start + 9; -const BYTE_IDX_HI_LIMB_SUM_INV_1: usize = AUX_INPUT_REGISTER_0.start + 10; -const BYTE_IDX_HI_LIMB_SUM_INV_2: usize = AUX_INPUT_REGISTER_0.start + 11; -const BYTE_IDX_HI_LIMB_SUM_INV_3: usize = AUX_INPUT_REGISTER_0.start + 12; - -/// Decompose `idx` into bits and bobs and store in `idx_decomp`. -/// -/// Specifically, write -/// -/// idx = idx0_lo5 + idx0_hi * 2^5 + \sum_i idx[i] * 2^(16i), -/// -/// where `0 <= idx0_lo5 < 32` and `0 <= idx0_hi < 2^11`. Store the -/// 5 bits of `idx0_lo5` in `idx_decomp[0..5]`; we don't explicitly need -/// the higher 11 bits of the first limb, so we put them in -/// `idx_decomp[5]`. The rest of `idx_decomp` is set to 0. -fn set_idx_decomp(idx_decomp: &mut [F], idx: &U256) { - debug_assert!(idx_decomp.len() == 6); - for i in 0..5 { - idx_decomp[i] = F::from_bool(idx.bit(i)); - } - idx_decomp[5] = F::from_canonical_u16((idx.low_u64() as u16) >> 5); -} - -pub(crate) fn generate(lv: &mut [F], idx: U256, val: U256) { - u256_to_array(&mut lv[INPUT_REGISTER_0], idx); - u256_to_array(&mut lv[INPUT_REGISTER_1], val); - set_idx_decomp(&mut lv[BYTE_IDX_DECOMP], &idx); - - let idx0_hi = lv[BYTE_IDX_DECOMP_HI]; - let hi_limb_sum = lv[INPUT_REGISTER_0][1..] - .iter() - .fold(idx0_hi, |acc, &x| acc + x); - let hi_limb_sum_inv = hi_limb_sum - .try_inverse() - .unwrap_or(F::ONE) - .to_canonical_u64(); - // It's a bit silly that we have to split this value, which - // doesn't need to be range-checked, into 16-bit limbs so that it - // can be range-checked; but the rigidity of the range-checking - // mechanism means we can't optionally switch it off for some - // instructions. - lv[BYTE_IDX_HI_LIMB_SUM_INV_0] = F::from_canonical_u16(hi_limb_sum_inv as u16); - lv[BYTE_IDX_HI_LIMB_SUM_INV_1] = F::from_canonical_u16((hi_limb_sum_inv >> 16) as u16); - lv[BYTE_IDX_HI_LIMB_SUM_INV_2] = F::from_canonical_u16((hi_limb_sum_inv >> 32) as u16); - lv[BYTE_IDX_HI_LIMB_SUM_INV_3] = F::from_canonical_u16((hi_limb_sum_inv >> 48) as u16); - lv[BYTE_IDX_IS_LARGE] = F::from_bool(!hi_limb_sum.is_zero()); - - // Set the tree values according to the low 5 bits of idx, even - // when idx >= 32. - - // Use the bits of idx0 to build a multiplexor that selects - // the correct byte of val. Each level of the tree uses one - // bit to halve the set of possible bytes from the previous - // level. The tree stores limbs rather than bytes though, so - // the last value must be handled specially. - - // Morally, offset at i is 2^i * bit[i], but because of the - // reversed indexing and handling of the last element - // separately, the offset is 2^i * ( ! bit[i + 1]). (The !bit - // corresponds to calculating 31 - bits which is just bitwise NOT.) - - // `lvl_len` is the number of elements of the current level of the - // "tree". Can think of `val_limbs` as level 0, with length = - // N_LIMBS = 16. - const_assert!(N_LIMBS == 16); // Enforce assumption - - // Build the tree of limbs from the low 5 bits of idx: - let mut i = 3; // tree level, from 3 downto 0. - let mut src = INPUT_REGISTER_1.start; // val_limbs start - let mut dest = AUX_INPUT_REGISTER_1.start; // tree start - loop { - let lvl_len = 1 << i; - // pick which half of src becomes the new tree level - let offset = (!idx.bit(i + 1) as usize) * lvl_len; - src += offset; - // copy new tree level to dest - lv.copy_within(src..src + lvl_len, dest); - if i == 0 { - break; - } - // next src is this new tree level - src = dest; - // next dest is after this new tree level - dest += lvl_len; - i -= 1; - } - - // Handle the last bit; i.e. pick a byte of the final limb. - let t = lv[dest].to_canonical_u64(); - let lo = t as u8 as u64; - let hi = t >> 8; - - // Store 256 * lo rather than lo: - lv[BYTE_LAST_LIMB_LO] = F::from_canonical_u64(lo << 8); - lv[BYTE_LAST_LIMB_HI] = F::from_canonical_u64(hi); - - let tree = &mut lv[AUX_INPUT_REGISTER_1]; - let output = if idx.bit(0) { - tree[15] = F::from_canonical_u64(lo); - lo.into() - } else { - tree[15] = F::from_canonical_u64(hi); - hi.into() - }; - - u256_to_array( - &mut lv[OUTPUT_REGISTER], - if idx < 32.into() { - output - } else { - U256::zero() - }, - ); -} - -pub(crate) fn eval_packed( - lv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - let is_byte = lv[IS_BYTE]; - - let idx = &lv[INPUT_REGISTER_0]; - let val = &lv[INPUT_REGISTER_1]; - let out = &lv[OUTPUT_REGISTER]; - let idx_decomp = &lv[AUX_INPUT_REGISTER_0]; - let tree = &lv[AUX_INPUT_REGISTER_1]; - - // low 5 bits of the first limb of idx: - let mut idx0_lo5 = P::ZEROS; - for i in 0..5 { - let bit = idx_decomp[i]; - yield_constr.constraint(is_byte * (bit * bit - bit)); - idx0_lo5 += bit * P::Scalar::from_canonical_u64(1 << i); - } - // Verify that idx0_hi is the high (11) bits of the first limb of - // idx (in particular idx0_hi is at most 11 bits, since idx[0] is - // at most 16 bits). - let idx0_hi = idx_decomp[5] * P::Scalar::from_canonical_u64(32u64); - yield_constr.constraint(is_byte * (idx[0] - (idx0_lo5 + idx0_hi))); - - // Verify the layers of the tree - // NB: Each of the bit values is negated in place to account for - // the reversed indexing. - let bit = idx_decomp[4]; - for i in 0..8 { - let limb = bit * val[i] + (P::ONES - bit) * val[i + 8]; - yield_constr.constraint(is_byte * (tree[i] - limb)); - } - - let bit = idx_decomp[3]; - for i in 0..4 { - let limb = bit * tree[i] + (P::ONES - bit) * tree[i + 4]; - yield_constr.constraint(is_byte * (tree[i + 8] - limb)); - } - - let bit = idx_decomp[2]; - for i in 0..2 { - let limb = bit * tree[i + 8] + (P::ONES - bit) * tree[i + 10]; - yield_constr.constraint(is_byte * (tree[i + 12] - limb)); - } - - let bit = idx_decomp[1]; - let limb = bit * tree[12] + (P::ONES - bit) * tree[13]; - yield_constr.constraint(is_byte * (tree[14] - limb)); - - // Check byte decomposition of last limb: - - let base8 = P::Scalar::from_canonical_u64(1 << 8); - let lo_byte = lv[BYTE_LAST_LIMB_LO]; - let hi_byte = lv[BYTE_LAST_LIMB_HI]; - yield_constr.constraint(is_byte * (lo_byte + base8 * (base8 * hi_byte - limb))); - - let bit = idx_decomp[0]; - let t = bit * lo_byte + (P::ONES - bit) * base8 * hi_byte; - yield_constr.constraint(is_byte * (base8 * tree[15] - t)); - let expected_out_byte = tree[15]; - - // Sum all higher limbs; sum will be non-zero iff idx >= 32. - let hi_limb_sum = lv[BYTE_IDX_DECOMP_HI] + idx[1..].iter().copied().sum::

(); - let idx_is_large = lv[BYTE_IDX_IS_LARGE]; - - // idx_is_large is 0 or 1 - yield_constr.constraint(is_byte * (idx_is_large * idx_is_large - idx_is_large)); - - // If hi_limb_sum is nonzero, then idx_is_large must be one. - yield_constr.constraint(is_byte * hi_limb_sum * (idx_is_large - P::ONES)); - - let hi_limb_sum_inv = lv[BYTE_IDX_HI_LIMB_SUM_INV_0] - + lv[BYTE_IDX_HI_LIMB_SUM_INV_1] * P::Scalar::from_canonical_u64(1 << 16) - + lv[BYTE_IDX_HI_LIMB_SUM_INV_2] * P::Scalar::from_canonical_u64(1 << 32) - + lv[BYTE_IDX_HI_LIMB_SUM_INV_3] * P::Scalar::from_canonical_u64(1 << 48); - - // If idx_is_large is 1, then hi_limb_sum_inv must be the inverse - // of hi_limb_sum, hence hi_limb_sum is non-zero, hence idx is - // indeed "large". - // - // Otherwise, if idx_is_large is 0, then hi_limb_sum * hi_limb_sum_inv - // is zero, which is only possible if hi_limb_sum is zero, since - // hi_limb_sum_inv is non-zero. - yield_constr.constraint(is_byte * (hi_limb_sum * hi_limb_sum_inv - idx_is_large)); - - let out_byte = out[0]; - let check = out_byte - (P::ONES - idx_is_large) * expected_out_byte; - yield_constr.constraint(is_byte * check); - - // Check that the rest of the output limbs are zero - for i in 1..N_LIMBS { - yield_constr.constraint(is_byte * out[i]); - } -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let is_byte = lv[IS_BYTE]; - - let idx = &lv[INPUT_REGISTER_0]; - let val = &lv[INPUT_REGISTER_1]; - let out = &lv[OUTPUT_REGISTER]; - let idx_decomp = &lv[AUX_INPUT_REGISTER_0]; - let tree = &lv[AUX_INPUT_REGISTER_1]; - - // low 5 bits of the first limb of idx: - let mut idx0_lo5 = builder.zero_extension(); - for i in 0..5 { - let bit = idx_decomp[i]; - let t = builder.mul_sub_extension(bit, bit, bit); - let t = builder.mul_extension(t, is_byte); - yield_constr.constraint(builder, t); - let scale = F::Extension::from(F::from_canonical_u64(1 << i)); - let scale = builder.constant_extension(scale); - idx0_lo5 = builder.mul_add_extension(bit, scale, idx0_lo5); - } - // Verify that idx0_hi is the high (11) bits of the first limb of - // idx (in particular idx0_hi is at most 11 bits, since idx[0] is - // at most 16 bits). - let t = F::Extension::from(F::from_canonical_u64(32)); - let t = builder.constant_extension(t); - let t = builder.mul_add_extension(idx_decomp[5], t, idx0_lo5); - let t = builder.sub_extension(idx[0], t); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - - // Verify the layers of the tree - // NB: Each of the bit values is negated in place to account for - // the reversed indexing. - let one = builder.one_extension(); - let bit = idx_decomp[4]; - for i in 0..8 { - let t = builder.mul_extension(bit, val[i]); - let u = builder.sub_extension(one, bit); - let v = builder.mul_add_extension(u, val[i + 8], t); - let t = builder.sub_extension(tree[i], v); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - } - - let bit = idx_decomp[3]; - for i in 0..4 { - let t = builder.mul_extension(bit, tree[i]); - let u = builder.sub_extension(one, bit); - let v = builder.mul_add_extension(u, tree[i + 4], t); - let t = builder.sub_extension(tree[i + 8], v); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - } - - let bit = idx_decomp[2]; - for i in 0..2 { - let t = builder.mul_extension(bit, tree[i + 8]); - let u = builder.sub_extension(one, bit); - let v = builder.mul_add_extension(u, tree[i + 10], t); - let t = builder.sub_extension(tree[i + 12], v); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - } - - let bit = idx_decomp[1]; - let t = builder.mul_extension(bit, tree[12]); - let u = builder.sub_extension(one, bit); - let limb = builder.mul_add_extension(u, tree[13], t); - let t = builder.sub_extension(tree[14], limb); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - - // Check byte decomposition of last limb: - - let base8 = F::Extension::from(F::from_canonical_u64(1 << 8)); - let base8 = builder.constant_extension(base8); - let lo_byte = lv[BYTE_LAST_LIMB_LO]; - let hi_byte = lv[BYTE_LAST_LIMB_HI]; - let t = builder.mul_sub_extension(base8, hi_byte, limb); - let t = builder.mul_add_extension(base8, t, lo_byte); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - - let bit = idx_decomp[0]; - let nbit = builder.sub_extension(one, bit); - let t = builder.mul_many_extension([nbit, base8, hi_byte]); - let t = builder.mul_add_extension(bit, lo_byte, t); - let t = builder.mul_sub_extension(base8, tree[15], t); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - let expected_out_byte = tree[15]; - - // Sum all higher limbs; sum will be non-zero iff idx >= 32. - let mut hi_limb_sum = lv[BYTE_IDX_DECOMP_HI]; - for i in 1..N_LIMBS { - hi_limb_sum = builder.add_extension(hi_limb_sum, idx[i]); - } - // idx_is_large is 0 or 1 - let idx_is_large = lv[BYTE_IDX_IS_LARGE]; - let t = builder.mul_sub_extension(idx_is_large, idx_is_large, idx_is_large); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - - // If hi_limb_sum is nonzero, then idx_is_large must be one. - let t = builder.sub_extension(idx_is_large, one); - let t = builder.mul_many_extension([is_byte, hi_limb_sum, t]); - yield_constr.constraint(builder, t); - - // If idx_is_large is 1, then hi_limb_sum_inv must be the inverse - // of hi_limb_sum, hence hi_limb_sum is non-zero, hence idx is - // indeed "large". - // - // Otherwise, if idx_is_large is 0, then hi_limb_sum * hi_limb_sum_inv - // is zero, which is only possible if hi_limb_sum is zero, since - // hi_limb_sum_inv is non-zero. - let base16 = F::from_canonical_u64(1 << 16); - let hi_limb_sum_inv = builder.mul_const_add_extension( - base16, - lv[BYTE_IDX_HI_LIMB_SUM_INV_3], - lv[BYTE_IDX_HI_LIMB_SUM_INV_2], - ); - let hi_limb_sum_inv = - builder.mul_const_add_extension(base16, hi_limb_sum_inv, lv[BYTE_IDX_HI_LIMB_SUM_INV_1]); - let hi_limb_sum_inv = - builder.mul_const_add_extension(base16, hi_limb_sum_inv, lv[BYTE_IDX_HI_LIMB_SUM_INV_0]); - let t = builder.mul_sub_extension(hi_limb_sum, hi_limb_sum_inv, idx_is_large); - let t = builder.mul_extension(is_byte, t); - yield_constr.constraint(builder, t); - - let out_byte = out[0]; - let t = builder.sub_extension(one, idx_is_large); - let t = builder.mul_extension(t, expected_out_byte); - let check = builder.sub_extension(out_byte, t); - let t = builder.mul_extension(is_byte, check); - yield_constr.constraint(builder, t); - - // Check that the rest of the output limbs are zero - for i in 1..N_LIMBS { - let t = builder.mul_extension(is_byte, out[i]); - yield_constr.constraint(builder, t); - } -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - - type F = GoldilocksField; - - fn verify_output(lv: &[F], expected_byte: u64) { - let out_byte = lv[OUTPUT_REGISTER][0].to_canonical_u64(); - assert!(out_byte == expected_byte); - for j in 1..N_LIMBS { - assert!(lv[OUTPUT_REGISTER][j] == F::ZERO); - } - } - - #[test] - fn generate_eval_consistency() { - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - const N_ITERS: usize = 1000; - - for _ in 0..N_ITERS { - // set entire row to random 16-bit values - let mut lv = - [F::default(); NUM_ARITH_COLUMNS].map(|_| F::from_canonical_u16(rng.gen::())); - - lv[IS_BYTE] = F::ONE; - - let val = U256::from(rng.gen::<[u8; 32]>()); - for i in 0..32 { - let idx = i.into(); - generate(&mut lv, idx, val); - - // Check correctness - let out_byte = val.byte(31 - i) as u64; - verify_output(&lv, out_byte); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - F::ONE, - F::ONE, - F::ONE, - ); - eval_packed(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, F::ZERO); - } - } - // Check that output is zero when the index is big. - let big_indices = [32.into(), 33.into(), val, U256::max_value()]; - for idx in big_indices { - generate(&mut lv, idx, val); - verify_output(&lv, 0); - } - } - } -} diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs deleted file mode 100644 index e4172bc073..0000000000 --- a/evm/src/arithmetic/columns.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! Arithmetic unit - -use core::ops::Range; - -pub(crate) const LIMB_BITS: usize = 16; -const EVM_REGISTER_BITS: usize = 256; - -/// Return the number of LIMB_BITS limbs that are in an EVM -/// register-sized number, panicking if LIMB_BITS doesn't divide in -/// the EVM register size. -const fn n_limbs() -> usize { - if EVM_REGISTER_BITS % LIMB_BITS != 0 { - panic!("limb size must divide EVM register size"); - } - let n = EVM_REGISTER_BITS / LIMB_BITS; - if n % 2 == 1 { - panic!("number of limbs must be even"); - } - n -} - -/// Number of LIMB_BITS limbs that are in on EVM register-sized number. -pub(crate) const N_LIMBS: usize = n_limbs(); - -pub(crate) const IS_ADD: usize = 0; -pub(crate) const IS_MUL: usize = IS_ADD + 1; -pub(crate) const IS_SUB: usize = IS_MUL + 1; -pub(crate) const IS_DIV: usize = IS_SUB + 1; -pub(crate) const IS_MOD: usize = IS_DIV + 1; -pub(crate) const IS_ADDMOD: usize = IS_MOD + 1; -pub(crate) const IS_MULMOD: usize = IS_ADDMOD + 1; -pub(crate) const IS_ADDFP254: usize = IS_MULMOD + 1; -pub(crate) const IS_MULFP254: usize = IS_ADDFP254 + 1; -pub(crate) const IS_SUBFP254: usize = IS_MULFP254 + 1; -pub(crate) const IS_SUBMOD: usize = IS_SUBFP254 + 1; -pub(crate) const IS_LT: usize = IS_SUBMOD + 1; -pub(crate) const IS_GT: usize = IS_LT + 1; -pub(crate) const IS_BYTE: usize = IS_GT + 1; -pub(crate) const IS_SHL: usize = IS_BYTE + 1; -pub(crate) const IS_SHR: usize = IS_SHL + 1; -pub(crate) const IS_RANGE_CHECK: usize = IS_SHR + 1; -/// Column that stores the opcode if the operation is a range check. -pub(crate) const OPCODE_COL: usize = IS_RANGE_CHECK + 1; -pub(crate) const START_SHARED_COLS: usize = OPCODE_COL + 1; - -pub(crate) const fn op_flags() -> Range { - IS_ADD..IS_RANGE_CHECK + 1 -} - -/// Within the Arithmetic Unit, there are shared columns which can be -/// used by any arithmetic circuit, depending on which one is active -/// this cycle. -/// -/// Modular arithmetic takes 11 * N_LIMBS columns which is split across -/// two rows, the first with 6 * N_LIMBS columns and the second with -/// 5 * N_LIMBS columns. (There are hence N_LIMBS "wasted columns" in -/// the second row.) -pub(crate) const NUM_SHARED_COLS: usize = 6 * N_LIMBS; -pub(crate) const SHARED_COLS: Range = START_SHARED_COLS..START_SHARED_COLS + NUM_SHARED_COLS; - -pub(crate) const INPUT_REGISTER_0: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; -pub(crate) const INPUT_REGISTER_1: Range = - INPUT_REGISTER_0.end..INPUT_REGISTER_0.end + N_LIMBS; -pub(crate) const INPUT_REGISTER_2: Range = - INPUT_REGISTER_1.end..INPUT_REGISTER_1.end + N_LIMBS; -pub(crate) const OUTPUT_REGISTER: Range = - INPUT_REGISTER_2.end..INPUT_REGISTER_2.end + N_LIMBS; - -// NB: Only one of AUX_INPUT_REGISTER_[01] or AUX_INPUT_REGISTER_DBL -// will be used for a given operation since they overlap -pub(crate) const AUX_INPUT_REGISTER_0: Range = - OUTPUT_REGISTER.end..OUTPUT_REGISTER.end + N_LIMBS; -pub(crate) const AUX_INPUT_REGISTER_1: Range = - AUX_INPUT_REGISTER_0.end..AUX_INPUT_REGISTER_0.end + N_LIMBS; -pub(crate) const AUX_INPUT_REGISTER_DBL: Range = - OUTPUT_REGISTER.end..OUTPUT_REGISTER.end + 2 * N_LIMBS; - -// The auxiliary input columns overlap the general input columns -// because they correspond to the values in the second row for modular -// operations. -const AUX_REGISTER_0: Range = START_SHARED_COLS..START_SHARED_COLS + N_LIMBS; -const AUX_REGISTER_1: Range = AUX_REGISTER_0.end..AUX_REGISTER_0.end + 2 * N_LIMBS; -const AUX_REGISTER_2: Range = AUX_REGISTER_1.end..AUX_REGISTER_1.end + 2 * N_LIMBS - 1; - -// Each element c of {MUL,MODULAR}_AUX_REGISTER is -2^20 <= c <= 2^20; -// this value is used as an offset so that everything is positive in -// the range checks. -pub(crate) const AUX_COEFF_ABS_MAX: i64 = 1 << 20; - -// MUL takes 5 * N_LIMBS = 80 columns -pub(crate) const MUL_AUX_INPUT_LO: Range = AUX_INPUT_REGISTER_0; -pub(crate) const MUL_AUX_INPUT_HI: Range = AUX_INPUT_REGISTER_1; - -// MULMOD takes 4 * N_LIMBS + 3 * 2*N_LIMBS + N_LIMBS = 176 columns -// but split over two rows of 96 columns and 80 columns. -// -// ADDMOD, SUBMOD, MOD and DIV are currently implemented in terms of -// the general modular code, so they also take 144 columns (also split -// over two rows). -pub(crate) const MODULAR_INPUT_0: Range = INPUT_REGISTER_0; -pub(crate) const MODULAR_INPUT_1: Range = INPUT_REGISTER_1; -pub(crate) const MODULAR_MODULUS: Range = INPUT_REGISTER_2; -pub(crate) const MODULAR_OUTPUT: Range = OUTPUT_REGISTER; -pub(crate) const MODULAR_QUO_INPUT: Range = AUX_INPUT_REGISTER_DBL; -pub(crate) const MODULAR_OUT_AUX_RED: Range = AUX_REGISTER_0; -// NB: Last value is not used in AUX, it is used in MOD_IS_ZERO -pub(crate) const MODULAR_MOD_IS_ZERO: usize = AUX_REGISTER_1.start; -pub(crate) const MODULAR_AUX_INPUT_LO: Range = AUX_REGISTER_1.start + 1..AUX_REGISTER_1.end; -pub(crate) const MODULAR_AUX_INPUT_HI: Range = AUX_REGISTER_2; -// Must be set to MOD_IS_ZERO for DIV and SHR operations i.e. MOD_IS_ZERO * (lv[IS_DIV] + lv[IS_SHR]). -pub(crate) const MODULAR_DIV_DENOM_IS_ZERO: usize = AUX_REGISTER_2.end; - -/// The counter column (used for the range check) starts from 0 and increments. -pub(crate) const RANGE_COUNTER: usize = START_SHARED_COLS + NUM_SHARED_COLS; -/// The frequencies column used in logUp. -pub(crate) const RC_FREQUENCIES: usize = RANGE_COUNTER + 1; - -/// Number of columns in `ArithmeticStark`. -pub(crate) const NUM_ARITH_COLUMNS: usize = START_SHARED_COLS + NUM_SHARED_COLS + 2; diff --git a/evm/src/arithmetic/divmod.rs b/evm/src/arithmetic/divmod.rs deleted file mode 100644 index d27fbc2e35..0000000000 --- a/evm/src/arithmetic/divmod.rs +++ /dev/null @@ -1,378 +0,0 @@ -//! Support for EVM instructions DIV and MOD. -//! -//! The logic for verifying them is detailed in the `modular` submodule. - -use core::ops::Range; - -use ethereum_types::U256; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::PrimeField64; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::arithmetic::columns::*; -use crate::arithmetic::modular::{ - generate_modular_op, modular_constr_poly, modular_constr_poly_ext_circuit, -}; -use crate::arithmetic::utils::*; - -/// Generates the output and auxiliary values for modular operations, -/// assuming the input, modular and output limbs are already set. -pub(crate) fn generate_divmod( - lv: &mut [F], - nv: &mut [F], - filter: usize, - input_limbs_range: Range, - modulus_range: Range, -) { - let input_limbs = read_value_i64_limbs::(lv, input_limbs_range); - let pol_input = pol_extend(input_limbs); - let (out, quo_input) = generate_modular_op(lv, nv, filter, pol_input, modulus_range); - - debug_assert!( - &quo_input[N_LIMBS..].iter().all(|&x| x == F::ZERO), - "expected top half of quo_input to be zero" - ); - - // Initialise whole (double) register to zero; the low half will - // be overwritten via lv[AUX_INPUT_REGISTER] below. - for i in MODULAR_QUO_INPUT { - lv[i] = F::ZERO; - } - - match filter { - IS_DIV | IS_SHR => { - debug_assert!( - lv[OUTPUT_REGISTER] - .iter() - .zip(&quo_input[..N_LIMBS]) - .all(|(x, y)| x == y), - "computed output doesn't match expected" - ); - lv[AUX_INPUT_REGISTER_0].copy_from_slice(&out); - } - IS_MOD => { - debug_assert!( - lv[OUTPUT_REGISTER].iter().zip(&out).all(|(x, y)| x == y), - "computed output doesn't match expected" - ); - lv[AUX_INPUT_REGISTER_0].copy_from_slice(&quo_input[..N_LIMBS]); - } - _ => panic!("expected filter to be IS_DIV, IS_SHR or IS_MOD but it was {filter}"), - }; -} -/// Generate the output and auxiliary values for modular operations. -pub(crate) fn generate( - lv: &mut [F], - nv: &mut [F], - filter: usize, - input0: U256, - input1: U256, - result: U256, -) { - debug_assert!(lv.len() == NUM_ARITH_COLUMNS); - - u256_to_array(&mut lv[INPUT_REGISTER_0], input0); - u256_to_array(&mut lv[INPUT_REGISTER_1], input1); - u256_to_array(&mut lv[OUTPUT_REGISTER], result); - - generate_divmod(lv, nv, filter, INPUT_REGISTER_0, INPUT_REGISTER_1); -} - -/// Verify that num = quo * den + rem and 0 <= rem < den. -pub(crate) fn eval_packed_divmod_helper( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, - filter: P, - num_range: Range, - den_range: Range, - quo_range: Range, - rem_range: Range, -) { - debug_assert!(quo_range.len() == N_LIMBS); - debug_assert!(rem_range.len() == N_LIMBS); - - yield_constr.constraint_last_row(filter); - - let num = &lv[num_range]; - let den = read_value(lv, den_range); - let quo = { - let mut quo = [P::ZEROS; 2 * N_LIMBS]; - quo[..N_LIMBS].copy_from_slice(&lv[quo_range]); - quo - }; - let rem = read_value(lv, rem_range); - - let mut constr_poly = modular_constr_poly(lv, nv, yield_constr, filter, rem, den, quo); - - let input = num; - pol_sub_assign(&mut constr_poly, input); - - for &c in constr_poly.iter() { - yield_constr.constraint_transition(filter * c); - } -} - -pub(crate) fn eval_packed( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_divmod_helper( - lv, - nv, - yield_constr, - lv[IS_DIV], - INPUT_REGISTER_0, - INPUT_REGISTER_1, - OUTPUT_REGISTER, - AUX_INPUT_REGISTER_0, - ); - eval_packed_divmod_helper( - lv, - nv, - yield_constr, - lv[IS_MOD], - INPUT_REGISTER_0, - INPUT_REGISTER_1, - AUX_INPUT_REGISTER_0, - OUTPUT_REGISTER, - ); -} - -pub(crate) fn eval_ext_circuit_divmod_helper, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, - filter: ExtensionTarget, - num_range: Range, - den_range: Range, - quo_range: Range, - rem_range: Range, -) { - yield_constr.constraint_last_row(builder, filter); - - let num = &lv[num_range]; - let den = read_value(lv, den_range); - let quo = { - let zero = builder.zero_extension(); - let mut quo = [zero; 2 * N_LIMBS]; - quo[..N_LIMBS].copy_from_slice(&lv[quo_range]); - quo - }; - let rem = read_value(lv, rem_range); - - let mut constr_poly = - modular_constr_poly_ext_circuit(lv, nv, builder, yield_constr, filter, rem, den, quo); - - let input = num; - pol_sub_assign_ext_circuit(builder, &mut constr_poly, input); - - for &c in constr_poly.iter() { - let t = builder.mul_extension(filter, c); - yield_constr.constraint_transition(builder, t); - } -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - eval_ext_circuit_divmod_helper( - builder, - lv, - nv, - yield_constr, - lv[IS_DIV], - INPUT_REGISTER_0, - INPUT_REGISTER_1, - OUTPUT_REGISTER, - AUX_INPUT_REGISTER_0, - ); - eval_ext_circuit_divmod_helper( - builder, - lv, - nv, - yield_constr, - lv[IS_MOD], - INPUT_REGISTER_0, - INPUT_REGISTER_1, - AUX_INPUT_REGISTER_0, - OUTPUT_REGISTER, - ); -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::{Field, Sample}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::constraint_consumer::ConstraintConsumer; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - - const N_RND_TESTS: usize = 1000; - const MODULAR_OPS: [usize; 2] = [IS_MOD, IS_DIV]; - - // TODO: Should be able to refactor this test to apply to all operations. - #[test] - fn generate_eval_consistency_not_modular() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // if `IS_MOD == 0`, then the constraints should be met even - // if all values are garbage (and similarly for the other operations). - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - // Since SHR uses the logic for DIV, `IS_SHR` should also be set to 0 here. - lv[IS_SHR] = F::ZERO; - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ONE, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - - #[test] - fn generate_eval_consistency() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - - for op_filter in MODULAR_OPS { - for i in 0..N_RND_TESTS { - // set inputs to random values - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - // Since SHR uses the logic for DIV, `IS_SHR` should also be set to 0 here. - lv[IS_SHR] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = { - let mut modulus_limbs = [0u8; 32]; - // For the second half of the tests, set the top - // 16-start digits of the "modulus" to zero so it is - // much smaller than the inputs. - if i > N_RND_TESTS / 2 { - // 1 <= start < N_LIMBS - let start = (rng.gen::() % (modulus_limbs.len() - 1)) + 1; - for mi in modulus_limbs.iter_mut().skip(start) { - *mi = 0u8; - } - } - U256::from(modulus_limbs) - }; - - let result = if input1 == U256::zero() { - U256::zero() - } else if op_filter == IS_DIV { - input0 / input1 - } else { - input0 % input1 - }; - generate(&mut lv, &mut nv, op_filter, input0, input1, result); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - } - } - - #[test] - fn zero_modulus() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - - for op_filter in MODULAR_OPS { - for _i in 0..N_RND_TESTS { - for corrupt_constraints in [false, true] { - // set inputs to random values and the modulus to zero; - // the output is defined to be zero when modulus is zero. - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - // Since SHR uses the logic for DIV, `IS_SHR` should also be set to 0 here. - lv[IS_SHR] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = U256::zero(); - - generate(&mut lv, &mut nv, op_filter, input0, input1, U256::zero()); - - // check that the correct output was generated - assert!(lv[OUTPUT_REGISTER].iter().all(|&c| c == F::ZERO)); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - - if corrupt_constraints { - // Corrupt one output limb by setting it to a non-zero value. - let random_oi = OUTPUT_REGISTER.start + rng.gen::() % N_LIMBS; - lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); - - eval_packed(&lv, &nv, &mut constraint_consumer); - - // Check that at least one of the constraints was non-zero. - assert!(constraint_consumer - .accumulators() - .iter() - .any(|&acc| acc != F::ZERO)); - } else { - assert!(constraint_consumer - .accumulators() - .iter() - .all(|&acc| acc == F::ZERO)); - } - } - } - } - } -} diff --git a/evm/src/arithmetic/mod.rs b/evm/src/arithmetic/mod.rs deleted file mode 100644 index f9a816c1f8..0000000000 --- a/evm/src/arithmetic/mod.rs +++ /dev/null @@ -1,350 +0,0 @@ -use ethereum_types::U256; -use plonky2::field::types::PrimeField64; - -use self::columns::{ - INPUT_REGISTER_0, INPUT_REGISTER_1, INPUT_REGISTER_2, OPCODE_COL, OUTPUT_REGISTER, -}; -use self::utils::u256_to_array; -use crate::arithmetic::columns::IS_RANGE_CHECK; -use crate::extension_tower::BN_BASE; -use crate::util::{addmod, mulmod, submod}; - -mod addcy; -mod byte; -mod divmod; -mod modular; -mod mul; -mod shift; -mod utils; - -pub mod arithmetic_stark; -pub(crate) mod columns; - -/// An enum representing different binary operations. -/// -/// `Shl` and `Shr` are handled differently, by leveraging `Mul` and `Div` respectively. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum BinaryOperator { - Add, - Mul, - Sub, - Div, - Mod, - Lt, - Gt, - AddFp254, - MulFp254, - SubFp254, - Byte, - Shl, // simulated with MUL - Shr, // simulated with DIV -} - -impl BinaryOperator { - /// Computes the result of a binary arithmetic operation given two inputs. - pub(crate) fn result(&self, input0: U256, input1: U256) -> U256 { - match self { - BinaryOperator::Add => input0.overflowing_add(input1).0, - BinaryOperator::Mul => input0.overflowing_mul(input1).0, - BinaryOperator::Shl => { - if input0 < U256::from(256usize) { - input1 << input0 - } else { - U256::zero() - } - } - BinaryOperator::Sub => input0.overflowing_sub(input1).0, - BinaryOperator::Div => { - if input1.is_zero() { - U256::zero() - } else { - input0 / input1 - } - } - BinaryOperator::Shr => { - if input0 < U256::from(256usize) { - input1 >> input0 - } else { - U256::zero() - } - } - BinaryOperator::Mod => { - if input1.is_zero() { - U256::zero() - } else { - input0 % input1 - } - } - BinaryOperator::Lt => U256::from((input0 < input1) as u8), - BinaryOperator::Gt => U256::from((input0 > input1) as u8), - BinaryOperator::AddFp254 => addmod(input0, input1, BN_BASE), - BinaryOperator::MulFp254 => mulmod(input0, input1, BN_BASE), - BinaryOperator::SubFp254 => submod(input0, input1, BN_BASE), - BinaryOperator::Byte => { - if input0 >= 32.into() { - U256::zero() - } else { - input1.byte(31 - input0.as_usize()).into() - } - } - } - } - - /// Maps a binary arithmetic operation to its associated flag column in the trace. - pub(crate) const fn row_filter(&self) -> usize { - match self { - BinaryOperator::Add => columns::IS_ADD, - BinaryOperator::Mul => columns::IS_MUL, - BinaryOperator::Sub => columns::IS_SUB, - BinaryOperator::Div => columns::IS_DIV, - BinaryOperator::Mod => columns::IS_MOD, - BinaryOperator::Lt => columns::IS_LT, - BinaryOperator::Gt => columns::IS_GT, - BinaryOperator::AddFp254 => columns::IS_ADDFP254, - BinaryOperator::MulFp254 => columns::IS_MULFP254, - BinaryOperator::SubFp254 => columns::IS_SUBFP254, - BinaryOperator::Byte => columns::IS_BYTE, - BinaryOperator::Shl => columns::IS_SHL, - BinaryOperator::Shr => columns::IS_SHR, - } - } -} - -/// An enum representing different ternary operations. -#[allow(clippy::enum_variant_names)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum TernaryOperator { - AddMod, - MulMod, - SubMod, -} - -impl TernaryOperator { - /// Computes the result of a ternary arithmetic operation given three inputs. - pub(crate) fn result(&self, input0: U256, input1: U256, input2: U256) -> U256 { - match self { - TernaryOperator::AddMod => addmod(input0, input1, input2), - TernaryOperator::MulMod => mulmod(input0, input1, input2), - TernaryOperator::SubMod => submod(input0, input1, input2), - } - } - - /// Maps a ternary arithmetic operation to its associated flag column in the trace. - pub(crate) const fn row_filter(&self) -> usize { - match self { - TernaryOperator::AddMod => columns::IS_ADDMOD, - TernaryOperator::MulMod => columns::IS_MULMOD, - TernaryOperator::SubMod => columns::IS_SUBMOD, - } - } -} - -/// An enum representing arithmetic operations that can be either binary or ternary. -#[allow(clippy::enum_variant_names)] -#[derive(Debug)] -pub(crate) enum Operation { - BinaryOperation { - operator: BinaryOperator, - input0: U256, - input1: U256, - result: U256, - }, - TernaryOperation { - operator: TernaryOperator, - input0: U256, - input1: U256, - input2: U256, - result: U256, - }, - RangeCheckOperation { - input0: U256, - input1: U256, - input2: U256, - opcode: U256, - result: U256, - }, -} - -impl Operation { - /// Creates a binary operator with given inputs. - /// - /// NB: This works as you would expect, EXCEPT for SHL and SHR, - /// whose inputs need a small amount of preprocessing. Specifically, - /// to create `SHL(shift, value)`, call (note the reversal of - /// argument order): - /// - /// `Operation::binary(BinaryOperator::Shl, value, 1 << shift)` - /// - /// Similarly, to create `SHR(shift, value)`, call - /// - /// `Operation::binary(BinaryOperator::Shr, value, 1 << shift)` - /// - /// See witness/operation.rs::append_shift() for an example (indeed - /// the only call site for such inputs). - pub(crate) fn binary(operator: BinaryOperator, input0: U256, input1: U256) -> Self { - let result = operator.result(input0, input1); - Self::BinaryOperation { - operator, - input0, - input1, - result, - } - } - - /// Creates a ternary operator with given inputs. - pub(crate) fn ternary( - operator: TernaryOperator, - input0: U256, - input1: U256, - input2: U256, - ) -> Self { - let result = operator.result(input0, input1, input2); - Self::TernaryOperation { - operator, - input0, - input1, - input2, - result, - } - } - - pub(crate) const fn range_check( - input0: U256, - input1: U256, - input2: U256, - opcode: U256, - result: U256, - ) -> Self { - Self::RangeCheckOperation { - input0, - input1, - input2, - opcode, - result, - } - } - - /// Gets the result of an arithmetic operation. - pub(crate) fn result(&self) -> U256 { - match self { - Operation::BinaryOperation { result, .. } => *result, - Operation::TernaryOperation { result, .. } => *result, - _ => panic!("This function should not be called for range checks."), - } - } - - /// Convert operation into one or two rows of the trace. - /// - /// Morally these types should be [F; NUM_ARITH_COLUMNS], but we - /// use vectors because that's what utils::transpose (who consumes - /// the result of this function as part of the range check code) - /// expects. - /// - /// The `is_simulated` bool indicates whether we use a native arithmetic - /// operation or simulate one with another. This is used to distinguish - /// SHL and SHR operations that are simulated through MUL and DIV respectively. - fn to_rows(&self) -> (Vec, Option>) { - match *self { - Operation::BinaryOperation { - operator, - input0, - input1, - result, - } => binary_op_to_rows(operator, input0, input1, result), - Operation::TernaryOperation { - operator, - input0, - input1, - input2, - result, - } => ternary_op_to_rows(operator.row_filter(), input0, input1, input2, result), - Operation::RangeCheckOperation { - input0, - input1, - input2, - opcode, - result, - } => range_check_to_rows(input0, input1, input2, opcode, result), - } - } -} - -/// Converts a ternary arithmetic operation to one or two rows of the `ArithmeticStark` table. -fn ternary_op_to_rows( - row_filter: usize, - input0: U256, - input1: U256, - input2: U256, - _result: U256, -) -> (Vec, Option>) { - let mut row1 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - let mut row2 = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - - row1[row_filter] = F::ONE; - - modular::generate(&mut row1, &mut row2, row_filter, input0, input1, input2); - - (row1, Some(row2)) -} - -/// Converts a binary arithmetic operation to one or two rows of the `ArithmeticStark` table. -fn binary_op_to_rows( - op: BinaryOperator, - input0: U256, - input1: U256, - result: U256, -) -> (Vec, Option>) { - let mut row = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - row[op.row_filter()] = F::ONE; - - match op { - BinaryOperator::Add | BinaryOperator::Sub | BinaryOperator::Lt | BinaryOperator::Gt => { - addcy::generate(&mut row, op.row_filter(), input0, input1); - (row, None) - } - BinaryOperator::Mul => { - mul::generate(&mut row, input0, input1); - (row, None) - } - BinaryOperator::Shl => { - let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - shift::generate(&mut row, &mut nv, true, input0, input1, result); - (row, None) - } - BinaryOperator::Div | BinaryOperator::Mod => { - let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - divmod::generate(&mut row, &mut nv, op.row_filter(), input0, input1, result); - (row, Some(nv)) - } - BinaryOperator::Shr => { - let mut nv = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - shift::generate(&mut row, &mut nv, false, input0, input1, result); - (row, Some(nv)) - } - BinaryOperator::AddFp254 | BinaryOperator::MulFp254 | BinaryOperator::SubFp254 => { - ternary_op_to_rows::(op.row_filter(), input0, input1, BN_BASE, result) - } - BinaryOperator::Byte => { - byte::generate(&mut row, input0, input1); - (row, None) - } - } -} - -fn range_check_to_rows( - input0: U256, - input1: U256, - input2: U256, - opcode: U256, - result: U256, -) -> (Vec, Option>) { - let mut row = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; - row[IS_RANGE_CHECK] = F::ONE; - row[OPCODE_COL] = F::from_canonical_u64(opcode.as_u64()); - u256_to_array(&mut row[INPUT_REGISTER_0], input0); - u256_to_array(&mut row[INPUT_REGISTER_1], input1); - u256_to_array(&mut row[INPUT_REGISTER_2], input2); - u256_to_array(&mut row[OUTPUT_REGISTER], result); - - (row, None) -} diff --git a/evm/src/arithmetic/modular.rs b/evm/src/arithmetic/modular.rs deleted file mode 100644 index a3806862ad..0000000000 --- a/evm/src/arithmetic/modular.rs +++ /dev/null @@ -1,1004 +0,0 @@ -//! Support for the EVM modular instructions ADDMOD, SUBMOD, MULMOD and MOD, -//! as well as DIV and FP254 related modular instructions. -//! -//! This crate verifies an EVM modular instruction, which takes three -//! 256-bit inputs A, B and M, and produces a 256-bit output C satisfying -//! -//! C = operation(A, B) (mod M). -//! -//! where operation can be addition, multiplication, or just return -//! the first argument (for MOD). Inputs A, B and M, and output C, -//! are given as arrays of 16-bit limbs. For example, if the limbs of -//! A are a[0]...a[15], then -//! -//! A = \sum_{i=0}^15 a[i] β^i, -//! -//! where β = 2^16 = 2^LIMB_BITS. To verify that A, B, M and C satisfy -//! the equation we proceed as follows. Define -//! -//! a(x) = \sum_{i=0}^15 a[i] x^i -//! -//! (so A = a(β)) and similarly for b(x), m(x) and c(x). Then -//! operation(A,B) = C (mod M) if and only if there exists q such that -//! the polynomial -//! -//! operation(a(x), b(x)) - c(x) - m(x) * q(x) -//! -//! is zero when evaluated at x = β, i.e. it is divisible by (x - β); -//! equivalently, there exists a polynomial s such that -//! -//! operation(a(x), b(x)) - c(x) - m(x) * q(x) - (x - β) * s(x) == 0 -//! -//! if and only if operation(A,B) = C (mod M). In the code below, this -//! "constraint polynomial" is constructed in the variable -//! `constr_poly`. It must be identically zero for the modular -//! operation to be verified, or, equivalently, each of its -//! coefficients must be zero. The variable names of the constituent -//! polynomials are (writing N for N_LIMBS=16): -//! -//! a(x) = \sum_{i=0}^{N-1} input0[i] * x^i -//! b(x) = \sum_{i=0}^{N-1} input1[i] * x^i -//! c(x) = \sum_{i=0}^{N-1} output[i] * x^i -//! m(x) = \sum_{i=0}^{N-1} modulus[i] * x^i -//! q(x) = \sum_{i=0}^{2N-1} quot[i] * x^i -//! s(x) = \sum_i^{2N-2} aux[i] * x^i -//! -//! Because A, B, M and C are 256-bit numbers, the degrees of a, b, m -//! and c are (at most) N-1 = 15. If m = 1, then Q would be A*B which -//! can be up to 2^512 - ε, so deg(q) can be up to 2*N-1 = 31. Note -//! that, although for arbitrary m and q we might have deg(m*q) = 3*N-2, -//! because the magnitude of M*Q must match that of operation(A,B), we -//! always have deg(m*q) <= 2*N-1. Finally, in order for all the degrees -//! to match, we have deg(s) <= 2*N-2 = 30. -//! -//! -*- -//! -//! To verify that the output is reduced, that is, output < modulus, -//! the prover supplies the value `out_aux_red` which must satisfy -//! -//! output - modulus = out_aux_red + 2^256 -//! -//! and these values are passed to the "less than" operation. -//! -//! -*- -//! -//! The EVM defines division by zero as zero. We handle this as -//! follows: -//! -//! The prover supplies a binary value `mod_is_zero` which is one if -//! the modulus is zero and zero otherwise. This is verified, then -//! added to the modulus (this can't overflow, as modulus[0] was -//! range-checked and mod_is_zero is 0 or 1). The rest of the -//! calculation proceeds as if modulus was actually 1; this correctly -//! verifies that the output is zero, as required by the standard. -//! To summarise: -//! -//! - mod_is_zero is 0 or 1 -//! - if mod_is_zero is 1, then -//! - given modulus is 0 -//! - updated modulus is 1, which forces the correct output of 0 -//! - if mod_is_zero is 0, then -//! - given modulus can be 0 or non-zero -//! - updated modulus is same as given -//! - if modulus is non-zero, correct output is obtained -//! - if modulus is 0, then the test output < modulus, checking that -//! the output is reduced, will fail, because output is non-negative. -//! -//! In the case of DIV, we do something similar, except that we "replace" -//! the modulus with "2^256" to force the quotient to be zero. -//! -//! -*- -//! -//! NB: The implementation uses 9 * N_LIMBS = 144 columns because of -//! the requirements of the general purpose MULMOD; since ADDMOD, -//! SUBMOD, MOD and DIV are currently implemented in terms of the -//! general modular code, they also take 144 columns. Possible -//! improvements: -//! -//! - We could reduce the number of columns to 112 for ADDMOD, SUBMOD, -//! etc. if they were implemented separately, so they don't pay the -//! full cost of the general MULMOD. -//! -//! - All these operations could have alternative forms where the -//! output was not guaranteed to be reduced, which is often sufficient -//! in practice, and which would save a further 16 columns. -//! -//! - If the modulus is known in advance (such as for elliptic curve -//! arithmetic), specialised handling of MULMOD in that case would -//! only require 96 columns, or 80 if the output doesn't need to be -//! reduced. - -use core::ops::Range; - -use ethereum_types::U256; -use num::bigint::Sign; -use num::{BigInt, One, Zero}; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::{Field, PrimeField64}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use static_assertions::const_assert; - -use super::columns; -use crate::arithmetic::addcy::{eval_ext_circuit_addcy, eval_packed_generic_addcy}; -use crate::arithmetic::columns::*; -use crate::arithmetic::utils::*; -use crate::extension_tower::BN_BASE; - -const fn bn254_modulus_limbs() -> [u16; N_LIMBS] { - const_assert!(N_LIMBS == 16); // Assumed below - let mut limbs = [0u16; N_LIMBS]; - let mut i = 0; - while i < N_LIMBS / 4 { - let x = BN_BASE.0[i]; - limbs[4 * i] = x as u16; - limbs[4 * i + 1] = (x >> 16) as u16; - limbs[4 * i + 2] = (x >> 32) as u16; - limbs[4 * i + 3] = (x >> 48) as u16; - i += 1; - } - limbs -} - -/// Convert the base-2^16 representation of a number into a BigInt. -/// -/// Given `N` signed (16 + ε)-bit values in `limbs`, return the BigInt -/// -/// \sum_{i=0}^{N-1} limbs[i] * β^i. -/// -/// This is basically "evaluate the given polynomial at β". Although -/// the input type is i64, the values must always be in (-2^16 - ε, -/// 2^16 + ε) because of the caller's range check on the inputs (the ε -/// allows us to convert calculated output, which can be bigger than -/// 2^16). -fn columns_to_bigint(limbs: &[i64; N]) -> BigInt { - const BASE: i64 = 1i64 << LIMB_BITS; - - let mut pos_limbs_u32 = Vec::with_capacity(N / 2 + 1); - let mut neg_limbs_u32 = Vec::with_capacity(N / 2 + 1); - let mut cy = 0i64; // cy is necessary to handle ε > 0 - for i in 0..(N / 2) { - let t = cy + limbs[2 * i] + BASE * limbs[2 * i + 1]; - pos_limbs_u32.push(if t > 0 { t as u32 } else { 0u32 }); - neg_limbs_u32.push(if t < 0 { -t as u32 } else { 0u32 }); - cy = t / (1i64 << 32); - } - if N & 1 != 0 { - // If N is odd we need to add the last limb on its own - let t = cy + limbs[N - 1]; - pos_limbs_u32.push(if t > 0 { t as u32 } else { 0u32 }); - neg_limbs_u32.push(if t < 0 { -t as u32 } else { 0u32 }); - cy = t / (1i64 << 32); - } - pos_limbs_u32.push(if cy > 0 { cy as u32 } else { 0u32 }); - neg_limbs_u32.push(if cy < 0 { -cy as u32 } else { 0u32 }); - - let pos = BigInt::from_slice(Sign::Plus, &pos_limbs_u32); - let neg = BigInt::from_slice(Sign::Plus, &neg_limbs_u32); - pos - neg -} - -/// Convert a BigInt into a base-2^16 representation. -/// -/// Given a BigInt `num`, return an array of `N` signed 16-bit -/// values, say `limbs`, such that -/// -/// num = \sum_{i=0}^{N-1} limbs[i] * β^i. -/// -/// Note that `N` must be at least ceil(log2(num)/16) in order to be -/// big enough to hold `num`. -fn bigint_to_columns(num: &BigInt) -> [i64; N] { - assert!(num.bits() <= 16 * N as u64); - let mut output = [0i64; N]; - for (i, limb) in num.iter_u32_digits().enumerate() { - output[2 * i] = limb as u16 as i64; - output[2 * i + 1] = (limb >> LIMB_BITS) as i64; - } - if num.sign() == Sign::Minus { - for c in output.iter_mut() { - *c = -*c; - } - } - output -} - -/// Generate the output and auxiliary values for given `operation`. -/// -/// NB: `operation` can set the higher order elements in its result to -/// zero if they are not used. -pub(crate) fn generate_modular_op( - lv: &[F], - nv: &mut [F], - filter: usize, - pol_input: [i64; 2 * N_LIMBS - 1], - modulus_range: Range, -) -> ([F; N_LIMBS], [F; 2 * N_LIMBS]) { - assert!(modulus_range.len() == N_LIMBS); - let mut modulus_limbs = read_value_i64_limbs(lv, modulus_range); - - // BigInts are just used to avoid having to implement modular - // reduction. - let mut modulus = columns_to_bigint(&modulus_limbs); - - // constr_poly is initialised to the input calculation as - // polynomials, and is used as such for the BigInt reduction; - // later, other values are added/subtracted, which is where its - // meaning as the "constraint polynomial" comes in. - let mut constr_poly = [0i64; 2 * N_LIMBS]; - constr_poly[..2 * N_LIMBS - 1].copy_from_slice(&pol_input); - - // two_exp_256 == 2^256 - let two_exp_256 = { - let mut t = BigInt::zero(); - t.set_bit(256, true); - t - }; - - let mut mod_is_zero = F::ZERO; - if modulus.is_zero() { - if filter == columns::IS_DIV || filter == columns::IS_SHR { - // set modulus = 2^256; the condition above means we know - // it's zero at this point, so we can just set bit 256. - modulus.set_bit(256, true); - // modulus_limbs don't play a role below - } else { - // set modulus = 1 - modulus = BigInt::one(); - modulus_limbs[0] = 1i64; - } - mod_is_zero = F::ONE; - } - - let input = columns_to_bigint(&constr_poly); - - // modulus != 0 here, because, if the given modulus was zero, then - // it was set to 1 or 2^256 above - let mut output = &input % &modulus; - // output will be -ve (but > -modulus) if input was -ve, so we can - // add modulus to obtain a "canonical" +ve output. - if output.sign() == Sign::Minus { - output += &modulus; - } - let output_limbs = bigint_to_columns::(&output); - // exact division; can be -ve for SUB* operations. - let quot = (&input - &output) / &modulus; - if quot.sign() == Sign::Minus { - debug_assert!(filter == IS_SUBMOD || filter == IS_SUBFP254); - } - let mut quot_limbs = bigint_to_columns::<{ 2 * N_LIMBS }>("); - - // output < modulus here; the proof requires (output - modulus) % 2^256: - let out_aux_red = bigint_to_columns::(&(two_exp_256 - modulus + output)); - - // constr_poly is the array of coefficients of the polynomial - // - // operation(a(x), b(x)) - c(x) - s(x)*m(x). - // - pol_sub_assign(&mut constr_poly, &output_limbs); - let prod = pol_mul_wide2(quot_limbs, modulus_limbs); - pol_sub_assign(&mut constr_poly, &prod[0..2 * N_LIMBS]); - - // Higher order terms of the product must be zero for valid quot and modulus: - debug_assert!(&prod[2 * N_LIMBS..].iter().all(|&x| x == 0i64)); - - // constr_poly must be zero when evaluated at x = β := - // 2^LIMB_BITS, hence it's divisible by (x - β). `aux_limbs` is - // the result of removing that root. - let mut aux_limbs = pol_remove_root_2exp::(constr_poly); - - for c in aux_limbs.iter_mut() { - // we store the unsigned offset value c + 2^20. - *c += AUX_COEFF_ABS_MAX; - } - debug_assert!(aux_limbs.iter().all(|&c| c.abs() <= 2 * AUX_COEFF_ABS_MAX)); - - for (i, &c) in MODULAR_AUX_INPUT_LO.zip(&aux_limbs[..2 * N_LIMBS - 1]) { - nv[i] = F::from_canonical_u16(c as u16); - } - for (i, &c) in MODULAR_AUX_INPUT_HI.zip(&aux_limbs[..2 * N_LIMBS - 1]) { - nv[i] = F::from_canonical_u16((c >> 16) as u16); - } - - // quo_input can be negative for SUB* operations, so we offset it - // to ensure it's positive. - if [columns::IS_SUBMOD, columns::IS_SUBFP254].contains(&filter) { - let (lo, hi) = quot_limbs.split_at_mut(N_LIMBS); - - // Verify that the elements are in the expected range. - debug_assert!(lo.iter().all(|&c| c <= u16::max_value() as i64)); - - // Top half of quot_limbs should be zero. - debug_assert!(hi.iter().all(|&d| d.is_zero())); - - if quot.sign() == Sign::Minus { - // quot is negative, so each c should be negative, i.e. in - // the range [-(2^16 - 1), 0]; so we add 2^16 - 1 to c so - // it's in the range [0, 2^16 - 1] which will correctly - // range-check. - for c in lo { - *c += u16::max_value() as i64; - } - // Store the sign of the quotient after the quotient. - hi[0] = 1; - } else { - hi[0] = 0; - }; - } - - nv[MODULAR_MOD_IS_ZERO] = mod_is_zero; - nv[MODULAR_OUT_AUX_RED].copy_from_slice(&out_aux_red.map(F::from_canonical_i64)); - nv[MODULAR_DIV_DENOM_IS_ZERO] = mod_is_zero * (lv[IS_DIV] + lv[IS_SHR]); - - ( - output_limbs.map(F::from_canonical_i64), - quot_limbs.map(F::from_noncanonical_i64), - ) -} - -/// Generate the output and auxiliary values for modular operations. -/// -/// `filter` must be one of `columns::IS_{ADD,MUL,SUB}{MOD,FP254}`. -pub(crate) fn generate( - lv: &mut [F], - nv: &mut [F], - filter: usize, - input0: U256, - input1: U256, - modulus: U256, -) { - debug_assert!(lv.len() == NUM_ARITH_COLUMNS && nv.len() == NUM_ARITH_COLUMNS); - - u256_to_array(&mut lv[MODULAR_INPUT_0], input0); - u256_to_array(&mut lv[MODULAR_INPUT_1], input1); - u256_to_array(&mut lv[MODULAR_MODULUS], modulus); - - if [ - columns::IS_ADDFP254, - columns::IS_SUBFP254, - columns::IS_MULFP254, - ] - .contains(&filter) - { - debug_assert!(modulus == BN_BASE); - } - - // Inputs are all in [0, 2^16), so the "as i64" conversion is safe. - let input0_limbs = read_value_i64_limbs(lv, MODULAR_INPUT_0); - let input1_limbs = read_value_i64_limbs(lv, MODULAR_INPUT_1); - - let pol_input = match filter { - columns::IS_ADDMOD | columns::IS_ADDFP254 => pol_add(input0_limbs, input1_limbs), - columns::IS_SUBMOD | columns::IS_SUBFP254 => pol_sub(input0_limbs, input1_limbs), - columns::IS_MULMOD | columns::IS_MULFP254 => pol_mul_wide(input0_limbs, input1_limbs), - _ => panic!("generate modular operation called with unknown opcode"), - }; - let (out, quo_input) = generate_modular_op(lv, nv, filter, pol_input, MODULAR_MODULUS); - lv[MODULAR_OUTPUT].copy_from_slice(&out); - lv[MODULAR_QUO_INPUT].copy_from_slice(&quo_input); -} - -pub(crate) fn check_reduced( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, - filter: P, - output: [P; N_LIMBS], - modulus: [P; N_LIMBS], - mod_is_zero: P, -) { - // Verify that the output is reduced, i.e. output < modulus. - let out_aux_red = &nv[MODULAR_OUT_AUX_RED]; - // This sets is_less_than to 1 unless we get mod_is_zero when - // doing a DIV or SHR; in that case, we need is_less_than=0, since - // eval_packed_generic_addcy checks - // - // modulus + out_aux_red == output + is_less_than*2^256 - // - // and we are given output = out_aux_red when modulus is zero. - let mut is_less_than = [P::ZEROS; N_LIMBS]; - is_less_than[0] = P::ONES - mod_is_zero * (lv[IS_DIV] + lv[IS_SHR]); - // NB: output and modulus in lv while out_aux_red and - // is_less_than (via mod_is_zero) depend on nv, hence the - // 'is_two_row_op' argument is set to 'true'. - eval_packed_generic_addcy( - yield_constr, - filter, - &modulus, - out_aux_red, - &output, - &is_less_than, - true, - ); -} - -/// Build the part of the constraint polynomial that applies to the -/// DIV, MOD, ADDMOD, MULMOD operations (and the FP254 variants), and -/// perform the common verifications. -/// -/// Specifically, with the notation above, build the polynomial -/// -/// c(x) + q(x) * m(x) + (x - β) * s(x) -/// -/// and check consistency when m = 0, and that c is reduced. Note that -/// q(x) CANNOT be negative here, but, in contrast to -/// addsubmod_constr_poly above, it is twice as long. -pub(crate) fn modular_constr_poly( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, - filter: P, - mut output: [P; N_LIMBS], - mut modulus: [P; N_LIMBS], - quot: [P; 2 * N_LIMBS], -) -> [P; 2 * N_LIMBS] { - let mod_is_zero = nv[MODULAR_MOD_IS_ZERO]; - - // Check that mod_is_zero is zero or one - yield_constr.constraint_transition(filter * (mod_is_zero * mod_is_zero - mod_is_zero)); - - // Check that mod_is_zero is zero if modulus is not zero (they - // could both be zero) - let limb_sum = modulus.into_iter().sum::

(); - yield_constr.constraint_transition(filter * limb_sum * mod_is_zero); - - // See the file documentation for why this suffices to handle - // modulus = 0. - modulus[0] += mod_is_zero; - - // Is 1 iff the operation is DIV or SHR and the denominator is zero. - let div_denom_is_zero = nv[MODULAR_DIV_DENOM_IS_ZERO]; - yield_constr.constraint_transition( - filter * (mod_is_zero * (lv[IS_DIV] + lv[IS_SHR]) - div_denom_is_zero), - ); - - // Needed to compensate for adding mod_is_zero to modulus above, - // since the call eval_packed_generic_addcy() below subtracts modulus - // to verify in the case of a DIV or SHR. - output[0] += div_denom_is_zero; - - check_reduced(lv, nv, yield_constr, filter, output, modulus, mod_is_zero); - - // restore output[0] - output[0] -= div_denom_is_zero; - - // prod = q(x) * m(x) - let prod = pol_mul_wide2(quot, modulus); - // higher order terms must be zero - for &x in prod[2 * N_LIMBS..].iter() { - yield_constr.constraint_transition(filter * x); - } - - // constr_poly = c(x) + q(x) * m(x) - let mut constr_poly: [_; 2 * N_LIMBS] = prod[0..2 * N_LIMBS].try_into().unwrap(); - pol_add_assign(&mut constr_poly, &output); - - let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); - let offset = P::Scalar::from_canonical_u64(AUX_COEFF_ABS_MAX as u64); - - // constr_poly = c(x) + q(x) * m(x) + (x - β) * s(x)c - let mut aux = [P::ZEROS; 2 * N_LIMBS]; - for (c, i) in aux.iter_mut().zip(MODULAR_AUX_INPUT_LO) { - // MODULAR_AUX_INPUT elements were offset by 2^20 in - // generation, so we undo that here. - *c = nv[i] - offset; - } - // add high 16-bits of aux input - for (c, j) in aux.iter_mut().zip(MODULAR_AUX_INPUT_HI) { - *c += base * nv[j]; - } - - pol_add_assign(&mut constr_poly, &pol_adjoin_root(aux, base)); - - constr_poly -} - -/// Build the part of the constraint polynomial that's common to the -/// SUBMOD and SUBFP254 operations, and perform the common -/// verifications. -/// -/// Specifically, with the notation above, build the polynomial -/// -/// c(x) + q(x) * m(x) + (x - β) * s(x) -/// -/// and check consistency when m = 0, and that c is reduced. Note that -/// q(x) can be negative here, so it needs to be reconstructed from -/// its hi and lo halves in MODULAR_QUO_INPUT and then to be -/// "de-biassed" from the range [0, 2^32) to the correct range -/// (-2^16,2^16). -pub(crate) fn submod_constr_poly( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, - filter: P, - output: [P; N_LIMBS], - modulus: [P; N_LIMBS], - mut quot: [P; 2 * N_LIMBS], -) -> [P; 2 * N_LIMBS] { - // quot was offset by 2^16 - 1 if it was negative; we undo that - // offset here: - let (lo, hi) = quot.split_at_mut(N_LIMBS); - let sign = hi[0]; - // sign must be 1 (negative) or 0 (positive) - yield_constr.constraint(filter * sign * (sign - P::ONES)); - let offset = P::Scalar::from_canonical_u16(u16::max_value()); - for c in lo { - *c -= offset * sign; - } - hi[0] = P::ZEROS; - for d in hi { - // All higher limbs must be zero - yield_constr.constraint(filter * *d); - } - - modular_constr_poly(lv, nv, yield_constr, filter, output, modulus, quot) -} - -/// Add constraints for modular operations. -pub(crate) fn eval_packed( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - // NB: The CTL code guarantees that filter is 0 or 1, i.e. that - // only one of the operations below is "live". - let bn254_filter = - lv[columns::IS_ADDFP254] + lv[columns::IS_MULFP254] + lv[columns::IS_SUBFP254]; - let filter = - lv[columns::IS_ADDMOD] + lv[columns::IS_SUBMOD] + lv[columns::IS_MULMOD] + bn254_filter; - - // Ensure that this operation is not the last row of the table; - // needed because we access the next row of the table in nv. - yield_constr.constraint_last_row(filter); - - // Verify that the modulus is the BN254 modulus for the - // {ADD,MUL,SUB}FP254 operations. - let modulus = read_value::(lv, MODULAR_MODULUS); - for (&mi, bi) in modulus.iter().zip(bn254_modulus_limbs()) { - yield_constr.constraint_transition(bn254_filter * (mi - P::Scalar::from_canonical_u16(bi))); - } - - let output = read_value::(lv, MODULAR_OUTPUT); - let quo_input = read_value::<{ 2 * N_LIMBS }, _>(lv, MODULAR_QUO_INPUT); - - let add_filter = lv[columns::IS_ADDMOD] + lv[columns::IS_ADDFP254]; - let sub_filter = lv[columns::IS_SUBMOD] + lv[columns::IS_SUBFP254]; - let mul_filter = lv[columns::IS_MULMOD] + lv[columns::IS_MULFP254]; - let addmul_filter = add_filter + mul_filter; - - // constr_poly has 2*N_LIMBS limbs - let submod_constr_poly = - submod_constr_poly(lv, nv, yield_constr, sub_filter, output, modulus, quo_input); - let modular_constr_poly = modular_constr_poly( - lv, - nv, - yield_constr, - addmul_filter, - output, - modulus, - quo_input, - ); - - let input0 = read_value(lv, MODULAR_INPUT_0); - let input1 = read_value(lv, MODULAR_INPUT_1); - - let add_input = pol_add(input0, input1); - let sub_input = pol_sub(input0, input1); - let mul_input = pol_mul_wide(input0, input1); - - for (input, &filter, constr_poly) in [ - (&add_input, &add_filter, modular_constr_poly), - (&sub_input, &sub_filter, submod_constr_poly), - (&mul_input, &mul_filter, modular_constr_poly), - ] { - // Need constr_poly_copy to be the first argument to - // pol_sub_assign, since it is the longer of the two - // arguments. - let mut constr_poly_copy = constr_poly; - pol_sub_assign(&mut constr_poly_copy, input); - - // At this point constr_poly_copy holds the coefficients of - // the polynomial - // - // operation(a(x), b(x)) - c(x) - q(x) * m(x) - (x - β) * s(x) - // - // where operation is add, mul or |a,b|->a. The modular - // operation is valid if and only if all of those coefficients - // are zero. - for &c in constr_poly_copy.iter() { - yield_constr.constraint_transition(filter * c); - } - } -} - -pub(crate) fn modular_constr_poly_ext_circuit, const D: usize>( - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - builder: &mut CircuitBuilder, - yield_constr: &mut RecursiveConstraintConsumer, - filter: ExtensionTarget, - mut output: [ExtensionTarget; N_LIMBS], - mut modulus: [ExtensionTarget; N_LIMBS], - quot: [ExtensionTarget; 2 * N_LIMBS], -) -> [ExtensionTarget; 2 * N_LIMBS] { - let mod_is_zero = nv[MODULAR_MOD_IS_ZERO]; - - // Check that mod_is_zero is zero or one - let t = builder.mul_sub_extension(mod_is_zero, mod_is_zero, mod_is_zero); - let t = builder.mul_extension(filter, t); - yield_constr.constraint_transition(builder, t); - - // Check that mod_is_zero is zero if modulus is not zero (they - // could both be zero) - let limb_sum = builder.add_many_extension(modulus); - let t = builder.mul_extension(limb_sum, mod_is_zero); - let t = builder.mul_extension(filter, t); - yield_constr.constraint_transition(builder, t); - - modulus[0] = builder.add_extension(modulus[0], mod_is_zero); - - // Is 1 iff the operation is DIV or SHR and the denominator is zero. - let div_denom_is_zero = nv[MODULAR_DIV_DENOM_IS_ZERO]; - let div_shr_filter = builder.add_extension(lv[IS_DIV], lv[IS_SHR]); - let t = builder.mul_sub_extension(mod_is_zero, div_shr_filter, div_denom_is_zero); - let t = builder.mul_extension(filter, t); - yield_constr.constraint_transition(builder, t); - - // Needed to compensate for adding mod_is_zero to modulus above, - // since the call eval_packed_generic_addcy() below subtracts modulus - // to verify in the case of a DIV or SHR. - output[0] = builder.add_extension(output[0], div_denom_is_zero); - - // Verify that the output is reduced, i.e. output < modulus. - let out_aux_red = &nv[MODULAR_OUT_AUX_RED]; - let one = builder.one_extension(); - let zero = builder.zero_extension(); - let mut is_less_than = [zero; N_LIMBS]; - is_less_than[0] = - builder.arithmetic_extension(F::NEG_ONE, F::ONE, mod_is_zero, div_shr_filter, one); - - eval_ext_circuit_addcy( - builder, - yield_constr, - filter, - &modulus, - out_aux_red, - &output, - &is_less_than, - true, - ); - // restore output[0] - output[0] = builder.sub_extension(output[0], div_denom_is_zero); - - // prod = q(x) * m(x) - let prod = pol_mul_wide2_ext_circuit(builder, quot, modulus); - // higher order terms must be zero - for &x in prod[2 * N_LIMBS..].iter() { - let t = builder.mul_extension(filter, x); - yield_constr.constraint_transition(builder, t); - } - - // constr_poly = c(x) + q(x) * m(x) - let mut constr_poly: [_; 2 * N_LIMBS] = prod[0..2 * N_LIMBS].try_into().unwrap(); - pol_add_assign_ext_circuit(builder, &mut constr_poly, &output); - - let offset = - builder.constant_extension(F::Extension::from_canonical_u64(AUX_COEFF_ABS_MAX as u64)); - let zero = builder.zero_extension(); - - // constr_poly = c(x) + q(x) * m(x) - let mut aux = [zero; 2 * N_LIMBS]; - for (c, i) in aux.iter_mut().zip(MODULAR_AUX_INPUT_LO) { - *c = builder.sub_extension(nv[i], offset); - } - // add high 16-bits of aux input - let base = F::from_canonical_u64(1u64 << LIMB_BITS); - for (c, j) in aux.iter_mut().zip(MODULAR_AUX_INPUT_HI) { - *c = builder.mul_const_add_extension(base, nv[j], *c); - } - - let base = builder.constant_extension(base.into()); - let t = pol_adjoin_root_ext_circuit(builder, aux, base); - pol_add_assign_ext_circuit(builder, &mut constr_poly, &t); - - constr_poly -} - -pub(crate) fn submod_constr_poly_ext_circuit, const D: usize>( - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - builder: &mut CircuitBuilder, - yield_constr: &mut RecursiveConstraintConsumer, - filter: ExtensionTarget, - output: [ExtensionTarget; N_LIMBS], - modulus: [ExtensionTarget; N_LIMBS], - mut quot: [ExtensionTarget; 2 * N_LIMBS], -) -> [ExtensionTarget; 2 * N_LIMBS] { - // quot was offset by 2^16 - 1 if it was negative; we undo that - // offset here: - let (lo, hi) = quot.split_at_mut(N_LIMBS); - let sign = hi[0]; - let t = builder.mul_sub_extension(sign, sign, sign); - let t = builder.mul_extension(filter, t); - // sign must be 1 (negative) or 0 (positive) - yield_constr.constraint(builder, t); - let offset = F::from_canonical_u16(u16::max_value()); - for c in lo { - let t = builder.mul_const_extension(offset, sign); - *c = builder.sub_extension(*c, t); - } - hi[0] = builder.zero_extension(); - for d in hi { - // All higher limbs must be zero - let t = builder.mul_extension(filter, *d); - yield_constr.constraint(builder, t); - } - - modular_constr_poly_ext_circuit(lv, nv, builder, yield_constr, filter, output, modulus, quot) -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let bn254_filter = builder.add_many_extension([ - lv[columns::IS_ADDFP254], - lv[columns::IS_MULFP254], - lv[columns::IS_SUBFP254], - ]); - let filter = builder.add_many_extension([ - lv[columns::IS_ADDMOD], - lv[columns::IS_SUBMOD], - lv[columns::IS_MULMOD], - bn254_filter, - ]); - - // Ensure that this operation is not the last row of the table; - // needed because we access the next row of the table in nv. - yield_constr.constraint_last_row(builder, filter); - - // Verify that the modulus is the BN254 modulus for the - // {ADD,MUL,SUB}FP254 operations. - let modulus = read_value::(lv, MODULAR_MODULUS); - for (&mi, bi) in modulus.iter().zip(bn254_modulus_limbs()) { - // bn254_filter * (mi - bi) - let t = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_u16(bi), - mi, - bn254_filter, - bn254_filter, - ); - yield_constr.constraint_transition(builder, t); - } - - let output = read_value::(lv, MODULAR_OUTPUT); - let quo_input = read_value::<{ 2 * N_LIMBS }, _>(lv, MODULAR_QUO_INPUT); - - let add_filter = builder.add_extension(lv[columns::IS_ADDMOD], lv[columns::IS_ADDFP254]); - let sub_filter = builder.add_extension(lv[columns::IS_SUBMOD], lv[columns::IS_SUBFP254]); - let mul_filter = builder.add_extension(lv[columns::IS_MULMOD], lv[columns::IS_MULFP254]); - let addmul_filter = builder.add_extension(add_filter, mul_filter); - - // constr_poly has 2*N_LIMBS limbs - let submod_constr_poly = submod_constr_poly_ext_circuit( - lv, - nv, - builder, - yield_constr, - sub_filter, - output, - modulus, - quo_input, - ); - let modular_constr_poly = modular_constr_poly_ext_circuit( - lv, - nv, - builder, - yield_constr, - addmul_filter, - output, - modulus, - quo_input, - ); - let input0 = read_value(lv, MODULAR_INPUT_0); - let input1 = read_value(lv, MODULAR_INPUT_1); - - let add_input = pol_add_ext_circuit(builder, input0, input1); - let sub_input = pol_sub_ext_circuit(builder, input0, input1); - let mul_input = pol_mul_wide_ext_circuit(builder, input0, input1); - - for (input, &filter, constr_poly) in [ - (&add_input, &add_filter, modular_constr_poly), - (&sub_input, &sub_filter, submod_constr_poly), - (&mul_input, &mul_filter, modular_constr_poly), - ] { - let mut constr_poly_copy = constr_poly; - pol_sub_assign_ext_circuit(builder, &mut constr_poly_copy, input); - for &c in constr_poly_copy.iter() { - let t = builder.mul_extension(filter, c); - yield_constr.constraint_transition(builder, t); - } - } -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::{Field, Sample}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::constraint_consumer::ConstraintConsumer; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - use crate::extension_tower::BN_BASE; - - const N_RND_TESTS: usize = 1000; - const MODULAR_OPS: [usize; 6] = [ - IS_ADDMOD, - IS_SUBMOD, - IS_MULMOD, - IS_ADDFP254, - IS_SUBFP254, - IS_MULFP254, - ]; - - // TODO: Should be able to refactor this test to apply to all operations. - #[test] - fn generate_eval_consistency_not_modular() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // if `IS_ADDMOD == 0`, then the constraints should be met even - // if all values are garbage (and similarly for the other operations). - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - lv[IS_SHR] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[IS_MOD] = F::ZERO; - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ONE, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - - #[test] - fn generate_eval_consistency() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - - for op_filter in MODULAR_OPS { - for i in 0..N_RND_TESTS { - // set inputs to random values - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - lv[IS_SHR] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[IS_MOD] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = U256::from(rng.gen::<[u8; 32]>()); - - let modulus = if [IS_ADDFP254, IS_MULFP254, IS_SUBFP254].contains(&op_filter) { - BN_BASE - } else { - let mut modulus_limbs = [0u8; 32]; - // For the second half of the tests, set the top - // 16-start digits of the modulus to zero so it is - // much smaller than the inputs. - if i > N_RND_TESTS / 2 { - // 1 <= start < N_LIMBS - let start = (rng.gen::() % (modulus_limbs.len() - 1)) + 1; - for mi in modulus_limbs.iter_mut().skip(start) { - *mi = 0u8; - } - } - U256::from(modulus_limbs) - }; - - generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - } - } - - #[test] - fn zero_modulus() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - - for op_filter in [IS_ADDMOD, IS_SUBMOD, IS_MULMOD] { - for _i in 0..N_RND_TESTS { - for corrupt_constraints in [false, true] { - // set inputs to random values and the modulus to zero; - // the output is defined to be zero when modulus is zero. - let mut lv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - let mut nv = [F::default(); NUM_ARITH_COLUMNS] - .map(|_| F::from_canonical_u16(rng.gen::())); - - // Reset operation columns, then select one - for op in MODULAR_OPS { - lv[op] = F::ZERO; - } - lv[IS_SHR] = F::ZERO; - lv[IS_DIV] = F::ZERO; - lv[IS_MOD] = F::ZERO; - lv[op_filter] = F::ONE; - - let input0 = U256::from(rng.gen::<[u8; 32]>()); - let input1 = U256::from(rng.gen::<[u8; 32]>()); - let modulus = U256::zero(); - - generate(&mut lv, &mut nv, op_filter, input0, input1, modulus); - - // check that the correct output was generated - assert!(lv[MODULAR_OUTPUT].iter().all(|&c| c == F::ZERO)); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ); - eval_packed(&lv, &nv, &mut constraint_consumer); - - if corrupt_constraints { - // Corrupt one output limb by setting it to a non-zero value. - let random_oi = MODULAR_OUTPUT.start + rng.gen::() % N_LIMBS; - lv[random_oi] = F::from_canonical_u16(rng.gen_range(1..u16::MAX)); - - eval_packed(&lv, &nv, &mut constraint_consumer); - - // Check that at least one of the constraints was non-zero. - assert!(constraint_consumer - .accumulators() - .iter() - .any(|&acc| acc != F::ZERO)); - } else { - assert!(constraint_consumer - .accumulators() - .iter() - .all(|&acc| acc == F::ZERO)); - } - } - } - } - } -} diff --git a/evm/src/arithmetic/mul.rs b/evm/src/arithmetic/mul.rs deleted file mode 100644 index 112ef7ebb5..0000000000 --- a/evm/src/arithmetic/mul.rs +++ /dev/null @@ -1,320 +0,0 @@ -//! Support for the EVM MUL instruction. -//! -//! This crate verifies an EVM MUL instruction, which takes two -//! 256-bit inputs A and B, and produces a 256-bit output C satisfying -//! -//! C = A*B (mod 2^256), -//! -//! i.e. C is the lower half of the usual long multiplication -//! A*B. Inputs A and B, and output C, are given as arrays of 16-bit -//! limbs. For example, if the limbs of A are a[0]...a[15], then -//! -//! A = \sum_{i=0}^15 a[i] β^i, -//! -//! where β = 2^16 = 2^LIMB_BITS. To verify that A, B and C satisfy -//! the equation we proceed as follows. Define -//! -//! a(x) = \sum_{i=0}^15 a[i] x^i -//! -//! (so A = a(β)) and similarly for b(x) and c(x). Then A*B = C (mod -//! 2^256) if and only if there exists q such that the polynomial -//! -//! a(x) * b(x) - c(x) - x^16 * q(x) -//! -//! is zero when evaluated at x = β, i.e. it is divisible by (x - β); -//! equivalently, there exists a polynomial s (representing the -//! carries from the long multiplication) such that -//! -//! a(x) * b(x) - c(x) - x^16 * q(x) - (x - β) * s(x) == 0 -//! -//! As we only need the lower half of the product, we can omit q(x) -//! since it is multiplied by the modulus β^16 = 2^256. Thus we only -//! need to verify -//! -//! a(x) * b(x) - c(x) - (x - β) * s(x) == 0 -//! -//! In the code below, this "constraint polynomial" is constructed in -//! the variable `constr_poly`. It must be identically zero for the -//! multiplication operation to be verified, or, equivalently, each of -//! its coefficients must be zero. The variable names of the -//! constituent polynomials are (writing N for N_LIMBS=16): -//! -//! a(x) = \sum_{i=0}^{N-1} input0[i] * x^i -//! b(x) = \sum_{i=0}^{N-1} input1[i] * x^i -//! c(x) = \sum_{i=0}^{N-1} output[i] * x^i -//! s(x) = \sum_i^{2N-3} aux[i] * x^i -//! -//! Because A, B and C are 256-bit numbers, the degrees of a, b and c -//! are (at most) 15. Thus deg(a*b) <= 30 and deg(s) <= 29; however, -//! as we're only verifying the lower half of A*B, we only need to -//! know s(x) up to degree 14 (so that (x - β)*s(x) has degree 15). On -//! the other hand, the coefficients of s(x) can be as large as -//! 16*(β-2) or 20 bits. -//! -//! Note that, unlike for the general modular multiplication (see the -//! file `modular.rs`), we don't need to check that output is reduced, -//! since any value of output is less than β^16 and is hence reduced. - -use ethereum_types::U256; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::{Field, PrimeField64}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::arithmetic::columns::*; -use crate::arithmetic::utils::*; - -/// Given the two limbs of `left_in` and `right_in`, computes `left_in * right_in`. -pub(crate) fn generate_mul(lv: &mut [F], left_in: [i64; 16], right_in: [i64; 16]) { - const MASK: i64 = (1i64 << LIMB_BITS) - 1i64; - - // Input and output have 16-bit limbs - let mut output_limbs = [0i64; N_LIMBS]; - - // Column-wise pen-and-paper long multiplication on 16-bit limbs. - // First calculate the coefficients of a(x)*b(x) (in unreduced_prod), - // then do carry propagation to obtain C = c(β) = a(β)*b(β). - let mut cy = 0i64; - let mut unreduced_prod = pol_mul_lo(left_in, right_in); - for col in 0..N_LIMBS { - let t = unreduced_prod[col] + cy; - cy = t >> LIMB_BITS; - output_limbs[col] = t & MASK; - } - // In principle, the last cy could be dropped because this is - // multiplication modulo 2^256. However, we need it below for - // aux_limbs to handle the fact that unreduced_prod will - // inevitably contain one digit's worth that is > 2^256. - - lv[OUTPUT_REGISTER].copy_from_slice(&output_limbs.map(|c| F::from_canonical_i64(c))); - pol_sub_assign(&mut unreduced_prod, &output_limbs); - - let mut aux_limbs = pol_remove_root_2exp::(unreduced_prod); - aux_limbs[N_LIMBS - 1] = -cy; - - for c in aux_limbs.iter_mut() { - // we store the unsigned offset value c + 2^20 - *c += AUX_COEFF_ABS_MAX; - } - - debug_assert!(aux_limbs.iter().all(|&c| c.abs() <= 2 * AUX_COEFF_ABS_MAX)); - - lv[MUL_AUX_INPUT_LO].copy_from_slice(&aux_limbs.map(|c| F::from_canonical_u16(c as u16))); - lv[MUL_AUX_INPUT_HI] - .copy_from_slice(&aux_limbs.map(|c| F::from_canonical_u16((c >> 16) as u16))); -} - -pub(crate) fn generate(lv: &mut [F], left_in: U256, right_in: U256) { - // TODO: It would probably be clearer/cleaner to read the U256 - // into an [i64;N] and then copy that to the lv table. - u256_to_array(&mut lv[INPUT_REGISTER_0], left_in); - u256_to_array(&mut lv[INPUT_REGISTER_1], right_in); - u256_to_array(&mut lv[INPUT_REGISTER_2], U256::zero()); - - let input0 = read_value_i64_limbs(lv, INPUT_REGISTER_0); - let input1 = read_value_i64_limbs(lv, INPUT_REGISTER_1); - - generate_mul(lv, input0, input1); -} - -pub(crate) fn eval_packed_generic_mul( - lv: &[P; NUM_ARITH_COLUMNS], - filter: P, - left_in_limbs: [P; 16], - right_in_limbs: [P; 16], - yield_constr: &mut ConstraintConsumer

, -) { - let output_limbs = read_value::(lv, OUTPUT_REGISTER); - - let base = P::Scalar::from_canonical_u64(1 << LIMB_BITS); - - let aux_limbs = { - // MUL_AUX_INPUT was offset by 2^20 in generation, so we undo - // that here - let offset = P::Scalar::from_canonical_u64(AUX_COEFF_ABS_MAX as u64); - let mut aux_limbs = read_value::(lv, MUL_AUX_INPUT_LO); - let aux_limbs_hi = &lv[MUL_AUX_INPUT_HI]; - for (lo, &hi) in aux_limbs.iter_mut().zip(aux_limbs_hi) { - *lo += hi * base - offset; - } - aux_limbs - }; - - // Constraint poly holds the coefficients of the polynomial that - // must be identically zero for this multiplication to be - // verified. - // - // These two lines set constr_poly to the polynomial a(x)b(x) - c(x), - // where a, b and c are the polynomials - // - // a(x) = \sum_i input0_limbs[i] * x^i - // b(x) = \sum_i input1_limbs[i] * x^i - // c(x) = \sum_i output_limbs[i] * x^i - // - // This polynomial should equal (x - β)*s(x) where s is - // - // s(x) = \sum_i aux_limbs[i] * x^i - // - let mut constr_poly = pol_mul_lo(left_in_limbs, right_in_limbs); - pol_sub_assign(&mut constr_poly, &output_limbs); - - // This subtracts (x - β) * s(x) from constr_poly. - pol_sub_assign(&mut constr_poly, &pol_adjoin_root(aux_limbs, base)); - - // At this point constr_poly holds the coefficients of the - // polynomial a(x)b(x) - c(x) - (x - β)*s(x). The - // multiplication is valid if and only if all of those - // coefficients are zero. - for &c in &constr_poly { - yield_constr.constraint(filter * c); - } -} - -pub(crate) fn eval_packed_generic( - lv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - let is_mul = lv[IS_MUL]; - let input0_limbs = read_value::(lv, INPUT_REGISTER_0); - let input1_limbs = read_value::(lv, INPUT_REGISTER_1); - - eval_packed_generic_mul(lv, is_mul, input0_limbs, input1_limbs, yield_constr); -} - -pub(crate) fn eval_ext_mul_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - filter: ExtensionTarget, - left_in_limbs: [ExtensionTarget; 16], - right_in_limbs: [ExtensionTarget; 16], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let output_limbs = read_value::(lv, OUTPUT_REGISTER); - - let aux_limbs = { - // MUL_AUX_INPUT was offset by 2^20 in generation, so we undo - // that here - let base = builder.constant_extension(F::Extension::from_canonical_u64(1 << LIMB_BITS)); - let offset = - builder.constant_extension(F::Extension::from_canonical_u64(AUX_COEFF_ABS_MAX as u64)); - let mut aux_limbs = read_value::(lv, MUL_AUX_INPUT_LO); - let aux_limbs_hi = &lv[MUL_AUX_INPUT_HI]; - for (lo, &hi) in aux_limbs.iter_mut().zip(aux_limbs_hi) { - //*lo = lo + hi * base - offset; - let t = builder.mul_sub_extension(hi, base, offset); - *lo = builder.add_extension(*lo, t); - } - aux_limbs - }; - - let mut constr_poly = pol_mul_lo_ext_circuit(builder, left_in_limbs, right_in_limbs); - pol_sub_assign_ext_circuit(builder, &mut constr_poly, &output_limbs); - - // This subtracts (x - β) * s(x) from constr_poly. - let base = builder.constant_extension(F::Extension::from_canonical_u64(1 << LIMB_BITS)); - let rhs = pol_adjoin_root_ext_circuit(builder, aux_limbs, base); - pol_sub_assign_ext_circuit(builder, &mut constr_poly, &rhs); - - // At this point constr_poly holds the coefficients of the - // polynomial a(x)b(x) - c(x) - (x - β)*s(x). The - // multiplication is valid if and only if all of those - // coefficients are zero. - for &c in &constr_poly { - let filter = builder.mul_extension(filter, c); - yield_constr.constraint(builder, filter); - } -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let is_mul = lv[IS_MUL]; - let input0_limbs = read_value::(lv, INPUT_REGISTER_0); - let input1_limbs = read_value::(lv, INPUT_REGISTER_1); - - eval_ext_mul_circuit( - builder, - lv, - is_mul, - input0_limbs, - input1_limbs, - yield_constr, - ); -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::{Field, Sample}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::constraint_consumer::ConstraintConsumer; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - - const N_RND_TESTS: usize = 1000; - - // TODO: Should be able to refactor this test to apply to all operations. - #[test] - fn generate_eval_consistency_not_mul() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // if `IS_MUL == 0`, then the constraints should be met even - // if all values are garbage. - lv[IS_MUL] = F::ZERO; - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ONE, - ); - eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - - #[test] - fn generate_eval_consistency_mul() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // set `IS_MUL == 1` and ensure all constraints are satisfied. - lv[IS_MUL] = F::ONE; - - for _i in 0..N_RND_TESTS { - // set inputs to random values - for (ai, bi) in INPUT_REGISTER_0.zip(INPUT_REGISTER_1) { - lv[ai] = F::from_canonical_u16(rng.gen()); - lv[bi] = F::from_canonical_u16(rng.gen()); - } - - let left_in = U256::from(rng.gen::<[u8; 32]>()); - let right_in = U256::from(rng.gen::<[u8; 32]>()); - generate(&mut lv, left_in, right_in); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ONE, - ); - eval_packed_generic(&lv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - } -} diff --git a/evm/src/arithmetic/shift.rs b/evm/src/arithmetic/shift.rs deleted file mode 100644 index bc6276b1b2..0000000000 --- a/evm/src/arithmetic/shift.rs +++ /dev/null @@ -1,338 +0,0 @@ -//! Support for the EVM SHL and SHR instructions. -//! -//! This crate verifies an EVM shift instruction, which takes two -//! 256-bit inputs S and A, and produces a 256-bit output C satisfying -//! -//! C = A << S (mod 2^256) for SHL or -//! C = A >> S (mod 2^256) for SHR. -//! -//! The way this computation is carried is by providing a third input -//! B = 1 << S (mod 2^256) -//! and then computing: -//! C = A * B (mod 2^256) for SHL or -//! C = A / B (mod 2^256) for SHR -//! -//! Inputs A, S, and B, and output C, are given as arrays of 16-bit -//! limbs. For example, if the limbs of A are a[0]...a[15], then -//! -//! A = \sum_{i=0}^15 a[i] β^i, -//! -//! where β = 2^16 = 2^LIMB_BITS. To verify that A, S, B and C satisfy -//! the equations, we proceed similarly to MUL for SHL and to DIV for SHR. - -use ethereum_types::U256; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::PrimeField64; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use super::{divmod, mul}; -use crate::arithmetic::columns::*; -use crate::arithmetic::utils::*; - -/// Generates a shift operation (either SHL or SHR). -/// The inputs are stored in the form `(shift, input, 1 << shift)`. -/// NB: if `shift >= 256`, then the third register holds 0. -/// We leverage the functions in mul.rs and divmod.rs to carry out -/// the computation. -pub(crate) fn generate( - lv: &mut [F], - nv: &mut [F], - is_shl: bool, - shift: U256, - input: U256, - result: U256, -) { - // We use the multiplication logic to generate SHL - // TODO: It would probably be clearer/cleaner to read the U256 - // into an [i64;N] and then copy that to the lv table. - // The first input is the shift we need to apply. - u256_to_array(&mut lv[INPUT_REGISTER_0], shift); - // The second register holds the input which needs shifting. - u256_to_array(&mut lv[INPUT_REGISTER_1], input); - u256_to_array(&mut lv[OUTPUT_REGISTER], result); - // If `shift >= 256`, the shifted displacement is set to 0. - // Compute 1 << shift and store it in the third input register. - let shifted_displacement = if shift > U256::from(255u64) { - U256::zero() - } else { - U256::one() << shift - }; - - u256_to_array(&mut lv[INPUT_REGISTER_2], shifted_displacement); - - let input0 = read_value_i64_limbs(lv, INPUT_REGISTER_1); // input - let input1 = read_value_i64_limbs(lv, INPUT_REGISTER_2); // 1 << shift - - if is_shl { - // We generate the multiplication input0 * input1 using mul.rs. - mul::generate_mul(lv, input0, input1); - } else { - // If the operation is SHR, we compute: `input / shifted_displacement` if `shifted_displacement == 0` - // otherwise, the output is 0. We use the logic in divmod.rs to achieve that. - divmod::generate_divmod(lv, nv, IS_SHR, INPUT_REGISTER_1, INPUT_REGISTER_2); - } -} - -/// Evaluates the constraints for an SHL opcode. -/// The logic is the same as the one for MUL. The only difference is that -/// the inputs are in `INPUT_REGISTER_1` and `INPUT_REGISTER_2` instead of -/// `INPUT_REGISTER_0` and `INPUT_REGISTER_1`. -fn eval_packed_shl( - lv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - let is_shl = lv[IS_SHL]; - let input0_limbs = read_value::(lv, INPUT_REGISTER_1); - let shifted_limbs = read_value::(lv, INPUT_REGISTER_2); - - mul::eval_packed_generic_mul(lv, is_shl, input0_limbs, shifted_limbs, yield_constr); -} - -/// Evaluates the constraints for an SHR opcode. -/// The logic is tha same as the one for DIV. The only difference is that -/// the inputs are in `INPUT_REGISTER_1` and `INPUT_REGISTER_2` instead of -/// `INPUT_REGISTER_0` and `INPUT_REGISTER_1`. -fn eval_packed_shr( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - let quo_range = OUTPUT_REGISTER; - let rem_range = AUX_INPUT_REGISTER_0; - let filter = lv[IS_SHR]; - - divmod::eval_packed_divmod_helper( - lv, - nv, - yield_constr, - filter, - INPUT_REGISTER_1, - INPUT_REGISTER_2, - quo_range, - rem_range, - ); -} - -pub(crate) fn eval_packed_generic( - lv: &[P; NUM_ARITH_COLUMNS], - nv: &[P; NUM_ARITH_COLUMNS], - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_shl(lv, yield_constr); - eval_packed_shr(lv, nv, yield_constr); -} - -fn eval_ext_circuit_shl, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let is_shl = lv[IS_SHL]; - let input0_limbs = read_value::(lv, INPUT_REGISTER_1); - let shifted_limbs = read_value::(lv, INPUT_REGISTER_2); - - mul::eval_ext_mul_circuit( - builder, - lv, - is_shl, - input0_limbs, - shifted_limbs, - yield_constr, - ); -} - -fn eval_ext_circuit_shr, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - let filter = lv[IS_SHR]; - let quo_range = OUTPUT_REGISTER; - let rem_range = AUX_INPUT_REGISTER_0; - - divmod::eval_ext_circuit_divmod_helper( - builder, - lv, - nv, - yield_constr, - filter, - INPUT_REGISTER_1, - INPUT_REGISTER_2, - quo_range, - rem_range, - ); -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - nv: &[ExtensionTarget; NUM_ARITH_COLUMNS], - yield_constr: &mut RecursiveConstraintConsumer, -) { - eval_ext_circuit_shl(builder, lv, yield_constr); - eval_ext_circuit_shr(builder, lv, nv, yield_constr); -} - -#[cfg(test)] -mod tests { - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::{Field, Sample}; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaCha8Rng; - use starky::constraint_consumer::ConstraintConsumer; - - use super::*; - use crate::arithmetic::columns::NUM_ARITH_COLUMNS; - - const N_RND_TESTS: usize = 1000; - - // TODO: Should be able to refactor this test to apply to all operations. - #[test] - fn generate_eval_consistency_not_shift() { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // if `IS_SHL == 0` and `IS_SHR == 0`, then the constraints should be met even - // if all values are garbage. - lv[IS_SHL] = F::ZERO; - lv[IS_SHR] = F::ZERO; - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ONE, - ); - eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - - fn generate_eval_consistency_shift(is_shl: bool) { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let mut nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // set `IS_SHL == 1` or `IS_SHR == 1` and ensure all constraints are satisfied. - if is_shl { - lv[IS_SHL] = F::ONE; - lv[IS_SHR] = F::ZERO; - } else { - // Set `IS_DIV` to 0 in this case, since we're using the logic of DIV for SHR. - lv[IS_DIV] = F::ZERO; - lv[IS_SHL] = F::ZERO; - lv[IS_SHR] = F::ONE; - } - - for _i in 0..N_RND_TESTS { - let shift = U256::from(rng.gen::()); - - let mut full_input = U256::from(0); - // set inputs to random values - for ai in INPUT_REGISTER_1 { - lv[ai] = F::from_canonical_u16(rng.gen()); - full_input = - U256::from(lv[ai].to_canonical_u64()) + full_input * U256::from(1 << 16); - } - - let output = if is_shl { - full_input << shift - } else { - full_input >> shift - }; - - generate(&mut lv, &mut nv, is_shl, shift, full_input, output); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ZERO, - ); - eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - } - - #[test] - fn generate_eval_consistency_shl() { - generate_eval_consistency_shift(true); - } - - #[test] - fn generate_eval_consistency_shr() { - generate_eval_consistency_shift(false); - } - - fn generate_eval_consistency_shift_over_256(is_shl: bool) { - type F = GoldilocksField; - - let mut rng = ChaCha8Rng::seed_from_u64(0x6feb51b7ec230f25); - let mut lv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - let mut nv = [F::default(); NUM_ARITH_COLUMNS].map(|_| F::sample(&mut rng)); - - // set `IS_SHL == 1` or `IS_SHR == 1` and ensure all constraints are satisfied. - if is_shl { - lv[IS_SHL] = F::ONE; - lv[IS_SHR] = F::ZERO; - } else { - // Set `IS_DIV` to 0 in this case, since we're using the logic of DIV for SHR. - lv[IS_DIV] = F::ZERO; - lv[IS_SHL] = F::ZERO; - lv[IS_SHR] = F::ONE; - } - - for _i in 0..N_RND_TESTS { - let mut shift = U256::from(rng.gen::()); - while shift > U256::MAX - 256 { - shift = U256::from(rng.gen::()); - } - shift += U256::from(256); - - let mut full_input = U256::from(0); - // set inputs to random values - for ai in INPUT_REGISTER_1 { - lv[ai] = F::from_canonical_u16(rng.gen()); - full_input = - U256::from(lv[ai].to_canonical_u64()) + full_input * U256::from(1 << 16); - } - - let output = 0.into(); - generate(&mut lv, &mut nv, is_shl, shift, full_input, output); - - let mut constraint_consumer = ConstraintConsumer::new( - vec![GoldilocksField(2), GoldilocksField(3), GoldilocksField(5)], - GoldilocksField::ONE, - GoldilocksField::ONE, - GoldilocksField::ZERO, - ); - eval_packed_generic(&lv, &nv, &mut constraint_consumer); - for &acc in &constraint_consumer.accumulators() { - assert_eq!(acc, GoldilocksField::ZERO); - } - } - } - - #[test] - fn generate_eval_consistency_shl_over_256() { - generate_eval_consistency_shift_over_256(true); - } - - #[test] - fn generate_eval_consistency_shr_over_256() { - generate_eval_consistency_shift_over_256(false); - } -} diff --git a/evm/src/arithmetic/utils.rs b/evm/src/arithmetic/utils.rs deleted file mode 100644 index 7350dd3263..0000000000 --- a/evm/src/arithmetic/utils.rs +++ /dev/null @@ -1,343 +0,0 @@ -use core::ops::{Add, AddAssign, Mul, Neg, Range, Shr, Sub, SubAssign}; - -use ethereum_types::U256; -use plonky2::field::extension::Extendable; -use plonky2::field::types::{Field, PrimeField64}; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use static_assertions::const_assert; - -use crate::arithmetic::columns::{LIMB_BITS, N_LIMBS}; - -/// Return an array of `N` zeros of type T. -pub(crate) fn pol_zero() -> [T; N] -where - T: Copy + Default, -{ - // TODO: This should really be T::zero() from num::Zero, because - // default() doesn't guarantee to initialise to zero (though in - // our case it always does). However I couldn't work out how to do - // that without touching half of the entire crate because it - // involves replacing Field::is_zero() with num::Zero::is_zero() - // which is used everywhere. Hence Default::default() it is. - [T::default(); N] -} - -/// a(x) += b(x), but must have deg(a) >= deg(b). -pub(crate) fn pol_add_assign(a: &mut [T], b: &[T]) -where - T: AddAssign + Copy + Default, -{ - debug_assert!(a.len() >= b.len(), "expected {} >= {}", a.len(), b.len()); - for (a_item, b_item) in a.iter_mut().zip(b) { - *a_item += *b_item; - } -} - -pub(crate) fn pol_add_assign_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: &mut [ExtensionTarget], - b: &[ExtensionTarget], -) { - debug_assert!(a.len() >= b.len(), "expected {} >= {}", a.len(), b.len()); - for (a_item, b_item) in a.iter_mut().zip(b) { - *a_item = builder.add_extension(*a_item, *b_item); - } -} - -/// Return a(x) + b(x); returned array is bigger than necessary to -/// make the interface consistent with `pol_mul_wide`. -pub(crate) fn pol_add(a: [T; N_LIMBS], b: [T; N_LIMBS]) -> [T; 2 * N_LIMBS - 1] -where - T: Add + Copy + Default, -{ - let mut sum = pol_zero(); - for i in 0..N_LIMBS { - sum[i] = a[i] + b[i]; - } - sum -} - -pub(crate) fn pol_add_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; N_LIMBS], - b: [ExtensionTarget; N_LIMBS], -) -> [ExtensionTarget; 2 * N_LIMBS - 1] { - let zero = builder.zero_extension(); - let mut sum = [zero; 2 * N_LIMBS - 1]; - for i in 0..N_LIMBS { - sum[i] = builder.add_extension(a[i], b[i]); - } - sum -} - -/// Return a(x) - b(x); returned array is bigger than necessary to -/// make the interface consistent with `pol_mul_wide`. -pub(crate) fn pol_sub(a: [T; N_LIMBS], b: [T; N_LIMBS]) -> [T; 2 * N_LIMBS - 1] -where - T: Sub + Copy + Default, -{ - let mut diff = pol_zero(); - for i in 0..N_LIMBS { - diff[i] = a[i] - b[i]; - } - diff -} - -pub(crate) fn pol_sub_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; N_LIMBS], - b: [ExtensionTarget; N_LIMBS], -) -> [ExtensionTarget; 2 * N_LIMBS - 1] { - let zero = builder.zero_extension(); - let mut diff = [zero; 2 * N_LIMBS - 1]; - for i in 0..N_LIMBS { - diff[i] = builder.sub_extension(a[i], b[i]); - } - diff -} - -/// a(x) -= b(x), but must have deg(a) >= deg(b). -pub(crate) fn pol_sub_assign(a: &mut [T], b: &[T]) -where - T: SubAssign + Copy, -{ - debug_assert!(a.len() >= b.len(), "expected {} >= {}", a.len(), b.len()); - for (a_item, b_item) in a.iter_mut().zip(b) { - *a_item -= *b_item; - } -} - -pub(crate) fn pol_sub_assign_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: &mut [ExtensionTarget], - b: &[ExtensionTarget], -) { - debug_assert!(a.len() >= b.len(), "expected {} >= {}", a.len(), b.len()); - for (a_item, b_item) in a.iter_mut().zip(b) { - *a_item = builder.sub_extension(*a_item, *b_item); - } -} - -/// Given polynomials a(x) and b(x), return a(x)*b(x). -/// -/// NB: The caller is responsible for ensuring that no undesired -/// overflow occurs during the calculation of the coefficients of the -/// product. -pub(crate) fn pol_mul_wide(a: [T; N_LIMBS], b: [T; N_LIMBS]) -> [T; 2 * N_LIMBS - 1] -where - T: AddAssign + Copy + Mul + Default, -{ - let mut res = [T::default(); 2 * N_LIMBS - 1]; - for (i, &ai) in a.iter().enumerate() { - for (j, &bj) in b.iter().enumerate() { - res[i + j] += ai * bj; - } - } - res -} - -pub(crate) fn pol_mul_wide_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; N_LIMBS], - b: [ExtensionTarget; N_LIMBS], -) -> [ExtensionTarget; 2 * N_LIMBS - 1] { - let zero = builder.zero_extension(); - let mut res = [zero; 2 * N_LIMBS - 1]; - for (i, &ai) in a.iter().enumerate() { - for (j, &bj) in b.iter().enumerate() { - res[i + j] = builder.mul_add_extension(ai, bj, res[i + j]); - } - } - res -} - -/// As for `pol_mul_wide` but the first argument has 2N elements and -/// hence the result has 3N-1. -pub(crate) fn pol_mul_wide2(a: [T; 2 * N_LIMBS], b: [T; N_LIMBS]) -> [T; 3 * N_LIMBS - 1] -where - T: AddAssign + Copy + Mul + Default, -{ - let mut res = [T::default(); 3 * N_LIMBS - 1]; - for (i, &ai) in a.iter().enumerate() { - for (j, &bj) in b.iter().enumerate() { - res[i + j] += ai * bj; - } - } - res -} - -pub(crate) fn pol_mul_wide2_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; 2 * N_LIMBS], - b: [ExtensionTarget; N_LIMBS], -) -> [ExtensionTarget; 3 * N_LIMBS - 1] { - let zero = builder.zero_extension(); - let mut res = [zero; 3 * N_LIMBS - 1]; - for (i, &ai) in a.iter().enumerate() { - for (j, &bj) in b.iter().enumerate() { - res[i + j] = builder.mul_add_extension(ai, bj, res[i + j]); - } - } - res -} - -/// Given a(x) and b(x), return a(x)*b(x) mod 2^256. -pub(crate) fn pol_mul_lo(a: [T; N], b: [T; N]) -> [T; N] -where - T: AddAssign + Copy + Default + Mul, -{ - let mut res = pol_zero(); - for deg in 0..N { - // Invariant: i + j = deg - for i in 0..=deg { - let j = deg - i; - res[deg] += a[i] * b[j]; - } - } - res -} - -pub(crate) fn pol_mul_lo_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; N_LIMBS], - b: [ExtensionTarget; N_LIMBS], -) -> [ExtensionTarget; N_LIMBS] { - let zero = builder.zero_extension(); - let mut res = [zero; N_LIMBS]; - for deg in 0..N_LIMBS { - for i in 0..=deg { - let j = deg - i; - res[deg] = builder.mul_add_extension(a[i], b[j], res[deg]); - } - } - res -} - -/// Adjoin M - N zeros to a, returning [a[0], a[1], ..., a[N-1], 0, 0, ..., 0]. -pub(crate) fn pol_extend(a: [T; N]) -> [T; M] -where - T: Copy + Default, -{ - assert_eq!(M, 2 * N - 1); - - let mut zero_extend = pol_zero(); - zero_extend[..N].copy_from_slice(&a); - zero_extend -} - -/// Given polynomial a(x) = \sum_{i=0}^{N-2} a[i] x^i and an element -/// `root`, return b = (x - root) * a(x). -pub(crate) fn pol_adjoin_root(a: [T; N], root: U) -> [T; N] -where - T: Add + Copy + Default + Mul + Sub, - U: Copy + Mul + Neg, -{ - // \sum_i res[i] x^i = (x - root) \sum_i a[i] x^i. Comparing - // coefficients, res[0] = -root*a[0] and - // res[i] = a[i-1] - root * a[i] - - let mut res = [T::default(); N]; - res[0] = -root * a[0]; - for deg in 1..N { - res[deg] = a[deg - 1] - (root * a[deg]); - } - res -} - -pub(crate) fn pol_adjoin_root_ext_circuit< - F: RichField + Extendable, - const D: usize, - const N: usize, ->( - builder: &mut CircuitBuilder, - a: [ExtensionTarget; N], - root: ExtensionTarget, -) -> [ExtensionTarget; N] { - let zero = builder.zero_extension(); - let mut res = [zero; N]; - // res[0] = NEG_ONE * root * a[0] + ZERO * zero - res[0] = builder.mul_extension_with_const(F::NEG_ONE, root, a[0]); - for deg in 1..N { - // res[deg] = NEG_ONE * root * a[deg] + ONE * a[deg - 1] - res[deg] = builder.arithmetic_extension(F::NEG_ONE, F::ONE, root, a[deg], a[deg - 1]); - } - res -} - -/// Given polynomial a(x) = \sum_{i=0}^{N-1} a[i] x^i and a root of `a` -/// of the form 2^EXP, return q(x) satisfying a(x) = (x - root) * q(x). -/// -/// NB: We do not verify that a(2^EXP) = 0; if this doesn't hold the -/// result is basically junk. -/// -/// NB: The result could be returned in N-1 elements, but we return -/// N and set the last element to zero since the calling code -/// happens to require a result zero-extended to N elements. -pub(crate) fn pol_remove_root_2exp(a: [T; N]) -> [T; N] -where - T: Copy + Default + Neg + Shr + Sub, -{ - // By assumption β := 2^EXP is a root of `a`, i.e. (x - β) divides - // `a`; if we write - // - // a(x) = \sum_{i=0}^{N-1} a[i] x^i - // = (x - β) \sum_{i=0}^{N-2} q[i] x^i - // - // then by comparing coefficients it is easy to see that - // - // q[0] = -a[0] / β and q[i] = (q[i-1] - a[i]) / β - // - // for 0 < i <= N-1 (and the divisions are exact). - - let mut q = [T::default(); N]; - q[0] = -(a[0] >> EXP); - - // NB: Last element of q is deliberately left equal to zero. - for deg in 1..N - 1 { - q[deg] = (q[deg - 1] - a[deg]) >> EXP; - } - q -} - -/// Read the range `value_idxs` of values from `lv` into an array of -/// length `N`. Panics if the length of the range is not `N`. -pub(crate) fn read_value(lv: &[T], value_idxs: Range) -> [T; N] { - lv[value_idxs].try_into().unwrap() -} - -/// Read the range `value_idxs` of values from `lv` into an array of -/// length `N`, interpreting the values as `i64`s. Panics if the -/// length of the range is not `N`. -pub(crate) fn read_value_i64_limbs( - lv: &[F], - value_idxs: Range, -) -> [i64; N] { - let limbs: [_; N] = lv[value_idxs].try_into().unwrap(); - limbs.map(|c| c.to_canonical_u64() as i64) -} - -#[inline] -/// Turn a 64-bit integer into 4 16-bit limbs and convert them to field elements. -fn u64_to_array(out: &mut [F], x: u64) { - const_assert!(LIMB_BITS == 16); - debug_assert!(out.len() == 4); - - out[0] = F::from_canonical_u16(x as u16); - out[1] = F::from_canonical_u16((x >> 16) as u16); - out[2] = F::from_canonical_u16((x >> 32) as u16); - out[3] = F::from_canonical_u16((x >> 48) as u16); -} - -/// Turn a 256-bit integer into 16 16-bit limbs and convert them to field elements. -// TODO: Refactor/replace u256_limbs in evm/src/util.rs -pub(crate) fn u256_to_array(out: &mut [F], x: U256) { - const_assert!(N_LIMBS == 16); - debug_assert!(out.len() == N_LIMBS); - - u64_to_array(&mut out[0..4], x.0[0]); - u64_to_array(&mut out[4..8], x.0[1]); - u64_to_array(&mut out[8..12], x.0[2]); - u64_to_array(&mut out[12..16], x.0[3]); -} diff --git a/evm/src/bin/assemble.rs b/evm/src/bin/assemble.rs deleted file mode 100644 index 2afd54d7e7..0000000000 --- a/evm/src/bin/assemble.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::{env, fs}; - -use hex::encode; -use plonky2_evm::cpu::kernel::assemble_to_bytes; - -fn main() { - let mut args = env::args(); - args.next(); - let file_contents: Vec<_> = args.map(|path| fs::read_to_string(path).unwrap()).collect(); - let assembled = assemble_to_bytes(&file_contents[..]); - println!("{}", encode(assembled)); -} diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs deleted file mode 100644 index 14cf61d5e4..0000000000 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ /dev/null @@ -1,440 +0,0 @@ -//! This crate enforces the correctness of reading and writing sequences -//! of bytes in Big-Endian ordering from and to the memory. -//! -//! The trace layout consists in one row for an `N` byte sequence (where 32 ≥ `N` > 0). -//! -//! At each row the `i`-th byte flag will be activated to indicate a sequence of -//! length i+1. -//! -//! The length of a sequence can be retrieved for CTLs as: -//! -//! sequence_length = \sum_{i=0}^31 b[i] * (i + 1) -//! -//! where b[i] is the `i`-th byte flag. -//! -//! Because of the discrepancy in endianness between the different tables, the byte sequences -//! are actually written in the trace in reverse order from the order they are provided. -//! We only store the virtual address `virt` of the first byte, and the virtual address for byte `i` -//! can be recovered as: -//! virt_i = virt + sequence_length - 1 - i -//! -//! Note that, when writing a sequence of bytes to memory, both the `U256` value and the -//! corresponding sequence length are being read from the stack. Because of the endianness -//! discrepancy mentioned above, we first convert the value to a byte sequence in Little-Endian, -//! then resize the sequence to prune unneeded zeros before reverting the sequence order. -//! This means that the higher-order bytes will be thrown away during the process, if the value -//! is greater than 256^length, and as a result a different value will be stored in memory. - -use core::marker::PhantomData; - -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use plonky2::util::transpose; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter, Lookup}; -use starky::stark::Stark; - -use super::NUM_BYTES; -use crate::all_stark::EvmStarkFrame; -use crate::byte_packing::columns::{ - index_len, value_bytes, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, IS_READ, LEN_INDICES_COLS, - NUM_COLUMNS, RANGE_COUNTER, RC_FREQUENCIES, TIMESTAMP, -}; -use crate::witness::memory::MemoryAddress; - -/// Strict upper bound for the individual bytes range-check. -const BYTE_RANGE_MAX: usize = 1usize << 8; - -/// Creates the vector of `Columns` for `BytePackingStark` corresponding to the final packed limbs being read/written. -/// `CpuStark` will look into these columns, as the CPU needs the output of byte packing. -pub(crate) fn ctl_looked_data() -> Vec> { - // Reconstruct the u32 limbs composing the final `U256` word - // being read/written from the underlying byte values. For each, - // we pack 4 consecutive bytes and shift them accordingly to - // obtain the corresponding limb. - let outputs: Vec> = (0..8) - .map(|i| { - let range = value_bytes(i * 4)..value_bytes(i * 4) + 4; - Column::linear_combination( - range - .enumerate() - .map(|(j, c)| (c, F::from_canonical_u64(1 << (8 * j)))), - ) - }) - .collect(); - - let sequence_len: Column = Column::linear_combination( - (0..NUM_BYTES).map(|i| (index_len(i), F::from_canonical_usize(i + 1))), - ); - - Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) - .chain([sequence_len]) - .chain(Column::singles(&[TIMESTAMP])) - .chain(outputs) - .collect() -} - -/// CTL filter for the `BytePackingStark` looked table. -pub(crate) fn ctl_looked_filter() -> Filter { - // The CPU table is only interested in our sequence end rows, - // since those contain the final limbs of our packed int. - Filter::new_simple(Column::sum((0..NUM_BYTES).map(index_len))) -} - -/// Column linear combination for the `BytePackingStark` table reading/writing the `i`th byte sequence from `MemoryStark`. -pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { - let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT]).collect_vec(); - - // Compute the virtual address: `ADDR_VIRTUAL` + `sequence_len` - 1 - i. - let sequence_len_minus_one = (0..NUM_BYTES) - .map(|j| (index_len(j), F::from_canonical_usize(j))) - .collect::>(); - let mut addr_virt_cols = vec![(ADDR_VIRTUAL, F::ONE)]; - addr_virt_cols.extend(sequence_len_minus_one); - let addr_virt = Column::linear_combination_with_constant( - addr_virt_cols, - F::NEG_ONE * F::from_canonical_usize(i), - ); - - res.push(addr_virt); - - // The i'th input byte being read/written. - res.push(Column::single(value_bytes(i))); - - // Since we're reading a single byte, the higher limbs must be zero. - res.extend((1..8).map(|_| Column::zero())); - - res.push(Column::single(TIMESTAMP)); - - res -} - -/// CTL filter for reading/writing the `i`th byte of the byte sequence from/to memory. -pub(crate) fn ctl_looking_memory_filter(i: usize) -> Filter { - Filter::new_simple(Column::sum((i..NUM_BYTES).map(index_len))) -} - -/// Information about a byte packing operation needed for witness generation. -#[derive(Clone, Debug)] -pub(crate) struct BytePackingOp { - /// Whether this is a read (packing) or write (unpacking) operation. - pub(crate) is_read: bool, - - /// The base address at which inputs are read/written. - pub(crate) base_address: MemoryAddress, - - /// The timestamp at which inputs are read/written. - pub(crate) timestamp: usize, - - /// The byte sequence that was read/written. - /// Its length is required to be at most 32. - pub(crate) bytes: Vec, -} - -#[derive(Copy, Clone, Default)] -pub(crate) struct BytePackingStark { - pub(crate) f: PhantomData, -} - -impl, const D: usize> BytePackingStark { - pub(crate) fn generate_trace( - &self, - ops: Vec, - min_rows: usize, - timing: &mut TimingTree, - ) -> Vec> { - // Generate most of the trace in row-major form. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_rows(ops, min_rows) - ); - let trace_row_vecs: Vec<_> = trace_rows.into_iter().map(|row| row.to_vec()).collect(); - - let mut trace_cols = transpose(&trace_row_vecs); - self.generate_range_checks(&mut trace_cols); - - trace_cols.into_iter().map(PolynomialValues::new).collect() - } - - fn generate_trace_rows( - &self, - ops: Vec, - min_rows: usize, - ) -> Vec<[F; NUM_COLUMNS]> { - let base_len: usize = ops.iter().map(|op| usize::from(!op.bytes.is_empty())).sum(); - let num_rows = core::cmp::max(base_len.max(BYTE_RANGE_MAX), min_rows).next_power_of_two(); - let mut rows = Vec::with_capacity(num_rows); - - for op in ops { - if !op.bytes.is_empty() { - rows.push(self.generate_row_for_op(op)); - } - } - - for _ in rows.len()..num_rows { - rows.push(self.generate_padding_row()); - } - - rows - } - - fn generate_row_for_op(&self, op: BytePackingOp) -> [F; NUM_COLUMNS] { - let BytePackingOp { - is_read, - base_address, - timestamp, - bytes, - } = op; - - let MemoryAddress { - context, - segment, - virt, - } = base_address; - - let mut row = [F::ZERO; NUM_COLUMNS]; - row[IS_READ] = F::from_bool(is_read); - - row[ADDR_CONTEXT] = F::from_canonical_usize(context); - row[ADDR_SEGMENT] = F::from_canonical_usize(segment); - // We store the initial virtual segment. But the CTLs, - // we start with virt + sequence_len - 1. - row[ADDR_VIRTUAL] = F::from_canonical_usize(virt); - - row[TIMESTAMP] = F::from_canonical_usize(timestamp); - - row[index_len(bytes.len() - 1)] = F::ONE; - - for (i, &byte) in bytes.iter().rev().enumerate() { - row[value_bytes(i)] = F::from_canonical_u8(byte); - } - - row - } - - const fn generate_padding_row(&self) -> [F; NUM_COLUMNS] { - [F::ZERO; NUM_COLUMNS] - } - - /// Expects input in *column*-major layout - fn generate_range_checks(&self, cols: &mut [Vec]) { - debug_assert!(cols.len() == NUM_COLUMNS); - - let n_rows = cols[0].len(); - debug_assert!(cols.iter().all(|col| col.len() == n_rows)); - - for i in 0..BYTE_RANGE_MAX { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(i); - } - for i in BYTE_RANGE_MAX..n_rows { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(BYTE_RANGE_MAX - 1); - } - - // For each column c in cols, generate the range-check - // permutations and put them in the corresponding range-check - // columns rc_c and rc_c+1. - for col in 0..NUM_BYTES { - for i in 0..n_rows { - let c = value_bytes(col); - let x = cols[c][i].to_canonical_u64() as usize; - assert!( - x < BYTE_RANGE_MAX, - "column value {} exceeds the max range value {}", - x, - BYTE_RANGE_MAX - ); - cols[RC_FREQUENCIES][x] += F::ONE; - } - } - } -} - -impl, const D: usize> Stark for BytePackingStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let local_values: &[P; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); - let next_values: &[P; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); - - // Check the range column: First value must be 0, last row - // must be 255, and intermediate rows must increment by 0 - // or 1. - let rc1 = local_values[RANGE_COUNTER]; - let rc2 = next_values[RANGE_COUNTER]; - yield_constr.constraint_first_row(rc1); - let incr = rc2 - rc1; - yield_constr.constraint_transition(incr * incr - incr); - let range_max = P::Scalar::from_canonical_u64((BYTE_RANGE_MAX - 1) as u64); - yield_constr.constraint_last_row(rc1 - range_max); - - let one = P::ONES; - - // We filter active columns by summing all the byte indices. - // Constraining each of them to be boolean is done later on below. - let current_filter = local_values[LEN_INDICES_COLS].iter().copied().sum::

(); - yield_constr.constraint(current_filter * (current_filter - one)); - - // The filter column must start by one. - yield_constr.constraint_first_row(current_filter - one); - - // The is_read flag must be boolean. - let current_is_read = local_values[IS_READ]; - yield_constr.constraint(current_is_read * (current_is_read - one)); - - // Each byte index must be boolean. - for i in 0..NUM_BYTES { - let idx_i = local_values[index_len(i)]; - yield_constr.constraint(idx_i * (idx_i - one)); - } - - // Only padding rows have their filter turned off. - let next_filter = next_values[LEN_INDICES_COLS].iter().copied().sum::

(); - yield_constr.constraint_transition(next_filter * (next_filter - current_filter)); - - // Check that all limbs after final length are 0. - for i in 0..NUM_BYTES - 1 { - // If the length is i+1, then value_bytes(i+1),...,value_bytes(NUM_BYTES-1) must be 0. - for j in i + 1..NUM_BYTES { - yield_constr.constraint(local_values[index_len(i)] * local_values[value_bytes(j)]); - } - } - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let local_values: &[ExtensionTarget; NUM_COLUMNS] = - vars.get_local_values().try_into().unwrap(); - let next_values: &[ExtensionTarget; NUM_COLUMNS] = - vars.get_next_values().try_into().unwrap(); - - // Check the range column: First value must be 0, last row - // must be 255, and intermediate rows must increment by 0 - // or 1. - let rc1 = local_values[RANGE_COUNTER]; - let rc2 = next_values[RANGE_COUNTER]; - yield_constr.constraint_first_row(builder, rc1); - let incr = builder.sub_extension(rc2, rc1); - let t = builder.mul_sub_extension(incr, incr, incr); - yield_constr.constraint_transition(builder, t); - let range_max = - builder.constant_extension(F::Extension::from_canonical_usize(BYTE_RANGE_MAX - 1)); - let t = builder.sub_extension(rc1, range_max); - yield_constr.constraint_last_row(builder, t); - - // We filter active columns by summing all the byte indices. - // Constraining each of them to be boolean is done later on below. - let current_filter = builder.add_many_extension(&local_values[LEN_INDICES_COLS]); - let constraint = builder.mul_sub_extension(current_filter, current_filter, current_filter); - yield_constr.constraint(builder, constraint); - - // The filter column must start by one. - let constraint = builder.add_const_extension(current_filter, F::NEG_ONE); - yield_constr.constraint_first_row(builder, constraint); - - // The is_read flag must be boolean. - let current_is_read = local_values[IS_READ]; - let constraint = - builder.mul_sub_extension(current_is_read, current_is_read, current_is_read); - yield_constr.constraint(builder, constraint); - - // Each byte index must be boolean. - for i in 0..NUM_BYTES { - let idx_i = local_values[index_len(i)]; - let constraint = builder.mul_sub_extension(idx_i, idx_i, idx_i); - yield_constr.constraint(builder, constraint); - } - - // Only padding rows have their filter turned off. - let next_filter = builder.add_many_extension(&next_values[LEN_INDICES_COLS]); - let constraint = builder.sub_extension(next_filter, current_filter); - let constraint = builder.mul_extension(next_filter, constraint); - yield_constr.constraint_transition(builder, constraint); - - // Check that all limbs after final length are 0. - for i in 0..NUM_BYTES - 1 { - // If the length is i+1, then value_bytes(i+1),...,value_bytes(NUM_BYTES-1) must be 0. - for j in i + 1..NUM_BYTES { - let constr = - builder.mul_extension(local_values[index_len(i)], local_values[value_bytes(j)]); - yield_constr.constraint(builder, constr); - } - } - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn lookups(&self) -> Vec> { - vec![Lookup { - columns: Column::singles(value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), - table_column: Column::single(RANGE_COUNTER), - frequencies_column: Column::single(RC_FREQUENCIES), - filter_columns: vec![None; NUM_BYTES], - }] - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -pub(crate) mod tests { - use anyhow::Result; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use crate::byte_packing::byte_packing_stark::BytePackingStark; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = BytePackingStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = BytePackingStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } -} diff --git a/evm/src/byte_packing/columns.rs b/evm/src/byte_packing/columns.rs deleted file mode 100644 index cbed53de1d..0000000000 --- a/evm/src/byte_packing/columns.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Byte packing registers. - -use core::ops::Range; - -use crate::byte_packing::NUM_BYTES; - -/// 1 if this is a READ operation, and 0 if this is a WRITE operation. -pub(crate) const IS_READ: usize = 0; - -pub(super) const LEN_INDICES_START: usize = IS_READ + 1; -// There are `NUM_BYTES` columns used to represent the length of -// the input byte sequence for a (un)packing operation. -// index_len(i) is 1 iff the length is i+1. -pub(crate) const fn index_len(i: usize) -> usize { - debug_assert!(i < NUM_BYTES); - LEN_INDICES_START + i -} - -// Note: Those are used to obtain the length of a sequence of bytes being processed. -pub(crate) const LEN_INDICES_COLS: Range = LEN_INDICES_START..LEN_INDICES_START + NUM_BYTES; - -pub(crate) const ADDR_CONTEXT: usize = LEN_INDICES_START + NUM_BYTES; -pub(crate) const ADDR_SEGMENT: usize = ADDR_CONTEXT + 1; -pub(crate) const ADDR_VIRTUAL: usize = ADDR_SEGMENT + 1; -pub(crate) const TIMESTAMP: usize = ADDR_VIRTUAL + 1; - -// 32 byte limbs hold a total of 256 bits. -const BYTES_VALUES_START: usize = TIMESTAMP + 1; -// There are `NUM_BYTES` columns used to store the values of the bytes -// that are being read/written for an (un)packing operation. -pub(crate) const fn value_bytes(i: usize) -> usize { - debug_assert!(i < NUM_BYTES); - BYTES_VALUES_START + i -} - -/// The counter column (used for the range check) starts from 0 and increments. -pub(crate) const RANGE_COUNTER: usize = BYTES_VALUES_START + NUM_BYTES; -/// The frequencies column used in logUp. -pub(crate) const RC_FREQUENCIES: usize = RANGE_COUNTER + 1; - -/// Number of columns in `BytePackingStark`. -pub(crate) const NUM_COLUMNS: usize = RANGE_COUNTER + 2; diff --git a/evm/src/byte_packing/mod.rs b/evm/src/byte_packing/mod.rs deleted file mode 100644 index 3767b21ed6..0000000000 --- a/evm/src/byte_packing/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Byte packing / unpacking unit for the EVM. -//! -//! This module handles reading / writing to memory byte sequences of -//! length at most 32 in Big-Endian ordering. - -pub mod byte_packing_stark; -pub mod columns; - -/// Maximum number of bytes being processed by a byte (un)packing operation. -pub(crate) const NUM_BYTES: usize = 32; diff --git a/evm/src/cpu/byte_unpacking.rs b/evm/src/cpu/byte_unpacking.rs deleted file mode 100644 index 4de1855dae..0000000000 --- a/evm/src/cpu/byte_unpacking.rs +++ /dev/null @@ -1,94 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES - // by the 5th bit set to 0. - let filter = lv.op.m_op_32bytes * (lv.opcode_bits[5] - P::ONES); - - // The address to write to is stored in the first memory channel. - // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. - // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. - let new_addr = nv.mem_channels[0].value; - let written_addr = lv.mem_channels[0].value; - - // Read len from opcode bits and constrain the pushed new offset. - let len_bits: P = lv.opcode_bits[..5] - .iter() - .enumerate() - .map(|(i, &bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum(); - let len = len_bits + P::ONES; - - // Check that `virt` is increased properly. - yield_constr.constraint(filter * (new_addr[0] - written_addr[0] - len)); - - // Check that `segment` and `ctx` do not change. - yield_constr.constraint(filter * (new_addr[1] - written_addr[1])); - yield_constr.constraint(filter * (new_addr[2] - written_addr[2])); - - // Check that the rest of the returned address is null. - for &limb in &new_addr[3..] { - yield_constr.constraint(filter * limb); - } -} - -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES - // by the 5th bit set to 0. - let filter = - builder.mul_sub_extension(lv.op.m_op_32bytes, lv.opcode_bits[5], lv.op.m_op_32bytes); - - // The address to write to is stored in the first memory channel. - // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. - // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. - let new_addr = nv.mem_channels[0].value; - let written_addr = lv.mem_channels[0].value; - - // Read len from opcode bits and constrain the pushed new offset. - let len_bits = lv.opcode_bits[..5].iter().enumerate().fold( - builder.zero_extension(), - |cumul, (i, &bit)| { - builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) - }, - ); - - // Check that `virt` is increased properly. - let diff = builder.sub_extension(new_addr[0], written_addr[0]); - let diff = builder.sub_extension(diff, len_bits); - let constr = builder.mul_sub_extension(filter, diff, filter); - yield_constr.constraint(builder, constr); - - // Check that `segment` and `ctx` do not change. - { - let diff = builder.sub_extension(new_addr[1], written_addr[1]); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - - let diff = builder.sub_extension(new_addr[2], written_addr[2]); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - // Check that the rest of the returned address is null. - for &limb in &new_addr[3..] { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/clock.rs b/evm/src/cpu/clock.rs deleted file mode 100644 index 4fa917a213..0000000000 --- a/evm/src/cpu/clock.rs +++ /dev/null @@ -1,37 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -/// Check the correct updating of `clock`. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // The clock is 0 at the beginning. - yield_constr.constraint_first_row(lv.clock); - // The clock is incremented by 1 at each row. - yield_constr.constraint_transition(nv.clock - lv.clock - P::ONES); -} - -/// Circuit version of `eval_packed`. -/// Check the correct updating of `clock`. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // The clock is 0 at the beginning. - yield_constr.constraint_first_row(builder, lv.clock); - // The clock is incremented by 1 at each row. - { - let new_clock = builder.add_const_extension(lv.clock, F::ONE); - let constr = builder.sub_extension(nv.clock, new_clock); - yield_constr.constraint_transition(builder, constr); - } -} diff --git a/evm/src/cpu/columns/general.rs b/evm/src/cpu/columns/general.rs deleted file mode 100644 index f565acc625..0000000000 --- a/evm/src/cpu/columns/general.rs +++ /dev/null @@ -1,157 +0,0 @@ -use core::borrow::{Borrow, BorrowMut}; -use core::fmt::{Debug, Formatter}; -use core::mem::{size_of, transmute}; - -/// General purpose columns, which can have different meanings depending on what CTL or other -/// operation is occurring at this row. -#[derive(Clone, Copy)] -pub(crate) union CpuGeneralColumnsView { - exception: CpuExceptionView, - logic: CpuLogicView, - jumps: CpuJumpsView, - shift: CpuShiftView, - stack: CpuStackView, -} - -impl CpuGeneralColumnsView { - /// View of the columns used for exceptions: they are the exception code bits. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn exception(&self) -> &CpuExceptionView { - unsafe { &self.exception } - } - - /// Mutable view of the column required for exceptions: they are the exception code bits. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn exception_mut(&mut self) -> &mut CpuExceptionView { - unsafe { &mut self.exception } - } - - /// View of the columns required for logic operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn logic(&self) -> &CpuLogicView { - unsafe { &self.logic } - } - - /// Mutable view of the columns required for logic operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn logic_mut(&mut self) -> &mut CpuLogicView { - unsafe { &mut self.logic } - } - - /// View of the columns required for jump operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn jumps(&self) -> &CpuJumpsView { - unsafe { &self.jumps } - } - - /// Mutable view of the columns required for jump operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn jumps_mut(&mut self) -> &mut CpuJumpsView { - unsafe { &mut self.jumps } - } - - /// View of the columns required for shift operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn shift(&self) -> &CpuShiftView { - unsafe { &self.shift } - } - - /// Mutable view of the columns required for shift operations. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn shift_mut(&mut self) -> &mut CpuShiftView { - unsafe { &mut self.shift } - } - - /// View of the columns required for the stack top. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn stack(&self) -> &CpuStackView { - unsafe { &self.stack } - } - - /// Mutable view of the columns required for the stack top. - /// SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn stack_mut(&mut self) -> &mut CpuStackView { - unsafe { &mut self.stack } - } -} - -impl PartialEq for CpuGeneralColumnsView { - #[allow(clippy::unconditional_recursion)] // false positive - fn eq(&self, other: &Self) -> bool { - let self_arr: &[T; NUM_SHARED_COLUMNS] = self.borrow(); - let other_arr: &[T; NUM_SHARED_COLUMNS] = other.borrow(); - self_arr == other_arr - } -} - -impl Eq for CpuGeneralColumnsView {} - -impl Debug for CpuGeneralColumnsView { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let self_arr: &[T; NUM_SHARED_COLUMNS] = self.borrow(); - Debug::fmt(self_arr, f) - } -} - -impl Borrow<[T; NUM_SHARED_COLUMNS]> for CpuGeneralColumnsView { - fn borrow(&self) -> &[T; NUM_SHARED_COLUMNS] { - unsafe { transmute(self) } - } -} - -impl BorrowMut<[T; NUM_SHARED_COLUMNS]> for CpuGeneralColumnsView { - fn borrow_mut(&mut self) -> &mut [T; NUM_SHARED_COLUMNS] { - unsafe { transmute(self) } - } -} - -/// View of the first three `CpuGeneralColumns` containing exception code bits. -#[derive(Copy, Clone)] -pub(crate) struct CpuExceptionView { - /// Exception code as little-endian bits. - pub(crate) exc_code_bits: [T; 3], -} - -/// View of the `CpuGeneralColumns` storing pseudo-inverses used to prove logic operations. -#[derive(Copy, Clone)] -pub(crate) struct CpuLogicView { - /// Pseudoinverse of `(input0 - input1)`. Used prove that they are unequal. Assumes 32-bit limbs. - pub(crate) diff_pinv: [T; 8], -} - -/// View of the first two `CpuGeneralColumns` storing a flag and a pseudoinverse used to prove jumps. -#[derive(Copy, Clone)] -pub(crate) struct CpuJumpsView { - /// A flag indicating whether a jump should occur. - pub(crate) should_jump: T, - /// Pseudoinverse of `cond.iter().sum()`. Used to check `should_jump`. - pub(crate) cond_sum_pinv: T, -} - -/// View of the first `CpuGeneralColumns` storing a pseudoinverse used to prove shift operations. -#[derive(Copy, Clone)] -pub(crate) struct CpuShiftView { - /// For a shift amount of displacement: [T], this is the inverse of - /// sum(displacement[1..]) or zero if the sum is zero. - pub(crate) high_limb_sum_inv: T, -} - -/// View of the last four `CpuGeneralColumns` storing stack-related variables. The first three are used -/// for conditionally enabling and disabling channels when reading the next `stack_top`, and the fourth one -/// is used to check for stack overflow. -#[derive(Copy, Clone)] -pub(crate) struct CpuStackView { - _unused: [T; 4], - /// Pseudoinverse of `stack_len - num_pops`. - pub(crate) stack_inv: T, - /// stack_inv * stack_len. - pub(crate) stack_inv_aux: T, - /// Used to reduce the degree of stack constraints when needed. - pub(crate) stack_inv_aux_2: T, - /// Pseudoinverse of `nv.stack_len - (MAX_USER_STACK_SIZE + 1)` to check for stack overflow. - pub(crate) stack_len_bounds_aux: T, -} - -/// Number of columns shared by all the views of `CpuGeneralColumnsView`. -/// `u8` is guaranteed to have a `size_of` of 1. -pub(crate) const NUM_SHARED_COLUMNS: usize = size_of::>(); diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs deleted file mode 100644 index 92da4e9979..0000000000 --- a/evm/src/cpu/columns/mod.rs +++ /dev/null @@ -1,168 +0,0 @@ -use core::borrow::{Borrow, BorrowMut}; -use core::fmt::Debug; -use core::mem::{size_of, transmute}; -use core::ops::{Index, IndexMut}; - -use plonky2::field::types::Field; - -use crate::cpu::columns::general::CpuGeneralColumnsView; -use crate::cpu::columns::ops::OpsColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::memory; -use crate::util::{indices_arr, transmute_no_compile_time_size_checks}; - -mod general; -/// Cpu operation flags. -pub(crate) mod ops; - -/// 32-bit limbs of the value stored in the current memory channel. -pub type MemValue = [T; memory::VALUE_LIMBS]; - -/// View of the columns required for one memory channel. -#[repr(C)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) struct MemoryChannelView { - /// 1 if this row includes a memory operation in the `i`th channel of the memory bus, otherwise - /// 0. - pub used: T, - /// 1 if a read is performed on the `i`th channel of the memory bus, otherwise 0. - pub is_read: T, - /// Context of the memory operation in the `i`th channel of the memory bus. - pub addr_context: T, - /// Segment of the memory operation in the `ith` channel of the memory bus. - pub addr_segment: T, - /// Virtual address of the memory operation in the `ith` channel of the memory bus. - pub addr_virtual: T, - /// Value, subdivided into 32-bit limbs, stored in the `ith` channel of the memory bus. - pub value: MemValue, -} - -/// View of all the columns in `CpuStark`. -#[repr(C)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -// A more lightweight channel, sharing values with the 0-th memory channel -// (which contains the top of the stack). -pub(crate) struct PartialMemoryChannelView { - pub used: T, - pub is_read: T, - pub addr_context: T, - pub addr_segment: T, - pub addr_virtual: T, -} - -#[repr(C)] -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub(crate) struct CpuColumnsView { - /// If CPU cycle: Current context. - pub context: T, - - /// If CPU cycle: Context for code memory channel. - pub code_context: T, - - /// If CPU cycle: The program counter for the current instruction. - pub program_counter: T, - - /// If CPU cycle: The stack length. - pub stack_len: T, - - /// If CPU cycle: We're in kernel (privileged) mode. - pub is_kernel_mode: T, - - /// If CPU cycle: Gas counter. - pub gas: T, - - /// If CPU cycle: flags for EVM instructions (a few cannot be shared; see the comments in - /// `OpsColumnsView`). - pub op: OpsColumnsView, - - /// If CPU cycle: the opcode, broken up into bits in little-endian order. - pub opcode_bits: [T; 8], - - /// Columns shared by various operations. - pub(crate) general: CpuGeneralColumnsView, - - /// CPU clock. - pub(crate) clock: T, - - /// Memory bus channels in the CPU. - /// Full channels are comprised of 13 columns. - pub mem_channels: [MemoryChannelView; NUM_GP_CHANNELS], - /// Partial channel is only comprised of 5 columns. - pub(crate) partial_channel: PartialMemoryChannelView, -} - -/// Total number of columns in `CpuStark`. -/// `u8` is guaranteed to have a `size_of` of 1. -pub(crate) const NUM_CPU_COLUMNS: usize = size_of::>(); - -impl Default for CpuColumnsView { - fn default() -> Self { - Self::from([F::ZERO; NUM_CPU_COLUMNS]) - } -} - -impl From<[T; NUM_CPU_COLUMNS]> for CpuColumnsView { - fn from(value: [T; NUM_CPU_COLUMNS]) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl From> for [T; NUM_CPU_COLUMNS] { - fn from(value: CpuColumnsView) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl Borrow> for [T; NUM_CPU_COLUMNS] { - fn borrow(&self) -> &CpuColumnsView { - unsafe { transmute(self) } - } -} - -impl BorrowMut> for [T; NUM_CPU_COLUMNS] { - fn borrow_mut(&mut self) -> &mut CpuColumnsView { - unsafe { transmute(self) } - } -} - -impl Borrow<[T; NUM_CPU_COLUMNS]> for CpuColumnsView { - fn borrow(&self) -> &[T; NUM_CPU_COLUMNS] { - unsafe { transmute(self) } - } -} - -impl BorrowMut<[T; NUM_CPU_COLUMNS]> for CpuColumnsView { - fn borrow_mut(&mut self) -> &mut [T; NUM_CPU_COLUMNS] { - unsafe { transmute(self) } - } -} - -impl Index for CpuColumnsView -where - [T]: Index, -{ - type Output = <[T] as Index>::Output; - - fn index(&self, index: I) -> &Self::Output { - let arr: &[T; NUM_CPU_COLUMNS] = self.borrow(); - <[T] as Index>::index(arr, index) - } -} - -impl IndexMut for CpuColumnsView -where - [T]: IndexMut, -{ - fn index_mut(&mut self, index: I) -> &mut Self::Output { - let arr: &mut [T; NUM_CPU_COLUMNS] = self.borrow_mut(); - <[T] as IndexMut>::index_mut(arr, index) - } -} - -const fn make_col_map() -> CpuColumnsView { - let indices_arr = indices_arr::(); - unsafe { transmute::<[usize; NUM_CPU_COLUMNS], CpuColumnsView>(indices_arr) } -} - -/// Mapping between [0..NUM_CPU_COLUMNS-1] and the CPU columns. -pub(crate) const COL_MAP: CpuColumnsView = make_col_map(); diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs deleted file mode 100644 index c15d657229..0000000000 --- a/evm/src/cpu/columns/ops.rs +++ /dev/null @@ -1,89 +0,0 @@ -use core::borrow::{Borrow, BorrowMut}; -use core::mem::{size_of, transmute}; -use core::ops::{Deref, DerefMut}; - -use crate::util::transmute_no_compile_time_size_checks; - -/// Structure representing the flags for the various opcodes. -#[repr(C)] -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub(crate) struct OpsColumnsView { - /// Combines ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags. - pub binary_op: T, - /// Combines ADDMOD, MULMOD and SUBMOD flags. - pub ternary_op: T, - /// Combines ADD_FP254, MUL_FP254 and SUB_FP254 flags. - pub fp254_op: T, - /// Combines EQ and ISZERO flags. - pub eq_iszero: T, - /// Combines AND, OR and XOR flags. - pub logic_op: T, - /// Combines NOT and POP flags. - pub not_pop: T, - /// Combines SHL and SHR flags. - pub shift: T, - /// Combines JUMPDEST and KECCAK_GENERAL flags. - pub jumpdest_keccak_general: T, - /// Combines JUMP and JUMPI flags. - pub jumps: T, - /// Combines PUSH and PROVER_INPUT flags. - pub push_prover_input: T, - /// Combines DUP and SWAP flags. - pub dup_swap: T, - /// Combines GET_CONTEXT and SET_CONTEXT flags. - pub context_op: T, - /// Combines MSTORE_32BYTES and MLOAD_32BYTES. - pub m_op_32bytes: T, - /// Flag for EXIT_KERNEL. - pub exit_kernel: T, - /// Combines MSTORE_GENERAL and MLOAD_GENERAL flags. - pub m_op_general: T, - /// Combines PC and PUSH0 - pub pc_push0: T, - - /// Flag for syscalls. - pub syscall: T, - /// Flag for exceptions. - pub exception: T, -} - -/// Number of columns in Cpu Stark. -/// `u8` is guaranteed to have a `size_of` of 1. -pub(crate) const NUM_OPS_COLUMNS: usize = size_of::>(); - -impl From<[T; NUM_OPS_COLUMNS]> for OpsColumnsView { - fn from(value: [T; NUM_OPS_COLUMNS]) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl From> for [T; NUM_OPS_COLUMNS] { - fn from(value: OpsColumnsView) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl Borrow> for [T; NUM_OPS_COLUMNS] { - fn borrow(&self) -> &OpsColumnsView { - unsafe { transmute(self) } - } -} - -impl BorrowMut> for [T; NUM_OPS_COLUMNS] { - fn borrow_mut(&mut self) -> &mut OpsColumnsView { - unsafe { transmute(self) } - } -} - -impl Deref for OpsColumnsView { - type Target = [T; NUM_OPS_COLUMNS]; - fn deref(&self) -> &Self::Target { - unsafe { transmute(self) } - } -} - -impl DerefMut for OpsColumnsView { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { transmute(self) } - } -} diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs deleted file mode 100644 index 9a0bb7483f..0000000000 --- a/evm/src/cpu/contextops.rs +++ /dev/null @@ -1,344 +0,0 @@ -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use super::columns::ops::OpsColumnsView; -use super::cpu_stark::{disable_unused_channels, disable_unused_channels_circuit}; -use crate::cpu::columns::CpuColumnsView; -use crate::memory::segments::Segment; - -// If true, the instruction will keep the current context for the next row. -// If false, next row's context is handled manually. -const KEEPS_CONTEXT: OpsColumnsView = OpsColumnsView { - binary_op: true, - ternary_op: true, - fp254_op: true, - eq_iszero: true, - logic_op: true, - not_pop: true, - shift: true, - jumpdest_keccak_general: true, - push_prover_input: true, - jumps: true, - pc_push0: true, - dup_swap: true, - context_op: false, - m_op_32bytes: true, - exit_kernel: true, - m_op_general: true, - syscall: true, - exception: true, -}; - -fn eval_packed_keep( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - for (op, keeps_context) in izip!(lv.op.into_iter(), KEEPS_CONTEXT.into_iter()) { - if keeps_context { - yield_constr.constraint_transition(op * (nv.context - lv.context)); - } - } - - // context_op is hybrid; we evaluate it separately. - let is_get_context = lv.op.context_op * (lv.opcode_bits[0] - P::ONES); - yield_constr.constraint_transition(is_get_context * (nv.context - lv.context)); -} - -fn eval_ext_circuit_keep, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - for (op, keeps_context) in izip!(lv.op.into_iter(), KEEPS_CONTEXT.into_iter()) { - if keeps_context { - let diff = builder.sub_extension(nv.context, lv.context); - let constr = builder.mul_extension(op, diff); - yield_constr.constraint_transition(builder, constr); - } - } - - // context_op is hybrid; we evaluate it separately. - let is_get_context = - builder.mul_sub_extension(lv.op.context_op, lv.opcode_bits[0], lv.op.context_op); - let diff = builder.sub_extension(nv.context, lv.context); - let constr = builder.mul_extension(is_get_context, diff); - yield_constr.constraint_transition(builder, constr); -} - -/// Evaluates constraints for GET_CONTEXT. -fn eval_packed_get( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // If the opcode is GET_CONTEXT, then lv.opcode_bits[0] = 0. - let filter = lv.op.context_op * (P::ONES - lv.opcode_bits[0]); - let new_stack_top = nv.mem_channels[0].value; - // Context is scaled by 2^64, hence stored in the 3rd limb. - yield_constr.constraint(filter * (new_stack_top[2] - lv.context)); - - for (_, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { - yield_constr.constraint(filter * limb); - } - - // Constrain new stack length. - yield_constr.constraint(filter * (nv.stack_len - (lv.stack_len + P::ONES))); - - // Unused channels. - disable_unused_channels(lv, filter, vec![1], yield_constr); - yield_constr.constraint(filter * nv.mem_channels[0].used); -} - -/// Circuit version of `eval_packed_get`. -/// Evaluates constraints for GET_CONTEXT. -fn eval_ext_circuit_get, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // If the opcode is GET_CONTEXT, then lv.opcode_bits[0] = 0. - let prod = builder.mul_extension(lv.op.context_op, lv.opcode_bits[0]); - let filter = builder.sub_extension(lv.op.context_op, prod); - let new_stack_top = nv.mem_channels[0].value; - // Context is scaled by 2^64, hence stored in the 3rd limb. - { - let diff = builder.sub_extension(new_stack_top[2], lv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - for (_, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } - - // Constrain new stack length. - { - let new_len = builder.add_const_extension(lv.stack_len, F::ONE); - let diff = builder.sub_extension(nv.stack_len, new_len); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - // Unused channels. - disable_unused_channels_circuit(builder, lv, filter, vec![1], yield_constr); - { - let constr = builder.mul_extension(filter, nv.mem_channels[0].used); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates constraints for `SET_CONTEXT`. -fn eval_packed_set( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let filter = lv.op.context_op * lv.opcode_bits[0]; - let stack_top = lv.mem_channels[0].value; - - // The next row's context is read from stack_top. - yield_constr.constraint(filter * (stack_top[2] - nv.context)); - for (_, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { - yield_constr.constraint(filter * limb); - } - - // The old SP is decremented (since the new context was popped) and stored in memory. - // The new SP is loaded from memory. - // This is all done with CTLs: nothing is constrained here. - - // Constrain stack_inv_aux_2. - let new_top_channel = nv.mem_channels[0]; - yield_constr.constraint( - lv.op.context_op - * (lv.general.stack().stack_inv_aux * lv.opcode_bits[0] - - lv.general.stack().stack_inv_aux_2), - ); - // The new top is loaded in memory channel 2, if the stack isn't empty (see eval_packed). - for (&limb_new_top, &limb_read_top) in new_top_channel - .value - .iter() - .zip(lv.mem_channels[2].value.iter()) - { - yield_constr.constraint( - lv.op.context_op * lv.general.stack().stack_inv_aux_2 * (limb_new_top - limb_read_top), - ); - } - - // Unused channels. - disable_unused_channels(lv, filter, vec![1], yield_constr); - yield_constr.constraint(filter * new_top_channel.used); -} - -/// Circuit version of `eval_packed_set`. -/// Evaluates constraints for SET_CONTEXT. -fn eval_ext_circuit_set, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let filter = builder.mul_extension(lv.op.context_op, lv.opcode_bits[0]); - let stack_top = lv.mem_channels[0].value; - - // The next row's context is read from stack_top. - { - let diff = builder.sub_extension(stack_top[2], nv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - for (_, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } - - // The old SP is decremented (since the new context was popped) and stored in memory. - // The new SP is loaded from memory. - // This is all done with CTLs: nothing is constrained here. - - // Constrain stack_inv_aux_2. - let new_top_channel = nv.mem_channels[0]; - { - let diff = builder.mul_sub_extension( - lv.general.stack().stack_inv_aux, - lv.opcode_bits[0], - lv.general.stack().stack_inv_aux_2, - ); - let constr = builder.mul_extension(lv.op.context_op, diff); - yield_constr.constraint(builder, constr); - } - // The new top is loaded in memory channel 2, if the stack isn't empty (see eval_packed). - for (&limb_new_top, &limb_read_top) in new_top_channel - .value - .iter() - .zip(lv.mem_channels[2].value.iter()) - { - let diff = builder.sub_extension(limb_new_top, limb_read_top); - let prod = builder.mul_extension(lv.general.stack().stack_inv_aux_2, diff); - let constr = builder.mul_extension(lv.op.context_op, prod); - yield_constr.constraint(builder, constr); - } - - // Unused channels. - disable_unused_channels_circuit(builder, lv, filter, vec![1], yield_constr); - { - let constr = builder.mul_extension(filter, new_top_channel.used); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates the constraints for the GET and SET opcodes. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_keep(lv, nv, yield_constr); - eval_packed_get(lv, nv, yield_constr); - eval_packed_set(lv, nv, yield_constr); - - // Stack constraints. - // Both operations use memory channel 2. The operations are similar enough that - // we can constrain both at the same time. - let filter = lv.op.context_op; - let channel = lv.mem_channels[2]; - // For get_context, we check if lv.stack_len is 0. For set_context, we check if nv.stack_len is 0. - // However, for get_context, we can deduce lv.stack_len from nv.stack_len since the operation only pushes. - let stack_len = nv.stack_len - (P::ONES - lv.opcode_bits[0]); - // Constrain stack_inv_aux. It's 0 if the relevant stack is empty, 1 otherwise. - yield_constr.constraint( - filter * (stack_len * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - // Enable or disable the channel. - yield_constr.constraint(filter * (lv.general.stack().stack_inv_aux - channel.used)); - let new_filter = filter * lv.general.stack().stack_inv_aux; - // It's a write for get_context, a read for set_context. - yield_constr.constraint(new_filter * (channel.is_read - lv.opcode_bits[0])); - // In both cases, next row's context works. - yield_constr.constraint(new_filter * (channel.addr_context - nv.context)); - // Same segment for both. - yield_constr.constraint( - new_filter - * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - // The address is one less than stack_len. - let addr_virtual = stack_len - P::ONES; - yield_constr.constraint(new_filter * (channel.addr_virtual - addr_virtual)); -} - -/// Circuit version of èval_packed`. -/// Evaluates the constraints for the GET and SET opcodes. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - eval_ext_circuit_keep(builder, lv, nv, yield_constr); - eval_ext_circuit_get(builder, lv, nv, yield_constr); - eval_ext_circuit_set(builder, lv, nv, yield_constr); - - // Stack constraints. - // Both operations use memory channel 2. The operations are similar enough that - // we can constrain both at the same time. - let filter = lv.op.context_op; - let channel = lv.mem_channels[2]; - // For get_context, we check if lv.stack_len is 0. For set_context, we check if nv.stack_len is 0. - // However, for get_context, we can deduce lv.stack_len from nv.stack_len since the operation only pushes. - let diff = builder.add_const_extension(lv.opcode_bits[0], -F::ONE); - let stack_len = builder.add_extension(nv.stack_len, diff); - // Constrain stack_inv_aux. It's 0 if the relevant stack is empty, 1 otherwise. - { - let diff = builder.mul_sub_extension( - stack_len, - lv.general.stack().stack_inv, - lv.general.stack().stack_inv_aux, - ); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - // Enable or disable the channel. - { - let diff = builder.sub_extension(lv.general.stack().stack_inv_aux, channel.used); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - let new_filter = builder.mul_extension(filter, lv.general.stack().stack_inv_aux); - // It's a write for get_context, a read for set_context. - { - let diff = builder.sub_extension(channel.is_read, lv.opcode_bits[0]); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint(builder, constr); - } - // In both cases, next row's context works. - { - let diff = builder.sub_extension(channel.addr_context, nv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint(builder, constr); - } - // Same segment for both. - { - let diff = builder.add_const_extension( - channel.addr_segment, - -F::from_canonical_usize(Segment::Stack.unscale()), - ); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint(builder, constr); - } - // The address is one less than stack_len. - { - let addr_virtual = builder.add_const_extension(stack_len, -F::ONE); - let diff = builder.sub_extension(channel.addr_virtual, addr_virtual); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs deleted file mode 100644 index a288746241..0000000000 --- a/evm/src/cpu/control_flow.rs +++ /dev/null @@ -1,166 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::{CpuColumnsView, COL_MAP}; -use crate::cpu::kernel::aggregator::KERNEL; - -const NATIVE_INSTRUCTIONS: [usize; 12] = [ - COL_MAP.op.binary_op, - COL_MAP.op.ternary_op, - COL_MAP.op.fp254_op, - COL_MAP.op.eq_iszero, - COL_MAP.op.logic_op, - COL_MAP.op.not_pop, - COL_MAP.op.shift, - COL_MAP.op.jumpdest_keccak_general, - // Not PROVER_INPUT: it is dealt with manually below. - // not JUMPS (possible need to jump) - COL_MAP.op.pc_push0, - // not PUSH (need to increment by more than 1) - COL_MAP.op.dup_swap, - COL_MAP.op.context_op, - // not EXIT_KERNEL (performs a jump) - COL_MAP.op.m_op_general, - // not SYSCALL (performs a jump) - // not exceptions (also jump) -]; - -/// Returns `halt`'s program counter. -pub(crate) fn get_halt_pc() -> F { - let halt_pc = KERNEL.global_labels["halt"]; - F::from_canonical_usize(halt_pc) -} - -/// Returns `main`'s program counter. -pub(crate) fn get_start_pc() -> F { - let start_pc = KERNEL.global_labels["main"]; - - F::from_canonical_usize(start_pc) -} - -/// Evaluates the constraints related to the flow of instructions. -pub(crate) fn eval_packed_generic( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let is_cpu_cycle: P = COL_MAP.op.iter().map(|&col_i| lv[col_i]).sum(); - let is_cpu_cycle_next: P = COL_MAP.op.iter().map(|&col_i| nv[col_i]).sum(); - - let next_halt_state = P::ONES - is_cpu_cycle_next; - - // Once we start executing instructions, then we continue until the end of the table - // or we reach dummy padding rows. This, along with the constraints on the first row, - // enforces that operation flags and the halt flag are mutually exclusive over the entire - // CPU trace. - yield_constr - .constraint_transition(is_cpu_cycle * (is_cpu_cycle_next + next_halt_state - P::ONES)); - - // If a row is a CPU cycle and executing a native instruction (implemented as a table row; not - // microcoded) then the program counter is incremented by 1 to obtain the next row's program - // counter. Also, the next row has the same kernel flag. - let is_native_instruction: P = NATIVE_INSTRUCTIONS.iter().map(|&col_i| lv[col_i]).sum(); - yield_constr.constraint_transition( - is_native_instruction * (lv.program_counter - nv.program_counter + P::ONES), - ); - yield_constr - .constraint_transition(is_native_instruction * (lv.is_kernel_mode - nv.is_kernel_mode)); - - // Apply the same checks as before, for PROVER_INPUT. - let is_prover_input: P = lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES); - yield_constr.constraint_transition( - is_prover_input * (lv.program_counter - nv.program_counter + P::ONES), - ); - yield_constr.constraint_transition(is_prover_input * (lv.is_kernel_mode - nv.is_kernel_mode)); - - // If a non-CPU cycle row is followed by a CPU cycle row, then: - // - the `program_counter` of the CPU cycle row is `main` (the entry point of our kernel), - // - execution is in kernel mode, and - // - the stack is empty. - let is_last_noncpu_cycle = (is_cpu_cycle - P::ONES) * is_cpu_cycle_next; - let pc_diff = nv.program_counter - get_start_pc::(); - yield_constr.constraint_transition(is_last_noncpu_cycle * pc_diff); - yield_constr.constraint_transition(is_last_noncpu_cycle * (nv.is_kernel_mode - P::ONES)); - yield_constr.constraint_transition(is_last_noncpu_cycle * nv.stack_len); -} - -/// Circuit version of `eval_packed`. -/// Evaluates the constraints related to the flow of instructions. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let one = builder.one_extension(); - - let is_cpu_cycle = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| lv[col_i])); - let is_cpu_cycle_next = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| nv[col_i])); - - let next_halt_state = builder.sub_extension(one, is_cpu_cycle_next); - - // Once we start executing instructions, then we continue until the end of the table - // or we reach dummy padding rows. This, along with the constraints on the first row, - // enforces that operation flags and the halt flag are mutually exclusive over the entire - // CPU trace. - { - let constr = builder.add_extension(is_cpu_cycle_next, next_halt_state); - let constr = builder.mul_sub_extension(is_cpu_cycle, constr, is_cpu_cycle); - yield_constr.constraint_transition(builder, constr); - } - - // If a row is a CPU cycle and executing a native instruction (implemented as a table row; not - // microcoded) then the program counter is incremented by 1 to obtain the next row's program - // counter. Also, the next row has the same kernel flag. - { - let filter = builder.add_many_extension(NATIVE_INSTRUCTIONS.iter().map(|&col_i| lv[col_i])); - let pc_diff = builder.sub_extension(lv.program_counter, nv.program_counter); - let pc_constr = builder.mul_add_extension(filter, pc_diff, filter); - yield_constr.constraint_transition(builder, pc_constr); - let kernel_diff = builder.sub_extension(lv.is_kernel_mode, nv.is_kernel_mode); - let kernel_constr = builder.mul_extension(filter, kernel_diff); - yield_constr.constraint_transition(builder, kernel_constr); - - // Same constraints as before, for PROVER_INPUT. - let is_prover_input = builder.mul_sub_extension( - lv.op.push_prover_input, - lv.opcode_bits[5], - lv.op.push_prover_input, - ); - let pc_constr = builder.mul_add_extension(is_prover_input, pc_diff, is_prover_input); - yield_constr.constraint_transition(builder, pc_constr); - let kernel_constr = builder.mul_extension(is_prover_input, kernel_diff); - yield_constr.constraint_transition(builder, kernel_constr); - } - - // If a non-CPU cycle row is followed by a CPU cycle row, then: - // - the `program_counter` of the CPU cycle row is `main` (the entry point of our kernel), - // - execution is in kernel mode, and - // - the stack is empty. - { - let is_last_noncpu_cycle = - builder.mul_sub_extension(is_cpu_cycle, is_cpu_cycle_next, is_cpu_cycle_next); - - // Start at `main`. - let main = builder.constant_extension(get_start_pc::().into()); - let pc_diff = builder.sub_extension(nv.program_counter, main); - let pc_constr = builder.mul_extension(is_last_noncpu_cycle, pc_diff); - yield_constr.constraint_transition(builder, pc_constr); - - // Start in kernel mode - let kernel_constr = builder.mul_sub_extension( - is_last_noncpu_cycle, - nv.is_kernel_mode, - is_last_noncpu_cycle, - ); - yield_constr.constraint_transition(builder, kernel_constr); - - // Start with empty stack - let kernel_constr = builder.mul_extension(is_last_noncpu_cycle, nv.stack_len); - yield_constr.constraint_transition(builder, kernel_constr); - } -} diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs deleted file mode 100644 index 340eede508..0000000000 --- a/evm/src/cpu/cpu_stark.rs +++ /dev/null @@ -1,574 +0,0 @@ -use core::borrow::Borrow; -use core::iter::repeat; -use core::marker::PhantomData; - -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::cross_table_lookup::TableWithColumns; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter}; -use starky::stark::Stark; - -use super::columns::CpuColumnsView; -use super::halt; -use super::kernel::constants::context_metadata::ContextMetadata; -use super::membus::NUM_GP_CHANNELS; -use crate::all_stark::{EvmStarkFrame, Table}; -use crate::cpu::columns::{COL_MAP, NUM_CPU_COLUMNS}; -use crate::cpu::{ - byte_unpacking, clock, contextops, control_flow, decode, dup_swap, gas, jumps, membus, memio, - modfp254, pc, push0, shift, simple_logic, stack, syscalls_exceptions, -}; -use crate::memory::segments::Segment; -use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; - -/// Creates the vector of `Columns` corresponding to the General Purpose channels when calling the Keccak sponge: -/// the CPU reads the output of the sponge directly from the `KeccakSpongeStark` table. -pub(crate) fn ctl_data_keccak_sponge() -> Vec> { - // When executing KECCAK_GENERAL, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = addr (context, segment, virt) - // GP channel 1: stack[-2] = len - // Next GP channel 0: pushed = outputs - let (context, segment, virt) = get_addr(&COL_MAP, 0); - let context = Column::single(context); - let segment = Column::single(segment); - let virt = Column::single(virt); - let len = Column::single(COL_MAP.mem_channels[1].value[0]); - - let num_channels = F::from_canonical_usize(NUM_CHANNELS); - let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - - let mut cols = vec![context, segment, virt, len, timestamp]; - cols.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value)); - cols -} - -/// CTL filter for a call to the Keccak sponge. -// KECCAK_GENERAL is differentiated from JUMPDEST by its second bit set to 0. -pub(crate) fn ctl_filter_keccak_sponge() -> Filter { - Filter::new( - vec![( - Column::single(COL_MAP.op.jumpdest_keccak_general), - Column::linear_combination_with_constant([(COL_MAP.opcode_bits[1], -F::ONE)], F::ONE), - )], - vec![], - ) -} - -/// Creates the vector of `Columns` corresponding to the two inputs and -/// one output of a binary operation. -fn ctl_data_binops() -> Vec> { - let mut res = Column::singles(COL_MAP.mem_channels[0].value).collect_vec(); - res.extend(Column::singles(COL_MAP.mem_channels[1].value)); - res.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value)); - res -} - -/// Creates the vector of `Columns` corresponding to the three inputs and -/// one output of a ternary operation. By default, ternary operations use -/// the first three memory channels, and the next top of the stack for the -/// result (binary operations do not use the third inputs). -fn ctl_data_ternops() -> Vec> { - let mut res = Column::singles(COL_MAP.mem_channels[0].value).collect_vec(); - res.extend(Column::singles(COL_MAP.mem_channels[1].value)); - res.extend(Column::singles(COL_MAP.mem_channels[2].value)); - res.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value)); - res -} - -/// Creates the vector of columns corresponding to the opcode, the two inputs and the output of the logic operation. -pub(crate) fn ctl_data_logic() -> Vec> { - // Instead of taking single columns, we reconstruct the entire opcode value directly. - let mut res = vec![Column::le_bits(COL_MAP.opcode_bits)]; - res.extend(ctl_data_binops()); - res -} - -/// CTL filter for logic operations. -pub(crate) fn ctl_filter_logic() -> Filter { - Filter::new_simple(Column::single(COL_MAP.op.logic_op)) -} - -/// Returns the `TableWithColumns` for the CPU rows calling arithmetic operations. -pub(crate) fn ctl_arithmetic_base_rows() -> TableWithColumns { - // Instead of taking single columns, we reconstruct the entire opcode value directly. - let mut columns = vec![Column::le_bits(COL_MAP.opcode_bits)]; - columns.extend(ctl_data_ternops()); - // Create the CPU Table whose columns are those with the three - // inputs and one output of the ternary operations listed in `ops` - // (also `ops` is used as the operation filter). The list of - // operations includes binary operations which will simply ignore - // the third input. - let col_bit = Column::linear_combination_with_constant( - vec![(COL_MAP.opcode_bits[5], F::NEG_ONE)], - F::ONE, - ); - TableWithColumns::new( - *Table::Cpu, - columns, - Some(Filter::new( - vec![(Column::single(COL_MAP.op.push_prover_input), col_bit)], - vec![Column::sum([ - COL_MAP.op.binary_op, - COL_MAP.op.fp254_op, - COL_MAP.op.ternary_op, - COL_MAP.op.shift, - COL_MAP.op.syscall, - COL_MAP.op.exception, - ])], - )), - ) -} - -/// Creates the vector of `Columns` corresponding to the contents of General Purpose channels when calling byte packing. -/// We use `ctl_data_keccak_sponge` because the `Columns` are the same as the ones computed for `KeccakSpongeStark`. -pub(crate) fn ctl_data_byte_packing() -> Vec> { - let mut res = vec![Column::constant(F::ONE)]; // is_read - res.extend(ctl_data_keccak_sponge()); - res -} - -/// CTL filter for the `MLOAD_32BYTES` operation. -/// MLOAD_32 BYTES is differentiated from MSTORE_32BYTES by its fifth bit set to 1. -pub(crate) fn ctl_filter_byte_packing() -> Filter { - Filter::new( - vec![( - Column::single(COL_MAP.op.m_op_32bytes), - Column::single(COL_MAP.opcode_bits[5]), - )], - vec![], - ) -} - -/// Creates the vector of `Columns` corresponding to the contents of General Purpose channels when calling byte unpacking. -pub(crate) fn ctl_data_byte_unpacking() -> Vec> { - let is_read = Column::constant(F::ZERO); - - // When executing MSTORE_32BYTES, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = addr (context, segment, virt) - // GP channel 1: stack[-2] = val - // Next GP channel 0: pushed = new_offset (virt + len) - let (context, segment, virt) = get_addr(&COL_MAP, 0); - let mut res = vec![ - is_read, - Column::single(context), - Column::single(segment), - Column::single(virt), - ]; - - // len can be reconstructed as new_offset - virt. - let len = Column::linear_combination_and_next_row_with_constant( - [(COL_MAP.mem_channels[0].value[0], -F::ONE)], - [(COL_MAP.mem_channels[0].value[0], F::ONE)], - F::ZERO, - ); - res.push(len); - - let num_channels = F::from_canonical_usize(NUM_CHANNELS); - let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - res.push(timestamp); - - let val = Column::singles(COL_MAP.mem_channels[1].value); - res.extend(val); - - res -} - -/// CTL filter for the `MSTORE_32BYTES` operation. -/// MSTORE_32BYTES is differentiated from MLOAD_32BYTES by its fifth bit set to 0. -pub(crate) fn ctl_filter_byte_unpacking() -> Filter { - Filter::new( - vec![( - Column::single(COL_MAP.op.m_op_32bytes), - Column::linear_combination_with_constant([(COL_MAP.opcode_bits[5], -F::ONE)], F::ONE), - )], - vec![], - ) -} - -/// Creates the vector of `Columns` corresponding to three consecutive (byte) reads in memory. -/// It's used by syscalls and exceptions to read an address in a jumptable. -pub(crate) fn ctl_data_jumptable_read() -> Vec> { - let is_read = Column::constant(F::ONE); - let mut res = vec![is_read]; - - // When reading the jumptable, the address to start reading from is in - // GP channel 1; the result is in GP channel 1's values. - let channel_map = COL_MAP.mem_channels[1]; - res.extend(Column::singles([ - channel_map.addr_context, - channel_map.addr_segment, - channel_map.addr_virtual, - ])); - let val = Column::singles(channel_map.value); - - // len is always 3. - let len = Column::constant(F::from_canonical_usize(3)); - res.push(len); - - let num_channels = F::from_canonical_usize(NUM_CHANNELS); - let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - res.push(timestamp); - - res.extend(val); - - res -} - -/// CTL filter for syscalls and exceptions. -pub(crate) fn ctl_filter_syscall_exceptions() -> Filter { - Filter::new_simple(Column::sum([COL_MAP.op.syscall, COL_MAP.op.exception])) -} - -/// Creates the vector of `Columns` corresponding to the contents of the CPU registers when performing a `PUSH`. -/// `PUSH` internal reads are done by calling `BytePackingStark`. -pub(crate) fn ctl_data_byte_packing_push() -> Vec> { - let is_read = Column::constant(F::ONE); - let context = Column::single(COL_MAP.code_context); - let segment = Column::constant(F::from_canonical_usize(Segment::Code as usize)); - // The initial offset if `pc + 1`. - let virt = - Column::linear_combination_with_constant([(COL_MAP.program_counter, F::ONE)], F::ONE); - let val = Column::singles_next_row(COL_MAP.mem_channels[0].value); - - // We fetch the length from the `PUSH` opcode lower bits, that indicate `len - 1`. - let len = Column::le_bits_with_constant(&COL_MAP.opcode_bits[0..5], F::ONE); - - let num_channels = F::from_canonical_usize(NUM_CHANNELS); - let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - - let mut res = vec![is_read, context, segment, virt, len, timestamp]; - res.extend(val); - - res -} - -/// CTL filter for the `PUSH` operation. -pub(crate) fn ctl_filter_byte_packing_push() -> Filter { - let bit_col = Column::single(COL_MAP.opcode_bits[5]); - Filter::new( - vec![(Column::single(COL_MAP.op.push_prover_input), bit_col)], - vec![], - ) -} - -/// Index of the memory channel storing code. -pub(crate) const MEM_CODE_CHANNEL_IDX: usize = 0; -/// Index of the first general purpose memory channel. -pub(crate) const MEM_GP_CHANNELS_IDX_START: usize = MEM_CODE_CHANNEL_IDX + 1; - -/// Recover the three components of an address, given a CPU row and -/// a provided memory channel index. -/// The components are recovered as follows: -/// -/// - `context`, shifted by 2^64 (i.e. at index 2) -/// - `segment`, shifted by 2^32 (i.e. at index 1) -/// - `virtual`, not shifted (i.e. at index 0) -pub(crate) const fn get_addr(lv: &CpuColumnsView, mem_channel: usize) -> (T, T, T) { - let addr_context = lv.mem_channels[mem_channel].value[2]; - let addr_segment = lv.mem_channels[mem_channel].value[1]; - let addr_virtual = lv.mem_channels[mem_channel].value[0]; - (addr_context, addr_segment, addr_virtual) -} - -/// Make the time/channel column for memory lookups. -fn mem_time_and_channel(channel: usize) -> Column { - let scalar = F::from_canonical_usize(NUM_CHANNELS); - let addend = F::from_canonical_usize(channel); - Column::linear_combination_with_constant([(COL_MAP.clock, scalar)], addend) -} - -/// Creates the vector of `Columns` corresponding to the contents of the code channel when reading code values. -pub(crate) fn ctl_data_code_memory() -> Vec> { - let mut cols = vec![ - Column::constant(F::ONE), // is_read - Column::single(COL_MAP.code_context), // addr_context - Column::constant(F::from_canonical_usize(Segment::Code.unscale())), // addr_segment - Column::single(COL_MAP.program_counter), // addr_virtual - ]; - - // Low limb of the value matches the opcode bits - cols.push(Column::le_bits(COL_MAP.opcode_bits)); - - // High limbs of the value are all zero. - cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); - - cols.push(mem_time_and_channel(MEM_CODE_CHANNEL_IDX)); - - cols -} - -/// Creates the vector of `Columns` corresponding to the contents of General Purpose channels. -pub(crate) fn ctl_data_gp_memory(channel: usize) -> Vec> { - let channel_map = COL_MAP.mem_channels[channel]; - let mut cols: Vec<_> = Column::singles([ - channel_map.is_read, - channel_map.addr_context, - channel_map.addr_segment, - channel_map.addr_virtual, - ]) - .collect(); - - cols.extend(Column::singles(channel_map.value)); - - cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + channel)); - - cols -} - -pub(crate) fn ctl_data_partial_memory() -> Vec> { - let channel_map = COL_MAP.partial_channel; - let values = COL_MAP.mem_channels[0].value; - let mut cols: Vec<_> = Column::singles([ - channel_map.is_read, - channel_map.addr_context, - channel_map.addr_segment, - channel_map.addr_virtual, - ]) - .collect(); - - cols.extend(Column::singles(values)); - - cols.push(mem_time_and_channel( - MEM_GP_CHANNELS_IDX_START + NUM_GP_CHANNELS, - )); - - cols -} - -/// Old stack pointer write for SET_CONTEXT. -pub(crate) fn ctl_data_memory_old_sp_write_set_context() -> Vec> { - let mut cols = vec![ - Column::constant(F::ZERO), // is_read - Column::single(COL_MAP.context), // addr_context - Column::constant(F::from_canonical_usize(Segment::ContextMetadata.unscale())), // addr_segment - Column::constant(F::from_canonical_usize( - ContextMetadata::StackSize.unscale(), - )), // addr_virtual - ]; - - // Low limb is current stack length minus one. - cols.push(Column::linear_combination_with_constant( - [(COL_MAP.stack_len, F::ONE)], - -F::ONE, - )); - - // High limbs of the value are all zero. - cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); - - cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + 1)); - - cols -} - -/// New stack pointer read for SET_CONTEXT. -pub(crate) fn ctl_data_memory_new_sp_read_set_context() -> Vec> { - let mut cols = vec![ - Column::constant(F::ONE), // is_read - Column::single(COL_MAP.mem_channels[0].value[2]), // addr_context (in the top of the stack) - Column::constant(F::from_canonical_usize(Segment::ContextMetadata.unscale())), // addr_segment - Column::constant(F::from_canonical_u64( - ContextMetadata::StackSize as u64 - Segment::ContextMetadata as u64, - )), // addr_virtual - ]; - - // Low limb is new stack length. - cols.push(Column::single_next_row(COL_MAP.stack_len)); - - // High limbs of the value are all zero. - cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1)); - - cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + 2)); - - cols -} - -/// CTL filter for code read and write operations. -pub(crate) fn ctl_filter_code_memory() -> Filter { - Filter::new_simple(Column::sum(COL_MAP.op.iter())) -} - -/// CTL filter for General Purpose memory read and write operations. -pub(crate) fn ctl_filter_gp_memory(channel: usize) -> Filter { - Filter::new_simple(Column::single(COL_MAP.mem_channels[channel].used)) -} - -pub(crate) fn ctl_filter_partial_memory() -> Filter { - Filter::new_simple(Column::single(COL_MAP.partial_channel.used)) -} - -/// CTL filter for the `SET_CONTEXT` operation. -/// SET_CONTEXT is differentiated from GET_CONTEXT by its zeroth bit set to 1 -pub(crate) fn ctl_filter_set_context() -> Filter { - Filter::new( - vec![( - Column::single(COL_MAP.op.context_op), - Column::single(COL_MAP.opcode_bits[0]), - )], - vec![], - ) -} - -/// Disable the specified memory channels. -/// Since channel 0 contains the top of the stack and is handled specially, -/// channels to disable are 1, 2 or both. All cases can be expressed as a vec. -pub(crate) fn disable_unused_channels( - lv: &CpuColumnsView

, - filter: P, - channels: Vec, - yield_constr: &mut ConstraintConsumer

, -) { - for i in channels { - yield_constr.constraint(filter * lv.mem_channels[i].used); - } -} - -/// Circuit version of `disable_unused_channels`. -/// Disable the specified memory channels. -/// Since channel 0 contains the top of the stack and is handled specially, -/// channels to disable are 1, 2 or both. All cases can be expressed as a vec. -pub(crate) fn disable_unused_channels_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - filter: ExtensionTarget, - channels: Vec, - yield_constr: &mut RecursiveConstraintConsumer, -) { - for i in channels { - let constr = builder.mul_extension(filter, lv.mem_channels[i].used); - yield_constr.constraint(builder, constr); - } -} - -/// Structure representing the CPU Stark. -#[derive(Copy, Clone, Default)] -pub(crate) struct CpuStark { - pub f: PhantomData, -} - -impl, const D: usize> Stark for CpuStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = - EvmStarkFrame, ExtensionTarget, NUM_CPU_COLUMNS>; - - /// Evaluates all CPU constraints. - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let local_values: &[P; NUM_CPU_COLUMNS] = vars.get_local_values().try_into().unwrap(); - let local_values: &CpuColumnsView

= local_values.borrow(); - let next_values: &[P; NUM_CPU_COLUMNS] = vars.get_next_values().try_into().unwrap(); - let next_values: &CpuColumnsView

= next_values.borrow(); - - byte_unpacking::eval_packed(local_values, next_values, yield_constr); - clock::eval_packed(local_values, next_values, yield_constr); - contextops::eval_packed(local_values, next_values, yield_constr); - control_flow::eval_packed_generic(local_values, next_values, yield_constr); - decode::eval_packed_generic(local_values, yield_constr); - dup_swap::eval_packed(local_values, next_values, yield_constr); - gas::eval_packed(local_values, next_values, yield_constr); - halt::eval_packed(local_values, next_values, yield_constr); - jumps::eval_packed(local_values, next_values, yield_constr); - membus::eval_packed(local_values, yield_constr); - memio::eval_packed(local_values, next_values, yield_constr); - modfp254::eval_packed(local_values, yield_constr); - pc::eval_packed(local_values, next_values, yield_constr); - push0::eval_packed(local_values, next_values, yield_constr); - shift::eval_packed(local_values, yield_constr); - simple_logic::eval_packed(local_values, next_values, yield_constr); - stack::eval_packed(local_values, next_values, yield_constr); - syscalls_exceptions::eval_packed(local_values, next_values, yield_constr); - } - - /// Circuit version of `eval_packed_generic`. - /// Evaluates all CPU constraints. - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let local_values: &[ExtensionTarget; NUM_CPU_COLUMNS] = - vars.get_local_values().try_into().unwrap(); - let local_values: &CpuColumnsView> = local_values.borrow(); - let next_values: &[ExtensionTarget; NUM_CPU_COLUMNS] = - vars.get_next_values().try_into().unwrap(); - let next_values: &CpuColumnsView> = next_values.borrow(); - - byte_unpacking::eval_ext_circuit(builder, local_values, next_values, yield_constr); - clock::eval_ext_circuit(builder, local_values, next_values, yield_constr); - contextops::eval_ext_circuit(builder, local_values, next_values, yield_constr); - control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr); - decode::eval_ext_circuit(builder, local_values, yield_constr); - dup_swap::eval_ext_circuit(builder, local_values, next_values, yield_constr); - gas::eval_ext_circuit(builder, local_values, next_values, yield_constr); - halt::eval_ext_circuit(builder, local_values, next_values, yield_constr); - jumps::eval_ext_circuit(builder, local_values, next_values, yield_constr); - membus::eval_ext_circuit(builder, local_values, yield_constr); - memio::eval_ext_circuit(builder, local_values, next_values, yield_constr); - modfp254::eval_ext_circuit(builder, local_values, yield_constr); - pc::eval_ext_circuit(builder, local_values, next_values, yield_constr); - push0::eval_ext_circuit(builder, local_values, next_values, yield_constr); - shift::eval_ext_circuit(builder, local_values, yield_constr); - simple_logic::eval_ext_circuit(builder, local_values, next_values, yield_constr); - stack::eval_ext_circuit(builder, local_values, next_values, yield_constr); - syscalls_exceptions::eval_ext_circuit(builder, local_values, next_values, yield_constr); - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use crate::cpu::cpu_stark::CpuStark; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = CpuStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = CpuStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } -} diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs deleted file mode 100644 index 83980239ac..0000000000 --- a/evm/src/cpu/decode.rs +++ /dev/null @@ -1,405 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::{CpuColumnsView, COL_MAP}; - -/// List of opcode blocks -/// Each block corresponds to exactly one flag, and each flag corresponds to exactly one block. -/// Each block of opcodes: -/// - is contiguous, -/// - has a length that is a power of 2, and -/// - its start index is a multiple of its length (it is aligned). -/// These properties permit us to check if an opcode belongs to a block of length 2^n by checking -/// its top 8-n bits. -/// Additionally, each block can be made available only to the user, only to the kernel, or to -/// both. This is mainly useful for making some instructions kernel-only, while still decoding to -/// invalid for the user. We do this by making one kernel-only block and another user-only block. -/// The exception is the PANIC instruction which is user-only without a corresponding kernel block. -/// This makes the proof unverifiable when PANIC is executed in kernel mode, which is the intended -/// behavior. -/// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to -/// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid. -const OPCODES: [(u8, usize, bool, usize); 5] = [ - // (start index of block, number of top bits to check (log2), kernel-only, flag column) - // ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly through the Arithmetic table CTL. - // ADDMOD, MULMOD and SUBMOD flags are handled partly manually here, and partly through the Arithmetic table CTL. - // FP254 operation flags are handled partly manually here, and partly through the Arithmetic table CTL. - (0x14, 1, false, COL_MAP.op.eq_iszero), - // AND, OR and XOR flags are handled partly manually here, and partly through the Logic table CTL. - // NOT and POP are handled manually here. - // SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL. - // JUMPDEST and KECCAK_GENERAL are handled manually here. - (0x56, 1, false, COL_MAP.op.jumps), // 0x56-0x57 - (0x80, 5, false, COL_MAP.op.dup_swap), // 0x80-0x9f - (0xf6, 1, true, COL_MAP.op.context_op), //0xf6-0xf7 - (0xf9, 0, true, COL_MAP.op.exit_kernel), - // MLOAD_GENERAL and MSTORE_GENERAL flags are handled manually here. -]; - -/// List of combined opcodes requiring a special handling. -/// Each index in the list corresponds to an arbitrary combination -/// of opcodes defined in evm/src/cpu/columns/ops.rs. -const COMBINED_OPCODES: [usize; 11] = [ - COL_MAP.op.logic_op, - COL_MAP.op.fp254_op, - COL_MAP.op.binary_op, - COL_MAP.op.ternary_op, - COL_MAP.op.shift, - COL_MAP.op.m_op_general, - COL_MAP.op.jumpdest_keccak_general, - COL_MAP.op.not_pop, - COL_MAP.op.pc_push0, - COL_MAP.op.m_op_32bytes, - COL_MAP.op.push_prover_input, -]; - -/// Break up an opcode (which is 8 bits long) into its eight bits. -const fn bits_from_opcode(opcode: u8) -> [bool; 8] { - [ - opcode & (1 << 0) != 0, - opcode & (1 << 1) != 0, - opcode & (1 << 2) != 0, - opcode & (1 << 3) != 0, - opcode & (1 << 4) != 0, - opcode & (1 << 5) != 0, - opcode & (1 << 6) != 0, - opcode & (1 << 7) != 0, - ] -} - -/// Evaluates the constraints for opcode decoding. -pub(crate) fn eval_packed_generic( - lv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // Ensure that the kernel flag is valid (either 0 or 1). - let kernel_mode = lv.is_kernel_mode; - yield_constr.constraint(kernel_mode * (kernel_mode - P::ONES)); - - // Ensure that the opcode bits are valid: each has to be either 0 or 1. - for bit in lv.opcode_bits { - yield_constr.constraint(bit * (bit - P::ONES)); - } - - // Check that the instruction flags are valid. - // First, check that they are all either 0 or 1. - for (_, _, _, flag_col) in OPCODES { - let flag = lv[flag_col]; - yield_constr.constraint(flag * (flag - P::ONES)); - } - // Also check that the combined instruction flags are valid. - for flag_idx in COMBINED_OPCODES { - yield_constr.constraint(lv[flag_idx] * (lv[flag_idx] - P::ONES)); - } - - // Now check that they sum to 0 or 1, including the combined flags. - let flag_sum: P = OPCODES - .into_iter() - .map(|(_, _, _, flag_col)| lv[flag_col]) - .chain(COMBINED_OPCODES.map(|op| lv[op])) - .sum::

(); - yield_constr.constraint(flag_sum * (flag_sum - P::ONES)); - - // Finally, classify all opcodes, together with the kernel flag, into blocks - for (oc, block_length, kernel_only, col) in OPCODES { - // 0 if the block/flag is available to us (is always available or we are in kernel mode) and - // 1 otherwise. - let unavailable = match kernel_only { - false => P::ZEROS, - true => P::ONES - kernel_mode, - }; - // 0 if all the opcode bits match, and something in {1, ..., 8}, otherwise. - let opcode_mismatch: P = lv - .opcode_bits - .into_iter() - .zip(bits_from_opcode(oc)) - .rev() - .take(8 - block_length) - .map(|(row_bit, flag_bit)| match flag_bit { - // 1 if the bit does not match, and 0 otherwise - false => row_bit, - true => P::ONES - row_bit, - }) - .sum(); - - // If unavailable + opcode_mismatch is 0, then the opcode bits all match and we are in the - // correct mode. - yield_constr.constraint(lv[col] * (unavailable + opcode_mismatch)); - } - - let opcode_high_bits = |num_high_bits| -> P { - lv.opcode_bits - .into_iter() - .enumerate() - .rev() - .take(num_high_bits) - .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum() - }; - - // Manually check lv.op.m_op_constr - let opcode = opcode_high_bits(8); - yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_general); - - let m_op_constr = (opcode - P::Scalar::from_canonical_usize(0xfb_usize)) - * (opcode - P::Scalar::from_canonical_usize(0xfc_usize)) - * lv.op.m_op_general; - yield_constr.constraint(m_op_constr); - - // Manually check lv.op.jumpdest_keccak_general. - // KECCAK_GENERAL is a kernel-only instruction, but not JUMPDEST. - // JUMPDEST is differentiated from KECCAK_GENERAL by its second bit set to 1. - yield_constr.constraint( - (P::ONES - kernel_mode) * lv.op.jumpdest_keccak_general * (P::ONES - lv.opcode_bits[1]), - ); - - // Check the JUMPDEST and KERNEL_GENERAL opcodes. - let jumpdest_opcode = P::Scalar::from_canonical_usize(0x5b); - let keccak_general_opcode = P::Scalar::from_canonical_usize(0x21); - let jumpdest_keccak_general_constr = (opcode - keccak_general_opcode) - * (opcode - jumpdest_opcode) - * lv.op.jumpdest_keccak_general; - yield_constr.constraint(jumpdest_keccak_general_constr); - - // Manually check lv.op.pc_push0. - // Both PC and PUSH0 can be called outside of the kernel mode: - // there is no need to constrain them in that regard. - let pc_push0_constr = (opcode - P::Scalar::from_canonical_usize(0x58_usize)) - * (opcode - P::Scalar::from_canonical_usize(0x5f_usize)) - * lv.op.pc_push0; - yield_constr.constraint(pc_push0_constr); - - // Manually check lv.op.not_pop. - // Both NOT and POP can be called outside of the kernel mode: - // there is no need to constrain them in that regard. - let not_pop_op = (opcode - P::Scalar::from_canonical_usize(0x19_usize)) - * (opcode - P::Scalar::from_canonical_usize(0x50_usize)) - * lv.op.not_pop; - yield_constr.constraint(not_pop_op); - - // Manually check lv.op.m_op_32bytes. - // Both are kernel-only. - yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_32bytes); - - // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. - let opcode_high_three = opcode_high_bits(3); - let op_32bytes = (opcode_high_three - P::Scalar::from_canonical_usize(0xc0_usize)) - * (opcode - P::Scalar::from_canonical_usize(0xf8_usize)) - * lv.op.m_op_32bytes; - yield_constr.constraint(op_32bytes); - - // Manually check PUSH and PROVER_INPUT. - // PROVER_INPUT is a kernel-only instruction, but not PUSH. - let push_prover_input_constr = (opcode - P::Scalar::from_canonical_usize(0x49_usize)) - * (opcode_high_three - P::Scalar::from_canonical_usize(0x60_usize)) - * lv.op.push_prover_input; - yield_constr.constraint(push_prover_input_constr); - let prover_input_constr = - lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES) * (P::ONES - kernel_mode); - yield_constr.constraint(prover_input_constr); -} - -fn opcode_high_bits_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - num_high_bits: usize, -) -> ExtensionTarget { - lv.opcode_bits - .into_iter() - .enumerate() - .rev() - .take(num_high_bits) - .fold(builder.zero_extension(), |cumul, (i, bit)| { - builder.mul_const_add_extension(F::from_canonical_usize(1 << i), bit, cumul) - }) -} - -/// Circuit version of `eval_packed_generic`. -/// Evaluates the constraints for opcode decoding. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let one = builder.one_extension(); - - // Note: The constraints below do not need to be restricted to CPU cycles. - - // Ensure that the kernel flag is valid (either 0 or 1). - let kernel_mode = lv.is_kernel_mode; - { - let constr = builder.mul_sub_extension(kernel_mode, kernel_mode, kernel_mode); - yield_constr.constraint(builder, constr); - } - - // Ensure that the opcode bits are valid: each has to be either 0 or 1. - for bit in lv.opcode_bits { - let constr = builder.mul_sub_extension(bit, bit, bit); - yield_constr.constraint(builder, constr); - } - - // Check that the instruction flags are valid. - // First, check that they are all either 0 or 1. - for (_, _, _, flag_col) in OPCODES { - let flag = lv[flag_col]; - let constr = builder.mul_sub_extension(flag, flag, flag); - yield_constr.constraint(builder, constr); - } - // Also check that the combined instruction flags are valid. - for flag_idx in COMBINED_OPCODES { - let constr = builder.mul_sub_extension(lv[flag_idx], lv[flag_idx], lv[flag_idx]); - yield_constr.constraint(builder, constr); - } - - // Now check that they sum to 0 or 1, including the combined flags. - { - let mut flag_sum = - builder.add_many_extension(COMBINED_OPCODES.into_iter().map(|idx| lv[idx])); - for (_, _, _, flag_col) in OPCODES { - let flag = lv[flag_col]; - flag_sum = builder.add_extension(flag_sum, flag); - } - let constr = builder.mul_sub_extension(flag_sum, flag_sum, flag_sum); - yield_constr.constraint(builder, constr); - } - - // Finally, classify all opcodes, together with the kernel flag, into blocks - for (oc, block_length, kernel_only, col) in OPCODES { - // 0 if the block/flag is available to us (is always available or we are in kernel mode) and - // 1 otherwise. - let unavailable = match kernel_only { - false => builder.zero_extension(), - true => builder.sub_extension(one, kernel_mode), - }; - // 0 if all the opcode bits match, and something in {1, ..., 8}, otherwise. - let opcode_mismatch = lv - .opcode_bits - .into_iter() - .zip(bits_from_opcode(oc)) - .rev() - .take(8 - block_length) - .fold(builder.zero_extension(), |cumul, (row_bit, flag_bit)| { - let to_add = match flag_bit { - false => row_bit, - true => builder.sub_extension(one, row_bit), - }; - builder.add_extension(cumul, to_add) - }); - - // If unavailable + opcode_mismatch is 0, then the opcode bits all match and we are in the - // correct mode. - let constr = builder.add_extension(unavailable, opcode_mismatch); - let constr = builder.mul_extension(lv[col], constr); - yield_constr.constraint(builder, constr); - } - - // Manually check lv.op.m_op_constr - let opcode = opcode_high_bits_circuit(builder, lv, 8); - - let mload_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfb_usize)); - let mstore_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfc_usize)); - - let one_extension = builder.constant_extension(F::Extension::ONE); - let is_not_kernel_mode = builder.sub_extension(one_extension, kernel_mode); - let constr = builder.mul_extension(is_not_kernel_mode, lv.op.m_op_general); - yield_constr.constraint(builder, constr); - - let mload_constr = builder.sub_extension(opcode, mload_opcode); - let mstore_constr = builder.sub_extension(opcode, mstore_opcode); - let mut m_op_constr = builder.mul_extension(mload_constr, mstore_constr); - m_op_constr = builder.mul_extension(m_op_constr, lv.op.m_op_general); - - yield_constr.constraint(builder, m_op_constr); - - // Manually check lv.op.jumpdest_keccak_general. - // KECCAK_GENERAL is a kernel-only instruction, but not JUMPDEST. - // JUMPDEST is differentiated from KECCAK_GENERAL by its second bit set to 1. - let jumpdest_opcode = - builder.constant_extension(F::Extension::from_canonical_usize(0x5b_usize)); - let keccak_general_opcode = - builder.constant_extension(F::Extension::from_canonical_usize(0x21_usize)); - - // Check that KECCAK_GENERAL is kernel-only. - let mut kernel_general_filter = builder.sub_extension(one, lv.opcode_bits[1]); - kernel_general_filter = - builder.mul_extension(lv.op.jumpdest_keccak_general, kernel_general_filter); - let constr = builder.mul_extension(is_not_kernel_mode, kernel_general_filter); - yield_constr.constraint(builder, constr); - - // Check the JUMPDEST and KERNEL_GENERAL opcodes. - let jumpdest_constr = builder.sub_extension(opcode, jumpdest_opcode); - let keccak_general_constr = builder.sub_extension(opcode, keccak_general_opcode); - let mut jumpdest_keccak_general_constr = - builder.mul_extension(jumpdest_constr, keccak_general_constr); - jumpdest_keccak_general_constr = builder.mul_extension( - jumpdest_keccak_general_constr, - lv.op.jumpdest_keccak_general, - ); - - yield_constr.constraint(builder, jumpdest_keccak_general_constr); - - // Manually check lv.op.pc_push0. - // Both PC and PUSH0 can be called outside of the kernel mode: - // there is no need to constrain them in that regard. - let pc_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0x58_usize)); - let push0_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0x5f_usize)); - let pc_constr = builder.sub_extension(opcode, pc_opcode); - let push0_constr = builder.sub_extension(opcode, push0_opcode); - let mut pc_push0_constr = builder.mul_extension(pc_constr, push0_constr); - pc_push0_constr = builder.mul_extension(pc_push0_constr, lv.op.pc_push0); - yield_constr.constraint(builder, pc_push0_constr); - - // Manually check lv.op.not_pop. - // Both NOT and POP can be called outside of the kernel mode: - // there is no need to constrain them in that regard. - let not_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0x19_usize)); - let pop_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0x50_usize)); - - let not_constr = builder.sub_extension(opcode, not_opcode); - let pop_constr = builder.sub_extension(opcode, pop_opcode); - - let mut not_pop_constr = builder.mul_extension(not_constr, pop_constr); - not_pop_constr = builder.mul_extension(lv.op.not_pop, not_pop_constr); - yield_constr.constraint(builder, not_pop_constr); - - // Manually check lv.op.m_op_32bytes. - // Both are kernel-only. - let constr = builder.mul_extension(is_not_kernel_mode, lv.op.m_op_32bytes); - yield_constr.constraint(builder, constr); - - // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. - let opcode_high_three = opcode_high_bits_circuit(builder, lv, 3); - let mstore_32bytes_opcode = - builder.constant_extension(F::Extension::from_canonical_usize(0xc0_usize)); - let mload_32bytes_opcode = - builder.constant_extension(F::Extension::from_canonical_usize(0xf8_usize)); - let mstore_32bytes_constr = builder.sub_extension(opcode_high_three, mstore_32bytes_opcode); - let mload_32bytes_constr = builder.sub_extension(opcode, mload_32bytes_opcode); - let constr = builder.mul_extension(mstore_32bytes_constr, mload_32bytes_constr); - let constr = builder.mul_extension(constr, lv.op.m_op_32bytes); - yield_constr.constraint(builder, constr); - - // Manually check PUSH and PROVER_INPUT. - // PROVER_INPUT is a kernel-only instruction, but not PUSH. - let prover_input_opcode = - builder.constant_extension(F::Extension::from_canonical_usize(0x49usize)); - let push_opcodes = builder.constant_extension(F::Extension::from_canonical_usize(0x60usize)); - - let push_constr = builder.sub_extension(opcode_high_three, push_opcodes); - let prover_input_constr = builder.sub_extension(opcode, prover_input_opcode); - - let push_prover_input_constr = - builder.mul_many_extension([lv.op.push_prover_input, prover_input_constr, push_constr]); - yield_constr.constraint(builder, push_prover_input_constr); - let prover_input_filter = builder.mul_sub_extension( - lv.op.push_prover_input, - lv.opcode_bits[5], - lv.op.push_prover_input, - ); - let constr = builder.mul_extension(prover_input_filter, is_not_kernel_mode); - yield_constr.constraint(builder, constr); -} diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs deleted file mode 100644 index e67eaa6253..0000000000 --- a/evm/src/cpu/dup_swap.rs +++ /dev/null @@ -1,343 +0,0 @@ -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::{CpuColumnsView, MemoryChannelView}; -use crate::memory::segments::Segment; - -/// Constrain two channels to have equal values. -fn channels_equal_packed( - filter: P, - ch_a: &MemoryChannelView

, - ch_b: &MemoryChannelView

, - yield_constr: &mut ConstraintConsumer

, -) { - for (limb_a, limb_b) in izip!(ch_a.value, ch_b.value) { - yield_constr.constraint(filter * (limb_a - limb_b)); - } -} - -/// Constrain two channels to have equal values. -fn channels_equal_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - filter: ExtensionTarget, - ch_a: &MemoryChannelView>, - ch_b: &MemoryChannelView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - for (limb_a, limb_b) in izip!(ch_a.value, ch_b.value) { - let diff = builder.sub_extension(limb_a, limb_b); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } -} - -/// Set `used`, `is_read`, and address for channel. -/// -/// `offset` is the stack index before this instruction is executed, e.g. `0` for the top of the -/// stack. -fn constrain_channel_packed( - is_read: bool, - filter: P, - offset: P, - channel: &MemoryChannelView

, - lv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - yield_constr.constraint(filter * (channel.used - P::ONES)); - yield_constr.constraint(filter * (channel.is_read - P::Scalar::from_bool(is_read))); - yield_constr.constraint(filter * (channel.addr_context - lv.context)); - yield_constr.constraint( - filter * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - // Top of the stack is at `addr = lv.stack_len - 1`. - let addr_virtual = lv.stack_len - P::ONES - offset; - yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); -} - -/// Set `used`, `is_read`, and address for channel. -/// -/// `offset` is the stack index before this instruction is executed, e.g. `0` for the top of the -/// stack. -fn constrain_channel_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - is_read: bool, - filter: ExtensionTarget, - offset: ExtensionTarget, - channel: &MemoryChannelView>, - lv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - { - let constr = builder.mul_sub_extension(filter, channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = if is_read { - builder.mul_sub_extension(filter, channel.is_read, filter) - } else { - builder.mul_extension(filter, channel.is_read) - }; - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_context, lv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::Stack.unscale()), - filter, - channel.addr_segment, - filter, - ); - yield_constr.constraint(builder, constr); - } - // Top of the stack is at `addr = lv.stack_len - 1`. - { - let constr = builder.add_extension(channel.addr_virtual, offset); - let constr = builder.sub_extension(constr, lv.stack_len); - let constr = builder.mul_add_extension(filter, constr, filter); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates constraints for DUP. -fn eval_packed_dup( - n: P, - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // DUP opcodes have 0 at the 5-th position, while SWAP opcodes have 1. - let filter = lv.op.dup_swap * (P::ONES - lv.opcode_bits[4]); - - let write_channel = &lv.mem_channels[1]; - let read_channel = &lv.mem_channels[2]; - - // Constrain the input and top of the stack channels to have the same value. - channels_equal_packed(filter, write_channel, &lv.mem_channels[0], yield_constr); - // Constrain the output channel's addresses, `is_read` and `used` fields. - constrain_channel_packed(false, filter, P::ZEROS, write_channel, lv, yield_constr); - - // Constrain the output and top of the stack channels to have the same value. - channels_equal_packed(filter, read_channel, &nv.mem_channels[0], yield_constr); - // Constrain the input channel's addresses, `is_read` and `used` fields. - constrain_channel_packed(true, filter, n, read_channel, lv, yield_constr); - - // Constrain nv.stack_len. - yield_constr.constraint_transition(filter * (nv.stack_len - lv.stack_len - P::ONES)); - - // Disable next top. - yield_constr.constraint(filter * nv.mem_channels[0].used); -} - -/// Circuit version of `eval_packed_dup`. -/// Evaluates constraints for DUP. -fn eval_ext_circuit_dup, const D: usize>( - builder: &mut CircuitBuilder, - n: ExtensionTarget, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let zero = builder.zero_extension(); - let one = builder.one_extension(); - // DUP opcodes have 0 at the 5-th position, while SWAP opcodes have 1. - let mut filter = builder.sub_extension(one, lv.opcode_bits[4]); - filter = builder.mul_extension(lv.op.dup_swap, filter); - - let write_channel = &lv.mem_channels[1]; - let read_channel = &lv.mem_channels[2]; - - // Constrain the input and top of the stack channels to have the same value. - channels_equal_ext_circuit( - builder, - filter, - write_channel, - &lv.mem_channels[0], - yield_constr, - ); - // Constrain the output channel's addresses, `is_read` and `used` fields. - constrain_channel_ext_circuit( - builder, - false, - filter, - zero, - write_channel, - lv, - yield_constr, - ); - - // Constrain the output and top of the stack channels to have the same value. - channels_equal_ext_circuit( - builder, - filter, - read_channel, - &nv.mem_channels[0], - yield_constr, - ); - // Constrain the input channel's addresses, `is_read` and `used` fields. - constrain_channel_ext_circuit(builder, true, filter, n, read_channel, lv, yield_constr); - - // Constrain nv.stack_len. - { - let diff = builder.sub_extension(nv.stack_len, lv.stack_len); - let constr = builder.mul_sub_extension(filter, diff, filter); - yield_constr.constraint_transition(builder, constr); - } - - // Disable next top. - { - let constr = builder.mul_extension(filter, nv.mem_channels[0].used); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates constraints for SWAP. -fn eval_packed_swap( - n: P, - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let n_plus_one = n + P::ONES; - - // DUP opcodes have 0 at the 5-th position, while SWAP opcodes have 1. - let filter = lv.op.dup_swap * lv.opcode_bits[4]; - - let in1_channel = &lv.mem_channels[0]; - let in2_channel = &lv.mem_channels[1]; - let out_channel = &lv.mem_channels[2]; - - // Constrain the first input channel value to be equal to the output channel value. - channels_equal_packed(filter, in1_channel, out_channel, yield_constr); - // We set `is_read`, `used` and the address for the first input. The first input is - // read from the top of the stack, and is therefore not a memory read. - constrain_channel_packed(false, filter, n_plus_one, out_channel, lv, yield_constr); - - // Constrain the second input channel value to be equal to the new top of the stack. - channels_equal_packed(filter, in2_channel, &nv.mem_channels[0], yield_constr); - // We set `is_read`, `used` and the address for the second input. - constrain_channel_packed(true, filter, n_plus_one, in2_channel, lv, yield_constr); - - // Constrain nv.stack_len. - yield_constr.constraint(filter * (nv.stack_len - lv.stack_len)); - - // Disable next top. - yield_constr.constraint(filter * nv.mem_channels[0].used); -} - -/// Circuit version of `eval_packed_swap`. -/// Evaluates constraints for SWAP. -fn eval_ext_circuit_swap, const D: usize>( - builder: &mut CircuitBuilder, - n: ExtensionTarget, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let one = builder.one_extension(); - let n_plus_one = builder.add_extension(n, one); - - // DUP opcodes have 0 at the 5-th position, while SWAP opcodes have 1. - let filter = builder.mul_extension(lv.op.dup_swap, lv.opcode_bits[4]); - - let in1_channel = &lv.mem_channels[0]; - let in2_channel = &lv.mem_channels[1]; - let out_channel = &lv.mem_channels[2]; - - // Constrain the first input channel value to be equal to the output channel value. - channels_equal_ext_circuit(builder, filter, in1_channel, out_channel, yield_constr); - // We set `is_read`, `used` and the address for the first input. The first input is - // read from the top of the stack, and is therefore not a memory read. - constrain_channel_ext_circuit( - builder, - false, - filter, - n_plus_one, - out_channel, - lv, - yield_constr, - ); - - // Constrain the second input channel value to be equal to the new top of the stack. - channels_equal_ext_circuit( - builder, - filter, - in2_channel, - &nv.mem_channels[0], - yield_constr, - ); - // We set `is_read`, `used` and the address for the second input. - constrain_channel_ext_circuit( - builder, - true, - filter, - n_plus_one, - in2_channel, - lv, - yield_constr, - ); - - // Constrain nv.stack_len. - let diff = builder.sub_extension(nv.stack_len, lv.stack_len); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - - // Disable next top. - { - let constr = builder.mul_extension(filter, nv.mem_channels[0].used); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates the constraints for the DUP and SWAP opcodes. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let n = lv.opcode_bits[0] - + lv.opcode_bits[1] * P::Scalar::from_canonical_u64(2) - + lv.opcode_bits[2] * P::Scalar::from_canonical_u64(4) - + lv.opcode_bits[3] * P::Scalar::from_canonical_u64(8); - - eval_packed_dup(n, lv, nv, yield_constr); - eval_packed_swap(n, lv, nv, yield_constr); - - // For both, disable the partial channel. - yield_constr.constraint(lv.op.dup_swap * lv.partial_channel.used); -} - -/// Circuit version of `eval_packed`. -/// Evaluates the constraints for the DUP and SWAP opcodes. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let n = lv.opcode_bits[..4].iter().enumerate().fold( - builder.zero_extension(), - |cumul, (i, &bit)| { - builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) - }, - ); - - eval_ext_circuit_dup(builder, n, lv, nv, yield_constr); - eval_ext_circuit_swap(builder, n, lv, nv, yield_constr); - - // For both, disable the partial channel. - { - let constr = builder.mul_extension(lv.op.dup_swap, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/gas.rs b/evm/src/cpu/gas.rs deleted file mode 100644 index 37097adcea..0000000000 --- a/evm/src/cpu/gas.rs +++ /dev/null @@ -1,324 +0,0 @@ -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use super::columns::COL_MAP; -use crate::cpu::columns::ops::OpsColumnsView; -use crate::cpu::columns::CpuColumnsView; - -const KERNEL_ONLY_INSTR: Option = Some(0); -const G_JUMPDEST: Option = Some(1); -const G_BASE: Option = Some(2); -const G_VERYLOW: Option = Some(3); -const G_LOW: Option = Some(5); -const G_MID: Option = Some(8); -const G_HIGH: Option = Some(10); - -const SIMPLE_OPCODES: OpsColumnsView> = OpsColumnsView { - binary_op: None, // This is handled manually below - ternary_op: None, // This is handled manually below - fp254_op: KERNEL_ONLY_INSTR, - eq_iszero: G_VERYLOW, - logic_op: G_VERYLOW, - not_pop: None, // This is handled manually below - shift: G_VERYLOW, - jumpdest_keccak_general: None, // This is handled manually below. - push_prover_input: None, // This is handled manually below. - jumps: None, // Combined flag handled separately. - pc_push0: G_BASE, - dup_swap: G_VERYLOW, - context_op: KERNEL_ONLY_INSTR, - m_op_32bytes: KERNEL_ONLY_INSTR, - exit_kernel: None, - m_op_general: KERNEL_ONLY_INSTR, - syscall: None, - exception: None, -}; - -fn eval_packed_accumulate( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // Is it an instruction that we constrain here? - // I.e., does it always cost a constant amount of gas? - let filter: P = SIMPLE_OPCODES - .into_iter() - .enumerate() - .filter_map(|(i, maybe_cost)| { - // Add flag `lv.op[i]` to the sum if `SIMPLE_OPCODES[i]` is `Some`. - maybe_cost.map(|_| lv.op[i]) - }) - .sum(); - - // How much gas did we use? - let gas_used: P = SIMPLE_OPCODES - .into_iter() - .enumerate() - .filter_map(|(i, maybe_cost)| { - maybe_cost.map(|cost| P::Scalar::from_canonical_u32(cost) * lv.op[i]) - }) - .sum(); - - let constr = nv.gas - (lv.gas + gas_used); - yield_constr.constraint_transition(filter * constr); - - let gas_diff = nv.gas - lv.gas; - - for (maybe_cost, op_flag) in izip!(SIMPLE_OPCODES.into_iter(), lv.op.into_iter()) { - if let Some(cost) = maybe_cost { - let cost = P::Scalar::from_canonical_u32(cost); - yield_constr.constraint_transition(op_flag * (gas_diff - cost)); - } - } - - // For jumps. - let jump_gas_cost = P::Scalar::from_canonical_u32(G_MID.unwrap()) - + lv.opcode_bits[0] * P::Scalar::from_canonical_u32(G_HIGH.unwrap() - G_MID.unwrap()); - yield_constr.constraint_transition(lv.op.jumps * (gas_diff - jump_gas_cost)); - - // For binary_ops. - // MUL, DIV and MOD are differentiated from ADD, SUB, LT, GT and BYTE by their first and fifth bits set to 0. - let cost_filter = lv.opcode_bits[0] + lv.opcode_bits[4] - lv.opcode_bits[0] * lv.opcode_bits[4]; - let binary_op_cost = P::Scalar::from_canonical_u32(G_LOW.unwrap()) - + cost_filter - * (P::Scalar::from_canonical_u32(G_VERYLOW.unwrap()) - - P::Scalar::from_canonical_u32(G_LOW.unwrap())); - yield_constr.constraint_transition(lv.op.binary_op * (gas_diff - binary_op_cost)); - - // For ternary_ops. - // SUBMOD is differentiated by its second bit set to 1. - let ternary_op_cost = P::Scalar::from_canonical_u32(G_MID.unwrap()) - - lv.opcode_bits[1] * P::Scalar::from_canonical_u32(G_MID.unwrap()); - yield_constr.constraint_transition(lv.op.ternary_op * (gas_diff - ternary_op_cost)); - - // For NOT and POP. - // NOT is differentiated from POP by its first bit set to 1. - let not_pop_cost = (P::ONES - lv.opcode_bits[0]) - * P::Scalar::from_canonical_u32(G_BASE.unwrap()) - + lv.opcode_bits[0] * P::Scalar::from_canonical_u32(G_VERYLOW.unwrap()); - yield_constr.constraint_transition(lv.op.not_pop * (gas_diff - not_pop_cost)); - - // For JUMPDEST and KECCAK_GENERAL. - // JUMPDEST is differentiated from KECCAK_GENERAL by its second bit set to 1. - let jumpdest_keccak_general_gas_cost = lv.opcode_bits[1] - * P::Scalar::from_canonical_u32(G_JUMPDEST.unwrap()) - + (P::ONES - lv.opcode_bits[1]) * P::Scalar::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()); - yield_constr.constraint_transition( - lv.op.jumpdest_keccak_general * (gas_diff - jumpdest_keccak_general_gas_cost), - ); - - // For PROVER_INPUT and PUSH operations. - // PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1. - let push_prover_input_gas_cost = lv.opcode_bits[5] - * P::Scalar::from_canonical_u32(G_VERYLOW.unwrap()) - + (P::ONES - lv.opcode_bits[5]) * P::Scalar::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()); - yield_constr - .constraint_transition(lv.op.push_prover_input * (gas_diff - push_prover_input_gas_cost)); -} - -fn eval_packed_init( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let is_cpu_cycle: P = COL_MAP.op.iter().map(|&col_i| lv[col_i]).sum(); - let is_cpu_cycle_next: P = COL_MAP.op.iter().map(|&col_i| nv[col_i]).sum(); - // `nv` is the first row that executes an instruction. - let filter = (is_cpu_cycle - P::ONES) * is_cpu_cycle_next; - // Set initial gas to zero. - yield_constr.constraint_transition(filter * nv.gas); -} - -/// Evaluate the gas constraints for the opcodes that cost a constant gas. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_accumulate(lv, nv, yield_constr); - eval_packed_init(lv, nv, yield_constr); -} - -fn eval_ext_circuit_accumulate, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // Is it an instruction that we constrain here? - // I.e., does it always cost a constant amount of gas? - let filter = SIMPLE_OPCODES.into_iter().enumerate().fold( - builder.zero_extension(), - |cumul, (i, maybe_cost)| { - // Add flag `lv.op[i]` to the sum if `SIMPLE_OPCODES[i]` is `Some`. - match maybe_cost { - None => cumul, - Some(_) => builder.add_extension(lv.op[i], cumul), - } - }, - ); - - // How much gas did we use? - let gas_used = SIMPLE_OPCODES.into_iter().enumerate().fold( - builder.zero_extension(), - |cumul, (i, maybe_cost)| match maybe_cost { - None => cumul, - Some(cost) => { - let cost_ext = builder.constant_extension(F::from_canonical_u32(cost).into()); - builder.mul_add_extension(lv.op[i], cost_ext, cumul) - } - }, - ); - - let constr = { - let t = builder.add_extension(lv.gas, gas_used); - builder.sub_extension(nv.gas, t) - }; - let filtered_constr = builder.mul_extension(filter, constr); - yield_constr.constraint_transition(builder, filtered_constr); - - for (maybe_cost, op_flag) in izip!(SIMPLE_OPCODES.into_iter(), lv.op.into_iter()) { - if let Some(cost) = maybe_cost { - let nv_lv_diff = builder.sub_extension(nv.gas, lv.gas); - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_u32(cost), - op_flag, - nv_lv_diff, - op_flag, - ); - yield_constr.constraint_transition(builder, constr); - } - } - - // For jumps. - let filter = lv.op.jumps; - let jump_gas_cost = builder.mul_const_extension( - F::from_canonical_u32(G_HIGH.unwrap() - G_MID.unwrap()), - lv.opcode_bits[0], - ); - let jump_gas_cost = - builder.add_const_extension(jump_gas_cost, F::from_canonical_u32(G_MID.unwrap())); - - let nv_lv_diff = builder.sub_extension(nv.gas, lv.gas); - let gas_diff = builder.sub_extension(nv_lv_diff, jump_gas_cost); - let constr = builder.mul_extension(filter, gas_diff); - yield_constr.constraint_transition(builder, constr); - - // For binary_ops. - // MUL, DIV and MOD are differentiated from ADD, SUB, LT, GT and BYTE by their first and fifth bits set to 0. - let filter = lv.op.binary_op; - let cost_filter = { - let a = builder.add_extension(lv.opcode_bits[0], lv.opcode_bits[4]); - let b = builder.mul_extension(lv.opcode_bits[0], lv.opcode_bits[4]); - builder.sub_extension(a, b) - }; - let binary_op_cost = builder.mul_const_extension( - F::from_canonical_u32(G_VERYLOW.unwrap()) - F::from_canonical_u32(G_LOW.unwrap()), - cost_filter, - ); - let binary_op_cost = - builder.add_const_extension(binary_op_cost, F::from_canonical_u32(G_LOW.unwrap())); - - let nv_lv_diff = builder.sub_extension(nv.gas, lv.gas); - let gas_diff = builder.sub_extension(nv_lv_diff, binary_op_cost); - let constr = builder.mul_extension(filter, gas_diff); - yield_constr.constraint_transition(builder, constr); - - // For ternary_ops. - // SUBMOD is differentiated by its second bit set to 1. - let filter = lv.op.ternary_op; - let ternary_op_cost = builder.mul_const_extension( - F::from_canonical_u32(G_MID.unwrap()).neg(), - lv.opcode_bits[1], - ); - let ternary_op_cost = - builder.add_const_extension(ternary_op_cost, F::from_canonical_u32(G_MID.unwrap())); - - let nv_lv_diff = builder.sub_extension(nv.gas, lv.gas); - let gas_diff = builder.sub_extension(nv_lv_diff, ternary_op_cost); - let constr = builder.mul_extension(filter, gas_diff); - yield_constr.constraint_transition(builder, constr); - - // For NOT and POP. - // NOT is differentiated from POP by its first bit set to 1. - let filter = lv.op.not_pop; - let one = builder.one_extension(); - let mut not_pop_cost = - builder.mul_const_extension(F::from_canonical_u32(G_VERYLOW.unwrap()), lv.opcode_bits[0]); - let mut pop_cost = builder.sub_extension(one, lv.opcode_bits[0]); - pop_cost = builder.mul_const_extension(F::from_canonical_u32(G_BASE.unwrap()), pop_cost); - not_pop_cost = builder.add_extension(not_pop_cost, pop_cost); - - let not_pop_gas_diff = builder.sub_extension(nv_lv_diff, not_pop_cost); - let not_pop_constr = builder.mul_extension(filter, not_pop_gas_diff); - yield_constr.constraint_transition(builder, not_pop_constr); - - // For JUMPDEST and KECCAK_GENERAL. - // JUMPDEST is differentiated from KECCAK_GENERAL by its second bit set to 1. - let one = builder.one_extension(); - let filter = lv.op.jumpdest_keccak_general; - - let jumpdest_keccak_general_gas_cost = builder.arithmetic_extension( - F::from_canonical_u32(G_JUMPDEST.unwrap()) - - F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), - F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), - lv.opcode_bits[1], - one, - one, - ); - - let gas_diff = builder.sub_extension(nv_lv_diff, jumpdest_keccak_general_gas_cost); - let constr = builder.mul_extension(filter, gas_diff); - - yield_constr.constraint_transition(builder, constr); - - // For PROVER_INPUT and PUSH operations. - // PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1. - let push_prover_input_gas_cost = builder.arithmetic_extension( - F::from_canonical_u32(G_VERYLOW.unwrap()) - - F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), - F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()), - lv.opcode_bits[5], - one, - one, - ); - let gas_diff = builder.sub_extension(nv_lv_diff, push_prover_input_gas_cost); - let constr = builder.mul_extension(lv.op.push_prover_input, gas_diff); - - yield_constr.constraint_transition(builder, constr); -} - -fn eval_ext_circuit_init, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // `nv` is the first row that executes an instruction. - let is_cpu_cycle = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| lv[col_i])); - let is_cpu_cycle_next = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| nv[col_i])); - let filter = builder.mul_sub_extension(is_cpu_cycle, is_cpu_cycle_next, is_cpu_cycle_next); - // Set initial gas to zero. - let constr = builder.mul_extension(filter, nv.gas); - yield_constr.constraint_transition(builder, constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluate the gas constraints for the opcodes that cost a constant gas. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // Evaluates the transition gas constraints. - eval_ext_circuit_accumulate(builder, lv, nv, yield_constr); - // Evaluates the initial gas constraints. - eval_ext_circuit_init(builder, lv, nv, yield_constr); -} diff --git a/evm/src/cpu/halt.rs b/evm/src/cpu/halt.rs deleted file mode 100644 index a04128608c..0000000000 --- a/evm/src/cpu/halt.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! Once the CPU execution is over (i.e. reached the `halt` label in the kernel), -//! the CPU trace will be padded with special dummy rows, incurring no memory overhead. - -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use super::control_flow::get_halt_pc; -use crate::cpu::columns::{CpuColumnsView, COL_MAP}; -use crate::cpu::membus::NUM_GP_CHANNELS; - -/// Evaluates constraints for the `halt` flag. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let is_cpu_cycle: P = COL_MAP.op.iter().map(|&col_i| lv[col_i]).sum(); - let is_cpu_cycle_next: P = COL_MAP.op.iter().map(|&col_i| nv[col_i]).sum(); - - let halt_state = P::ONES - is_cpu_cycle; - let next_halt_state = P::ONES - is_cpu_cycle_next; - - // The halt flag must be boolean. - yield_constr.constraint(halt_state * (halt_state - P::ONES)); - // Once we reach a padding row, there must be only padding rows. - yield_constr.constraint_transition(halt_state * (next_halt_state - P::ONES)); - // Check that we're in kernel mode. - yield_constr.constraint(halt_state * (lv.is_kernel_mode - P::ONES)); - - // Padding rows should have their memory channels disabled. - for i in 0..NUM_GP_CHANNELS { - let channel = lv.mem_channels[i]; - yield_constr.constraint(halt_state * channel.used); - } - - // The last row must be a dummy padding row. - yield_constr.constraint_last_row(halt_state - P::ONES); - - // Also, a padding row's `program_counter` must be at the `halt` label. - // In particular, it ensures that the first padding row may only be added - // after we jumped to the `halt` function. Subsequent padding rows may set - // the `program_counter` to arbitrary values (there's no transition - // constraints) so we can place this requirement on them too. - let halt_pc = get_halt_pc::(); - yield_constr.constraint(halt_state * (lv.program_counter - halt_pc)); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for the `halt` flag. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let one = builder.one_extension(); - - let is_cpu_cycle = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| lv[col_i])); - let is_cpu_cycle_next = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| nv[col_i])); - - let halt_state = builder.sub_extension(one, is_cpu_cycle); - let next_halt_state = builder.sub_extension(one, is_cpu_cycle_next); - - // The halt flag must be boolean. - let constr = builder.mul_sub_extension(halt_state, halt_state, halt_state); - yield_constr.constraint(builder, constr); - // Once we reach a padding row, there must be only padding rows. - let constr = builder.mul_sub_extension(halt_state, next_halt_state, halt_state); - yield_constr.constraint_transition(builder, constr); - // Check that we're in kernel mode. - let constr = builder.mul_sub_extension(halt_state, lv.is_kernel_mode, halt_state); - yield_constr.constraint(builder, constr); - - // Padding rows should have their memory channels disabled. - for i in 0..NUM_GP_CHANNELS { - let channel = lv.mem_channels[i]; - let constr = builder.mul_extension(halt_state, channel.used); - yield_constr.constraint(builder, constr); - } - - // The last row must be a dummy padding row. - { - let one = builder.one_extension(); - let constr = builder.sub_extension(halt_state, one); - yield_constr.constraint_last_row(builder, constr); - } - - // Also, a padding row's `program_counter` must be at the `halt` label. - // In particular, it ensures that the first padding row may only be added - // after we jumped to the `halt` function. Subsequent padding rows may set - // the `program_counter` to arbitrary values (there's no transition - // constraints) so we can place this requirement on them too. - { - let halt_pc = get_halt_pc(); - let halt_pc_target = builder.constant_extension(halt_pc); - let constr = builder.sub_extension(lv.program_counter, halt_pc_target); - let constr = builder.mul_extension(halt_state, constr); - - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/jumps.rs b/evm/src/cpu/jumps.rs deleted file mode 100644 index f3413b0f0a..0000000000 --- a/evm/src/cpu/jumps.rs +++ /dev/null @@ -1,390 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::memory::segments::Segment; - -/// Evaluates constraints for EXIT_KERNEL. -pub(crate) fn eval_packed_exit_kernel( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let input = lv.mem_channels[0].value; - let filter = lv.op.exit_kernel; - - // If we are executing `EXIT_KERNEL` then we simply restore the program counter, kernel mode - // flag, and gas counter. The middle 4 (32-bit) limbs are ignored (this is not part of the spec, - // but we trust the kernel to set them to zero). - yield_constr.constraint_transition(filter * (input[0] - nv.program_counter)); - yield_constr.constraint_transition(filter * (input[1] - nv.is_kernel_mode)); - yield_constr.constraint_transition(filter * (input[6] - nv.gas)); - // High limb of gas must be 0 for convenient detection of overflow. - yield_constr.constraint(filter * input[7]); -} - -/// Circuit version of `eval_packed_exit_kernel`. -/// Evaluates constraints for EXIT_KERNEL. -pub(crate) fn eval_ext_circuit_exit_kernel, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let input = lv.mem_channels[0].value; - let filter = lv.op.exit_kernel; - - // If we are executing `EXIT_KERNEL` then we simply restore the program counter and kernel mode - // flag. The top 6 (32-bit) limbs are ignored (this is not part of the spec, but we trust the - // kernel to set them to zero). - - let pc_constr = builder.sub_extension(input[0], nv.program_counter); - let pc_constr = builder.mul_extension(filter, pc_constr); - yield_constr.constraint_transition(builder, pc_constr); - - let kernel_constr = builder.sub_extension(input[1], nv.is_kernel_mode); - let kernel_constr = builder.mul_extension(filter, kernel_constr); - yield_constr.constraint_transition(builder, kernel_constr); - - { - let diff = builder.sub_extension(input[6], nv.gas); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - // High limb of gas must be 0 for convenient detection of overflow. - let constr = builder.mul_extension(filter, input[7]); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates constraints jump operations: JUMP and JUMPI. -pub(crate) fn eval_packed_jump_jumpi( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let jumps_lv = lv.general.jumps(); - let dst = lv.mem_channels[0].value; - let cond = lv.mem_channels[1].value; - let filter = lv.op.jumps; // `JUMP` or `JUMPI` - let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; - let is_jump = filter * (P::ONES - lv.opcode_bits[0]); - let is_jumpi = filter * lv.opcode_bits[0]; - - // Stack constraints. - // If (JUMP and stack_len != 1) or (JUMPI and stack_len != 2)... - let len_diff = lv.stack_len - P::ONES - lv.opcode_bits[0]; - let new_filter = len_diff * filter; - // Read an extra element. - let channel = nv.mem_channels[0]; - yield_constr.constraint_transition(new_filter * (channel.used - P::ONES)); - yield_constr.constraint_transition(new_filter * (channel.is_read - P::ONES)); - yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); - yield_constr.constraint_transition( - new_filter - * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - let addr_virtual = nv.stack_len - P::ONES; - yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); - // Constrain `stack_inv_aux`. - yield_constr.constraint( - filter * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - // Disable channel if stack_len == N. - let empty_stack_filter = filter * (lv.general.stack().stack_inv_aux - P::ONES); - yield_constr.constraint_transition(empty_stack_filter * channel.used); - - // If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1. - // In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`. - yield_constr.constraint(is_jump * (cond[0] - P::ONES)); - for &limb in &cond[1..] { - // Set all limbs (other than the least-significant limb) to 0. - // NB: Technically, they don't have to be 0, as long as the sum - // `cond[0] + ... + cond[7]` cannot overflow. - yield_constr.constraint(is_jump * limb); - } - - // Check `should_jump`: - yield_constr.constraint(filter * jumps_lv.should_jump * (jumps_lv.should_jump - P::ONES)); - let cond_sum: P = cond.into_iter().sum(); - yield_constr.constraint(filter * (jumps_lv.should_jump - P::ONES) * cond_sum); - yield_constr.constraint(filter * (jumps_lv.cond_sum_pinv * cond_sum - jumps_lv.should_jump)); - - // If we're jumping, then the high 7 limbs of the destination must be 0. - let dst_hi_sum: P = dst[1..].iter().copied().sum(); - yield_constr.constraint(filter * jumps_lv.should_jump * dst_hi_sum); - // Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint - // does not need to be conditioned on `should_jump` because no read takes place if we're not - // jumping, so we're free to set the channel to 1. - yield_constr.constraint(filter * (jumpdest_flag_channel.value[0] - P::ONES)); - - // Make sure that the JUMPDEST flag channel is constrained. - // Only need to read if we're about to jump and we're not in kernel mode. - yield_constr.constraint( - filter - * (jumpdest_flag_channel.used - jumps_lv.should_jump * (P::ONES - lv.is_kernel_mode)), - ); - yield_constr.constraint(filter * (jumpdest_flag_channel.is_read - P::ONES)); - yield_constr.constraint(filter * (jumpdest_flag_channel.addr_context - lv.context)); - yield_constr.constraint( - filter - * (jumpdest_flag_channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::JumpdestBits.unscale())), - ); - yield_constr.constraint(filter * (jumpdest_flag_channel.addr_virtual - dst[0])); - - // Disable unused memory channels - for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] { - yield_constr.constraint(filter * channel.used); - } - yield_constr.constraint(filter * lv.partial_channel.used); - - // Channel 1 is unused by the `JUMP` instruction. - yield_constr.constraint(is_jump * lv.mem_channels[1].used); - - // Update stack length. - yield_constr.constraint_transition(is_jump * (nv.stack_len - lv.stack_len + P::ONES)); - yield_constr.constraint_transition( - is_jumpi * (nv.stack_len - lv.stack_len + P::Scalar::from_canonical_u64(2)), - ); - - // Finally, set the next program counter. - let fallthrough_dst = lv.program_counter + P::ONES; - let jump_dest = dst[0]; - yield_constr.constraint_transition( - filter * (jumps_lv.should_jump - P::ONES) * (nv.program_counter - fallthrough_dst), - ); - yield_constr - .constraint_transition(filter * jumps_lv.should_jump * (nv.program_counter - jump_dest)); -} - -/// Circuit version of `eval_packed_jumpi_jumpi`. -/// Evaluates constraints jump operations: JUMP and JUMPI. -pub(crate) fn eval_ext_circuit_jump_jumpi, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let jumps_lv = lv.general.jumps(); - let dst = lv.mem_channels[0].value; - let cond = lv.mem_channels[1].value; - let filter = lv.op.jumps; // `JUMP` or `JUMPI` - let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; - let one_extension = builder.one_extension(); - let is_jump = builder.sub_extension(one_extension, lv.opcode_bits[0]); - let is_jump = builder.mul_extension(filter, is_jump); - let is_jumpi = builder.mul_extension(filter, lv.opcode_bits[0]); - - // Stack constraints. - // If (JUMP and stack_len != 1) or (JUMPI and stack_len != 2)... - let len_diff = builder.sub_extension(lv.stack_len, one_extension); - let len_diff = builder.sub_extension(len_diff, lv.opcode_bits[0]); - let new_filter = builder.mul_extension(len_diff, filter); - // Read an extra element. - let channel = nv.mem_channels[0]; - - { - let constr = builder.mul_sub_extension(new_filter, channel.used, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.mul_sub_extension(new_filter, channel.is_read, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_context, nv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::Stack.unscale()), - new_filter, - channel.addr_segment, - new_filter, - ); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_virtual, nv.stack_len); - let constr = builder.arithmetic_extension(F::ONE, F::ONE, new_filter, diff, new_filter); - yield_constr.constraint_transition(builder, constr); - } - // Constrain `stack_inv_aux`. - { - let prod = builder.mul_extension(len_diff, lv.general.stack().stack_inv); - let diff = builder.sub_extension(prod, lv.general.stack().stack_inv_aux); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - // Disable channel if stack_len == N. - { - let empty_stack_filter = - builder.mul_sub_extension(filter, lv.general.stack().stack_inv_aux, filter); - let constr = builder.mul_extension(empty_stack_filter, channel.used); - yield_constr.constraint_transition(builder, constr); - } - - // If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1. - // In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`. - { - let constr = builder.mul_sub_extension(is_jump, cond[0], is_jump); - yield_constr.constraint(builder, constr); - } - for &limb in &cond[1..] { - // Set all limbs (other than the least-significant limb) to 0. - // NB: Technically, they don't have to be 0, as long as the sum - // `cond[0] + ... + cond[7]` cannot overflow. - let constr = builder.mul_extension(is_jump, limb); - yield_constr.constraint(builder, constr); - } - - // Check `should_jump`: - { - let constr = builder.mul_sub_extension( - jumps_lv.should_jump, - jumps_lv.should_jump, - jumps_lv.should_jump, - ); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - let cond_sum = builder.add_many_extension(cond); - { - let constr = builder.mul_sub_extension(cond_sum, jumps_lv.should_jump, cond_sum); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - { - let constr = - builder.mul_sub_extension(jumps_lv.cond_sum_pinv, cond_sum, jumps_lv.should_jump); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - - // If we're jumping, then the high 7 limbs of the destination must be 0. - let dst_hi_sum = builder.add_many_extension(&dst[1..]); - { - let constr = builder.mul_extension(jumps_lv.should_jump, dst_hi_sum); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - // Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint - // does not need to be conditioned on `should_jump` because no read takes place if we're not - // jumping, so we're free to set the channel to 1. - { - let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.value[0], filter); - yield_constr.constraint(builder, constr); - } - - // Make sure that the JUMPDEST flag channel is constrained. - // Only need to read if we're about to jump and we're not in kernel mode. - { - let constr = builder.mul_sub_extension( - jumps_lv.should_jump, - lv.is_kernel_mode, - jumps_lv.should_jump, - ); - let constr = builder.add_extension(jumpdest_flag_channel.used, constr); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.is_read, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.sub_extension(jumpdest_flag_channel.addr_context, lv.context); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::JumpdestBits.unscale()), - filter, - jumpdest_flag_channel.addr_segment, - filter, - ); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.sub_extension(jumpdest_flag_channel.addr_virtual, dst[0]); - let constr = builder.mul_extension(filter, constr); - yield_constr.constraint(builder, constr); - } - - // Disable unused memory channels - for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] { - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(filter, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } - // Channel 1 is unused by the `JUMP` instruction. - { - let constr = builder.mul_extension(is_jump, lv.mem_channels[1].used); - yield_constr.constraint(builder, constr); - } - - // Update stack length. - { - let diff = builder.sub_extension(nv.stack_len, lv.stack_len); - let constr = builder.mul_add_extension(is_jump, diff, is_jump); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(nv.stack_len, lv.stack_len); - let diff = builder.add_const_extension(diff, F::TWO); - let constr = builder.mul_extension(is_jumpi, diff); - yield_constr.constraint_transition(builder, constr); - } - - // Finally, set the next program counter. - let fallthrough_dst = builder.add_const_extension(lv.program_counter, F::ONE); - let jump_dest = dst[0]; - { - let constr_a = builder.mul_sub_extension(filter, jumps_lv.should_jump, filter); - let constr_b = builder.sub_extension(nv.program_counter, fallthrough_dst); - let constr = builder.mul_extension(constr_a, constr_b); - yield_constr.constraint_transition(builder, constr); - } - { - let constr_a = builder.mul_extension(filter, jumps_lv.should_jump); - let constr_b = builder.sub_extension(nv.program_counter, jump_dest); - let constr = builder.mul_extension(constr_a, constr_b); - yield_constr.constraint_transition(builder, constr); - } -} - -/// Evaluates constraints for EXIT_KERNEL, JUMP and JUMPI. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_exit_kernel(lv, nv, yield_constr); - eval_packed_jump_jumpi(lv, nv, yield_constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for EXIT_KERNEL, JUMP and JUMPI. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - eval_ext_circuit_exit_kernel(builder, lv, nv, yield_constr); - eval_ext_circuit_jump_jumpi(builder, lv, nv, yield_constr); -} diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs deleted file mode 100644 index 6376552550..0000000000 --- a/evm/src/cpu/kernel/aggregator.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! Loads each kernel assembly file and concatenates them. - -use itertools::Itertools; -use once_cell::sync::Lazy; - -use super::assembler::{assemble, Kernel}; -use crate::cpu::kernel::constants::evm_constants; -use crate::cpu::kernel::parser::parse; - -pub static KERNEL: Lazy = Lazy::new(combined_kernel); - -pub(crate) fn combined_kernel() -> Kernel { - let files = vec![ - "global jumped_to_0: PANIC", - "global jumped_to_1: PANIC", - include_str!("asm/bignum/add.asm"), - include_str!("asm/bignum/addmul.asm"), - include_str!("asm/bignum/cmp.asm"), - include_str!("asm/bignum/isone.asm"), - include_str!("asm/bignum/iszero.asm"), - include_str!("asm/bignum/modexp.asm"), - include_str!("asm/bignum/modmul.asm"), - include_str!("asm/bignum/mul.asm"), - include_str!("asm/bignum/shr.asm"), - include_str!("asm/bignum/util.asm"), - include_str!("asm/core/call.asm"), - include_str!("asm/core/call_gas.asm"), - include_str!("asm/core/create.asm"), - include_str!("asm/core/create_addresses.asm"), - include_str!("asm/core/create_contract_account.asm"), - include_str!("asm/core/exception.asm"), - include_str!("asm/core/create_receipt.asm"), - include_str!("asm/core/gas.asm"), - include_str!("asm/core/intrinsic_gas.asm"), - include_str!("asm/core/jumpdest_analysis.asm"), - include_str!("asm/core/nonce.asm"), - include_str!("asm/core/process_txn.asm"), - include_str!("asm/core/syscall.asm"), - include_str!("asm/core/terminate.asm"), - include_str!("asm/core/transfer.asm"), - include_str!("asm/core/util.asm"), - include_str!("asm/core/access_lists.asm"), - include_str!("asm/core/log.asm"), - include_str!("asm/core/selfdestruct_list.asm"), - include_str!("asm/core/touched_addresses.asm"), - include_str!("asm/core/withdrawals.asm"), - include_str!("asm/core/precompiles/main.asm"), - include_str!("asm/core/precompiles/ecrec.asm"), - include_str!("asm/core/precompiles/sha256.asm"), - include_str!("asm/core/precompiles/rip160.asm"), - include_str!("asm/core/precompiles/id.asm"), - include_str!("asm/core/precompiles/expmod.asm"), - include_str!("asm/core/precompiles/bn_add.asm"), - include_str!("asm/core/precompiles/bn_mul.asm"), - include_str!("asm/core/precompiles/snarkv.asm"), - include_str!("asm/core/precompiles/blake2_f.asm"), - include_str!("asm/curve/bls381/util.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/final_exponent.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/glv.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/miller_loop.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/msm.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/pairing.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/precomputation.asm"), - include_str!("asm/curve/bn254/curve_arithmetic/twisted_curve.asm"), - include_str!("asm/curve/bn254/field_arithmetic/degree_6_mul.asm"), - include_str!("asm/curve/bn254/field_arithmetic/degree_12_mul.asm"), - include_str!("asm/curve/bn254/field_arithmetic/frobenius.asm"), - include_str!("asm/curve/bn254/field_arithmetic/inverse.asm"), - include_str!("asm/curve/bn254/field_arithmetic/util.asm"), - include_str!("asm/curve/common.asm"), - include_str!("asm/curve/secp256k1/curve_add.asm"), - include_str!("asm/curve/secp256k1/ecrecover.asm"), - include_str!("asm/curve/secp256k1/inverse_scalar.asm"), - include_str!("asm/curve/secp256k1/lift_x.asm"), - include_str!("asm/curve/secp256k1/moddiv.asm"), - include_str!("asm/curve/secp256k1/glv.asm"), - include_str!("asm/curve/secp256k1/precomputation.asm"), - include_str!("asm/curve/wnaf.asm"), - include_str!("asm/exp.asm"), - include_str!("asm/halt.asm"), - include_str!("asm/hash/blake2/addresses.asm"), - include_str!("asm/hash/blake2/blake2_f.asm"), - // include_str!("asm/hash/blake2/blake2b.asm"), - // include_str!("asm/hash/blake2/compression.asm"), - include_str!("asm/hash/blake2/g_functions.asm"), - include_str!("asm/hash/blake2/hash.asm"), - include_str!("asm/hash/blake2/iv.asm"), - include_str!("asm/hash/blake2/ops.asm"), - include_str!("asm/hash/blake2/permutations.asm"), - include_str!("asm/hash/ripemd/box.asm"), - include_str!("asm/hash/ripemd/compression.asm"), - include_str!("asm/hash/ripemd/constants.asm"), - include_str!("asm/hash/ripemd/functions.asm"), - include_str!("asm/hash/ripemd/main.asm"), - include_str!("asm/hash/ripemd/update.asm"), - include_str!("asm/hash/sha2/compression.asm"), - include_str!("asm/hash/sha2/constants.asm"), - include_str!("asm/hash/sha2/main.asm"), - include_str!("asm/hash/sha2/message_schedule.asm"), - include_str!("asm/hash/sha2/ops.asm"), - include_str!("asm/hash/sha2/temp_words.asm"), - include_str!("asm/hash/sha2/write_length.asm"), - include_str!("asm/main.asm"), - include_str!("asm/memory/core.asm"), - include_str!("asm/memory/memcpy.asm"), - include_str!("asm/memory/memset.asm"), - include_str!("asm/memory/metadata.asm"), - include_str!("asm/memory/packing.asm"), - include_str!("asm/memory/syscalls.asm"), - include_str!("asm/memory/txn_fields.asm"), - include_str!("asm/mpt/accounts.asm"), - include_str!("asm/mpt/delete/delete.asm"), - include_str!("asm/mpt/delete/delete_branch.asm"), - include_str!("asm/mpt/delete/delete_extension.asm"), - include_str!("asm/mpt/hash/hash.asm"), - include_str!("asm/mpt/hash/hash_trie_specific.asm"), - include_str!("asm/mpt/hex_prefix.asm"), - include_str!("asm/mpt/insert/insert.asm"), - include_str!("asm/mpt/insert/insert_extension.asm"), - include_str!("asm/mpt/insert/insert_leaf.asm"), - include_str!("asm/mpt/insert/insert_trie_specific.asm"), - include_str!("asm/mpt/read.asm"), - include_str!("asm/mpt/storage/storage_read.asm"), - include_str!("asm/mpt/storage/storage_write.asm"), - include_str!("asm/mpt/util.asm"), - include_str!("asm/rlp/decode.asm"), - include_str!("asm/rlp/encode.asm"), - include_str!("asm/rlp/encode_rlp_scalar.asm"), - include_str!("asm/rlp/encode_rlp_string.asm"), - include_str!("asm/rlp/increment_bounded_rlp.asm"), - include_str!("asm/rlp/num_bytes.asm"), - include_str!("asm/rlp/read_to_memory.asm"), - include_str!("asm/shift.asm"), - include_str!("asm/signed.asm"), - include_str!("asm/journal/journal.asm"), - include_str!("asm/journal/account_loaded.asm"), - include_str!("asm/journal/account_destroyed.asm"), - include_str!("asm/journal/account_touched.asm"), - include_str!("asm/journal/balance_transfer.asm"), - include_str!("asm/journal/nonce_change.asm"), - include_str!("asm/journal/storage_change.asm"), - include_str!("asm/journal/storage_loaded.asm"), - include_str!("asm/journal/code_change.asm"), - include_str!("asm/journal/refund.asm"), - include_str!("asm/journal/account_created.asm"), - include_str!("asm/journal/revert.asm"), - include_str!("asm/journal/log.asm"), - include_str!("asm/transactions/common_decoding.asm"), - include_str!("asm/transactions/router.asm"), - include_str!("asm/transactions/type_0.asm"), - include_str!("asm/transactions/type_1.asm"), - include_str!("asm/transactions/type_2.asm"), - include_str!("asm/util/assertions.asm"), - include_str!("asm/util/basic_macros.asm"), - include_str!("asm/util/keccak.asm"), - include_str!("asm/util/math.asm"), - include_str!("asm/account_code.asm"), - include_str!("asm/balance.asm"), - include_str!("asm/bloom_filter.asm"), - ]; - - let parsed_files = files.iter().map(|f| parse(f)).collect_vec(); - assemble(parsed_files, evm_constants(), true) -} - -#[cfg(test)] -mod tests { - use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; - use log::debug; - - use crate::cpu::kernel::aggregator::combined_kernel; - - #[test] - fn make_kernel() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug")); - - // Make sure we can parse and assemble the entire kernel. - let kernel = combined_kernel(); - debug!("Total kernel size: {} bytes", kernel.code.len()); - } -} diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm deleted file mode 100644 index 2654bedc7b..0000000000 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ /dev/null @@ -1,136 +0,0 @@ -global sys_extcodehash: - // stack: kexit_info, address - SWAP1 %u256_to_addr - // stack: address, kexit_info - SWAP1 - DUP2 %insert_accessed_addresses - // stack: cold_access, kexit_info, address - PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS - MUL - PUSH @GAS_WARMACCESS - ADD - %charge_gas - // stack: kexit_info, address - - SWAP1 - DUP1 %is_dead %jumpi(extcodehash_dead) - %extcodehash - // stack: hash, kexit_info - SWAP1 - EXIT_KERNEL -extcodehash_dead: - %stack (address, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -global extcodehash: - // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - DUP1 ISZERO %jumpi(retzero) - %add_const(3) - // stack: codehash_ptr, retdest - %mload_trie_data - // stack: codehash, retdest - SWAP1 JUMP -retzero: - %stack (account_ptr, retdest) -> (retdest, 0) - JUMP - -%macro extcodehash - %stack (address) -> (address, %%after) - %jump(extcodehash) -%%after: -%endmacro - -%macro ext_code_empty - %extcodehash - %eq_const(@EMPTY_STRING_HASH) -%endmacro - -%macro extcodesize - %stack (address) -> (address, %%after) - %jump(extcodesize) -%%after: -%endmacro - -global sys_extcodesize: - // stack: kexit_info, address - SWAP1 %u256_to_addr - // stack: address, kexit_info - SWAP1 - DUP2 %insert_accessed_addresses - // stack: cold_access, kexit_info, address - PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS - MUL - PUSH @GAS_WARMACCESS - ADD - %charge_gas - // stack: kexit_info, address - - SWAP1 - // stack: address, kexit_info - %extcodesize - // stack: code_size, kexit_info - SWAP1 - EXIT_KERNEL - -global extcodesize: - // stack: address, retdest - %next_context_id - // stack: codesize_ctx, address, retdest - SWAP1 - // stack: address, codesize_ctx, retdest - %jump(load_code) - -// Loads the code at `address` into memory, in the code segment of the given context, starting at offset 0. -// Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. -// Pre stack: address, ctx, retdest -// Post stack: code_size -// -// NOTE: The provided `dest` **MUST** have a virtual address of 0. -global load_code: - %stack (address, ctx, retdest) -> (extcodehash, address, load_code_ctd, ctx, retdest) - JUMP -load_code_ctd: - // stack: codehash, ctx, retdest - DUP1 ISZERO %jumpi(load_code_non_existent_account) - // Load the code non-deterministically in memory and return the length. - PROVER_INPUT(account_code) - %stack (code_size, codehash, ctx, retdest) -> (ctx, code_size, codehash, retdest, code_size) - // Check that the hash of the loaded code equals `codehash`. - // ctx == DST, as SEGMENT_CODE == offset == 0. - KECCAK_GENERAL - // stack: shouldbecodehash, codehash, retdest, code_size - %assert_eq - // stack: retdest, code_size - JUMP - -load_code_non_existent_account: - // Write 0 at address 0 for soundness: SEGMENT_CODE == 0, hence ctx == addr. - // stack: codehash, addr, retdest - %stack (codehash, addr, retdest) -> (0, addr, retdest, 0) - MSTORE_GENERAL - // stack: retdest, 0 - JUMP - -// Identical to load_code, but adds 33 zeros after code_size for soundness reasons. -// If the code ends with an incomplete PUSH, we must make sure that every subsequent read is 0, -// accordingly to the Ethereum specs. -// Pre stack: address, ctx, retdest -// Post stack: code_size -global load_code_padded: - %stack (address, ctx, retdest) -> (address, ctx, load_code_padded_ctd, ctx, retdest) - %jump(load_code) - -load_code_padded_ctd: - // SEGMENT_CODE == 0. - // stack: code_size, ctx, retdest - %stack (code_size, ctx, retdest) -> (ctx, code_size, 0, retdest, code_size) - ADD - // stack: addr, 0, retdest, code_size - MSTORE_32BYTES_32 - // stack: addr', retdest, code_size - PUSH 0 - MSTORE_GENERAL - // stack: retdest, code_size - JUMP diff --git a/evm/src/cpu/kernel/asm/balance.asm b/evm/src/cpu/kernel/asm/balance.asm deleted file mode 100644 index d39f660630..0000000000 --- a/evm/src/cpu/kernel/asm/balance.asm +++ /dev/null @@ -1,56 +0,0 @@ -global sys_balance: - // stack: kexit_info, address - SWAP1 %u256_to_addr - // stack: address, kexit_info - SWAP1 - DUP2 %insert_accessed_addresses - // stack: cold_access, kexit_info, address - PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS - MUL - PUSH @GAS_WARMACCESS - ADD - %charge_gas - // stack: kexit_info, address - - SWAP1 - // stack: address, kexit_info - %balance - // stack: balance, kexit_info - SWAP1 - EXIT_KERNEL - -%macro balance - %stack (address) -> (address, %%after) - %jump(balance) -%%after: -%endmacro - -global balance: - // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - DUP1 ISZERO %jumpi(retzero) // If the account pointer is null, return 0. - %add_const(1) - // stack: balance_ptr, retdest - %mload_trie_data - // stack: balance, retdest - SWAP1 JUMP - -retzero: - %stack (account_ptr, retdest) -> (retdest, 0) - JUMP - -global sys_selfbalance: - // stack: kexit_info - %charge_gas_const(@GAS_LOW) - %selfbalance - // stack: balance, kexit_info - SWAP1 - EXIT_KERNEL - -%macro selfbalance - PUSH %%after - %address - %jump(balance) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/bignum/add.asm b/evm/src/cpu/kernel/asm/bignum/add.asm deleted file mode 100644 index 4433ab2245..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/add.asm +++ /dev/null @@ -1,73 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Adds two bignums of the same given length. Assumes that len > 0. -// Replaces a with a + b, leaving b unchanged, and returns the final carry. -global add_bignum: - // stack: len, a_start_loc, b_start_loc, retdest - DUP1 - ISZERO - %jumpi(len_zero) - // stack: len, a_start_loc, b_start_loc, retdest - %build_current_general_address_no_offset - PUSH 0 - // stack: carry=0, base_addr, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, retdest -add_loop: - // stack: carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - DUP2 - // stack: base_addr, carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - DUP6 ADD // base_addr + b_cur_loc - MLOAD_GENERAL - // stack: b[cur], carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - DUP3 - DUP6 ADD // base_addr + a_cur_loc - MLOAD_GENERAL - // stack: a[cur], b[cur], carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - ADD - ADD - // stack: a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - DUP1 - // stack: a[cur] + b[cur] + carry, a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - %shr_const(128) - // stack: (a[cur] + b[cur] + carry) // 2^128, a[cur] + b[cur] + carry, base_addr, i, a_cur_loc, b_cur_loc, retdest - SWAP1 - // stack: a[cur] + b[cur] + carry, (a[cur] + b[cur] + carry) // 2^128, base_addr, i, a_cur_loc, b_cur_loc, retdest - %mod_const(0x100000000000000000000000000000000) - // stack: c[cur] = (a[cur] + b[cur] + carry) % 2^128, carry_new = (a[cur] + b[cur] + carry) // 2^128, base_addr, i, a_cur_loc, b_cur_loc, retdest - DUP3 - DUP6 - ADD // base_addr + a_cur_loc - // stack: a_cur_addr, c[cur], carry_new, base_addr, i, a_cur_loc, b_cur_loc, retdest - %swap_mstore - // stack: carry_new, base_addr, i, a_cur_loc, b_cur_loc, retdest - SWAP3 - %increment - SWAP3 - // stack: carry_new, base_addr, i, a_cur_loc + 1, b_cur_loc, retdest - SWAP4 - %increment - SWAP4 - // stack: carry_new, base_addr, i, a_cur_loc + 1, b_cur_loc + 1, retdest - SWAP2 - %decrement - SWAP2 - // stack: carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest - DUP3 - // stack: i - 1, carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest - %jumpi(add_loop) -add_end: - // stack: carry_new, base_addr, i - 1, a_cur_loc + 1, b_cur_loc + 1, retdest - %stack (c, addr, i, a, b) -> (c) - // stack: carry_new, retdest - SWAP1 - // stack: retdest, carry_new - JUMP - -len_zero: - // stack: len, a_start_loc, b_start_loc, retdest - %pop3 - // stack: retdest - PUSH 0 - // stack: carry=0, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/addmul.asm b/evm/src/cpu/kernel/asm/bignum/addmul.asm deleted file mode 100644 index 9cdf904e1f..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/addmul.asm +++ /dev/null @@ -1,116 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Sets a[0:len] += b[0:len] * val, and returns the carry (a limb of up to 128 bits). -global addmul_bignum: - // stack: len, a_start_loc, b_start_loc, val, retdest - DUP1 - // stack: len, len, a_start_loc, b_start_loc, val, retdest - ISZERO - %jumpi(len_zero) - %build_current_general_address_no_offset - PUSH 0 - // stack: carry_limb=0, base_addr, i=len, a_cur_loc=a_start_loc, b_cur_loc=b_start_loc, val, retdest -addmul_loop: - // stack: carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP2 - DUP6 ADD // base_addr + b_cur_loc - // stack: b_cur_addr, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - MLOAD_GENERAL - // stack: b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP7 - // stack: val, b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - MUL - // stack: val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP1 - // stack: val * b[cur], val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - %shr_const(128) - // stack: (val * b[cur]) // 2^128, val * b[cur], carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP1 - // stack: val * b[cur], (val * b[cur]) // 2^128, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - %shl_const(128) - %shr_const(128) - // stack: prod_lo = val * b[cur] % 2^128, prod_hi = (val * b[cur]) // 2^128, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP4 - DUP7 ADD // base_addr + a_cur_loc - // stack: a_cur_addr, prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - MLOAD_GENERAL - // stack: a[cur], prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP1 - // stack: a[cur], a[cur], prod_lo, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP2 - // stack: prod_lo, a[cur], a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - ADD - %shl_const(128) - %shr_const(128) - // stack: prod_lo' = (prod_lo + a[cur]) % 2^128, a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP1 - // stack: prod_lo', prod_lo', a[cur], prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP2 - // stack: a[cur], prod_lo', prod_lo', prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - GT - // stack: prod_lo_carry_limb = a[cur] > prod_lo', prod_lo', prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP1 - // stack: prod_lo', prod_lo_carry_limb, prod_hi, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP2 - // stack: prod_hi, prod_lo_carry_limb, prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - ADD - // stack: prod_hi' = prod_hi + prod_lo_carry_limb, prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP3 - // stack: carry_limb, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP3 - // stack: prod_lo', carry_limb, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - ADD - %shl_const(128) - %shr_const(128) - // stack: to_write = (prod_lo' + carry_limb) % 2^128, prod_hi', prod_lo', carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP2 - // stack: prod_lo', prod_hi', to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP3 - // stack: to_write, prod_lo', prod_hi', to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - LT - // stack: carry_limb_new = to_write < prod_lo', prod_hi', to_write, carry_limb, i, a_cur_loc, b_cur_loc, val, retdest - %stack (vals: 3, c) -> (vals) - // stack: carry_limb_new, prod_hi', to_write, addr, i, a_cur_loc, b_cur_loc, val, retdest - ADD - // stack: carry_limb = carry_limb_new' + prod_hi', to_write, addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP1 - // stack: to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - DUP3 - DUP6 ADD // base_addr + a_cur_loc - // stack: a_cur_addr, to_write, carry_limb, addr, i, a_cur_loc, b_cur_loc, val, retdest - %swap_mstore - // stack: carry_limb, base_addr, i, a_cur_loc, b_cur_loc, val, retdest - SWAP2 - // stack: i, base_addr, carry_limb, a_cur_loc, b_cur_loc, val, retdest - %decrement - // stack: i-1, base_addr, carry_limb, a_cur_loc, b_cur_loc, val, retdest - SWAP3 - // stack: a_cur_loc, base_addr, carry_limb, i-1, b_cur_loc, val, retdest - %increment - // stack: a_cur_loc+1, base_addr, carry_limb, i-1, b_cur_loc, val, retdest - SWAP4 - // stack: b_cur_loc, base_addr, carry_limb, i-1, a_cur_loc+1, val, retdest - %increment - // stack: b_cur_loc+1, base_addr, carry_limb, i-1, a_cur_loc+1, val, retdest - %stack (b, addr, c, i, a) -> (c, addr, i, a, b) - // stack: carry_limb, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest - DUP3 - // stack: i-1, carry_limb, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest - %jumpi(addmul_loop) -addmul_end: - // stack: carry_limb_new, base_addr, i-1, a_cur_loc+1, b_cur_loc+1, val, retdest - %stack (c, addr, i, a, b, v) -> (c) - // stack: carry_limb_new, retdest - SWAP1 - // stack: retdest, carry_limb_new - JUMP - -len_zero: - // stack: len, a_start_loc, b_start_loc, val, retdest - %pop4 - // stack: retdest - PUSH 0 - // stack: carry_limb=0, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/cmp.asm b/evm/src/cpu/kernel/asm/bignum/cmp.asm deleted file mode 100644 index c27687542e..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/cmp.asm +++ /dev/null @@ -1,93 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Compares two bignums of the same given length. Assumes that len > 0. -// Returns 1 if a > b, 0 if a == b, and -1 (that is, 2^256 - 1) if a < b. -global cmp_bignum: - // stack: len, a_start_loc, b_start_loc, retdest - %build_current_general_address_no_offset - // stack: base_addr, len, a_start_loc, b_start_loc, retdest - DUP2 - // stack: len, base_addr, len, a_start_loc, b_start_loc, retdest - ISZERO - %jumpi(equal) // len and base_addr are swapped, but they will be popped anyway - // stack: base_addr, len, a_start_loc, b_start_loc, retdest - SWAP2 - // stack: a_start_loc, len, base_addr, b_start_loc, retdest - PUSH 1 - DUP3 - SUB - // stack: len-1, a_start_loc, len, base_addr, b_start_loc, retdest - ADD - // stack: a_end_loc, len, base_addr, b_start_loc, retdest - SWAP3 - // stack: b_start_loc, len, base_addr, a_end_loc, retdest - PUSH 1 - DUP3 - SUB - // stack: len-1, b_start_loc, len, base_addr, a_end_loc, retdest - ADD - // stack: b_end_loc, len, base_addr, a_end_loc, retdest - - %stack (b, l, addr, a) -> (l, addr, a, b) - // stack: len, base_addr, a_end_loc, b_end_loc, retdest - %decrement -ge_loop: - // stack: i, base_addr, a_i_loc, b_i_loc, retdest - DUP4 - // stack: b_i_loc, i, base_addr, a_i_loc, b_i_loc, retdest - DUP3 ADD // b_i_addr - MLOAD_GENERAL - // stack: b[i], i, base_addr, a_i_loc, b_i_loc, retdest - DUP4 - // stack: a_i_loc, b[i], i, base_addr, a_i_loc, b_i_loc, retdest - DUP4 ADD // a_i_addr - MLOAD_GENERAL - // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest - %stack (vals: 2) -> (vals, vals) - GT - %jumpi(greater) - // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest - LT - %jumpi(less) - // stack: i, base_addr, a_i_loc, b_i_loc, retdest - DUP1 - ISZERO - %jumpi(equal) - %decrement - // stack: i-1, base_addr, a_i_loc, b_i_loc, retdest - SWAP2 - // stack: a_i_loc, base_addr, i-1, b_i_loc, retdest - %decrement - // stack: a_i_loc_new, base_addr, i-1, b_i_loc, retdest - SWAP3 - // stack: b_i_loc, base_addr, i-1, a_i_loc_new, retdest - %decrement - // stack: b_i_loc_new, base_addr, i-1, a_i_loc_new, retdest - %stack (b, addr, i, a) -> (i, addr, a, b) - // stack: i-1, base_addr, a_i_loc_new, b_i_loc_new, retdest - %jump(ge_loop) -equal: - // stack: i, base_addr, a_i_loc, b_i_loc, retdest - %pop4 - // stack: retdest - PUSH 0 - // stack: 0, retdest - SWAP1 - JUMP -greater: - // stack: a[i], b[i], i, base_addr, a_i_loc, b_i_loc, retdest - %pop6 - // stack: retdest - PUSH 1 - // stack: 1, retdest - SWAP1 - JUMP -less: - // stack: i, base_addr, a_i_loc, b_i_loc, retdest - %pop4 - // stack: retdest - PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - // stack: -1, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/isone.asm b/evm/src/cpu/kernel/asm/bignum/isone.asm deleted file mode 100644 index 7aaf32f451..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/isone.asm +++ /dev/null @@ -1,35 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -global isone_bignum: - // stack: len, start_loc, retdest - DUP1 - // stack: len, len, start_loc, retdest - ISZERO - %jumpi(eqzero) - // stack: len, start_loc, retdest - DUP2 - // stack: start_loc, len, start_loc, retdest - %mload_current_general - // stack: start_val, len, start_loc, retdest - %eq_const(1) - %jumpi(starts_with_one) - // Does not start with one, so not equal to one. - // stack: len, start_loc, retdest - %stack (vals: 2, retdest) -> (retdest, 0) - JUMP -eqzero: - // Is zero, so not equal to one. - // stack: cur_loc, end_loc, retdest - %stack (vals: 2, retdest) -> (retdest, 0) - // stack: retdest, 0 - JUMP -starts_with_one: - // Starts with one, so check that the remaining limbs are zero. - // stack: len, start_loc, retdest - %decrement - SWAP1 - %increment - SWAP1 - // stack: len-1, start_loc+1, retdest - %jump(iszero_bignum) diff --git a/evm/src/cpu/kernel/asm/bignum/iszero.asm b/evm/src/cpu/kernel/asm/bignum/iszero.asm deleted file mode 100644 index a6027b6116..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/iszero.asm +++ /dev/null @@ -1,40 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -global iszero_bignum: - // stack: len, start_loc, retdest - DUP1 - // stack: len, len, start_loc, retdest - ISZERO - %jumpi(eqzero) - DUP2 - // stack: start_loc, len, start_loc, retdest - ADD - // stack: end_loc, start_loc, retdest - SWAP1 - // stack: cur_loc=start_loc, end_loc, retdest -iszero_loop: - // stack: cur_loc, end_loc, retdest - DUP1 - // stack: cur_loc, cur_loc, end_loc, retdest - %mload_current_general - // stack: cur_val, cur_loc, end_loc, retdest - %jumpi(neqzero) - // stack: cur_loc, end_loc, retdest - %increment - // stack: cur_loc + 1, end_loc, retdest - %stack (vals: 2) -> (vals, vals) - // stack: cur_loc + 1, end_loc, cur_loc + 1, end_loc, retdest - EQ - %jumpi(eqzero) - %jump(iszero_loop) -neqzero: - // stack: cur_loc, end_loc, retdest - %stack (vals: 2, retdest) -> (retdest, 0) - // stack: retdest, 0 - JUMP -eqzero: - // stack: cur_loc, end_loc, retdest - %stack (vals: 2, retdest) -> (retdest, 1) - // stack: retdest, 1 - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/modexp.asm b/evm/src/cpu/kernel/asm/bignum/modexp.asm deleted file mode 100644 index f149e54dfc..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/modexp.asm +++ /dev/null @@ -1,192 +0,0 @@ -// Arithmetic on integers represented with 128-bit limbs. -// These integers are represented in LITTLE-ENDIAN form. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Stores b ^ e % m in output_loc, leaving b, e, and m unchanged. -// b, e, and m must have the same length. -// output_loc must have size length and be initialized with zeroes; scratch_1 must have size length. -// All of scratch_2..scratch_5 must have size 2 * length and be initialized with zeroes. -// Also, scratch_2..scratch_5 must be CONSECUTIVE in memory. -global modexp_bignum: - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // Special input cases: - - // (1) Modulus is zero (also covers len=0 case). - PUSH modulus_zero_return - // stack: modulus_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP5 - // stack: m_loc, modulus_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP3 - // stack: len, m_loc, modulus_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(iszero_bignum) -modulus_zero_return: - // stack: m==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jumpi(modulus_zero_or_one) - - // (2) Modulus is one. - PUSH modulus_one_return - // stack: modulus_one_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP5 - // stack: m_loc, modulus_one_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP3 - // stack: len, m_loc, modulus_one_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(isone_bignum) -modulus_one_return: - // stack: m==1, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jumpi(modulus_zero_or_one) - - // (3) Both b and e are zero. - PUSH b_zero_return - // stack: b_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP3 - // stack: b_loc, b_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP3 - // stack: len, b_loc, b_zero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(iszero_bignum) -b_zero_return: - // stack: b==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - PUSH e_zero_return - // stack: e_zero_return, b==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP5 - // stack: e_loc, e_zero_return, b==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - DUP4 - // stack: len, e_loc, e_zero_return, b==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(iszero_bignum) -e_zero_return: - // stack: e==0, b==0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - MUL // logical AND - %jumpi(b_and_e_zero) - - // End of special cases. - - // We store the repeated-squares accumulator x_i in scratch_1, starting with x_0 := b. - DUP1 - DUP3 - DUP8 - // stack: s1, b_loc, len, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %memcpy_current_general - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // We store the accumulated output value x_i in output_loc, starting with x_0=1. - PUSH 1 - DUP6 - // stack: out_loc, 1, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %mstore_current_general - -modexp_loop: - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // y := e % 2 - DUP3 - // stack: e_loc, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %mload_current_general - // stack: e_first, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %mod_const(2) - // stack: y = e_first % 2 = e % 2, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - ISZERO - // stack: y == 0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jumpi(modexp_y_0) - - // if y == 1, modular-multiply output_loc by scratch_1, using scratch_2..scratch_4 as scratch space, and store in scratch_5. - PUSH modexp_mul_return - DUP10 - DUP10 - DUP10 - DUP14 - DUP9 - DUP12 - DUP12 - DUP9 - // stack: len, out_loc, s1, m_loc, s5, s2, s3, s4, modexp_mul_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(modmul_bignum) -modexp_mul_return: - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // Copy scratch_5 to output_loc. - DUP1 - DUP11 - DUP7 - // stack: out_loc, s5, len, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %memcpy_current_general - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // Zero out scratch_2..scratch_5. - DUP1 - %mul_const(8) - DUP8 - // stack: s2, 8 * len, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %clear_current_general - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - -modexp_y_0: - // if y == 0, do nothing - - // Modular-square repeated-squares accumulator x_i (in scratch_1), using scratch_2..scratch_4 as scratch space, and store in scratch_5. - PUSH modexp_square_return - DUP10 - DUP10 - DUP10 - DUP14 - DUP9 - DUP12 - DUP1 - DUP9 - // stack: len, s1, s1, m_loc, s5, s2, s3, s4, modexp_square_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(modmul_bignum) - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - -modexp_square_return: - // Copy scratch_5 to scratch_1. - DUP1 - DUP11 - DUP8 - // stack: s1, s5, len, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %memcpy_current_general - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // Zero out scratch_2..scratch_5. - DUP1 - %mul_const(8) - DUP8 - // stack: s2, 8 * len, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %clear_current_general - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // e //= 2 (with shr_bignum) - - PUSH modexp_shr_return - DUP4 - DUP3 - // stack: len, e_loc, modexp_shr_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(shr_bignum) -modexp_shr_return: - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - - // check if e == 0 (with iszero_bignum) - PUSH modexp_iszero_return - DUP4 - DUP3 - // stack: len, e_loc, modexp_iszero_return, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jump(iszero_bignum) -modexp_iszero_return: - // stack: e == 0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - ISZERO - // stack: e != 0, len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %jumpi(modexp_loop) -// end of modexp_loop -modulus_zero_or_one: - // If modulus is zero or one, return 0. - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - %pop10 - // stack: retdest - JUMP -b_and_e_zero: - // If base and exponent are zero (and modulus > 1), return 1. - // stack: len, b_loc, e_loc, m_loc, out_loc, s1, s2, s3, s4, s5, retdest - PUSH 1 - DUP6 - %mstore_current_general - %pop10 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/modmul.asm b/evm/src/cpu/kernel/asm/bignum/modmul.asm deleted file mode 100644 index 9735f6108d..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/modmul.asm +++ /dev/null @@ -1,178 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Stores a * b % m in output_loc, leaving a, b, and m unchanged. -// a, b, and m must have the same length. -// output_loc must have size length; scratch_2 must have size 2*length. -// Both scratch_2 and scratch_3 have size 2*length and be initialized with zeroes. - -// The prover provides x := (a * b) % m, which is the output of this function. -// We first check that x < m. -// The prover also provides k := (a * b) / m, stored in scratch space. -// We then check that x + k * m = a * b, by computing both of those using -// bignum arithmetic, storing the results in scratch space. -// We assert equality between those two, limb by limb. -global modmul_bignum: - // stack: len, a_loc, b_loc, m_loc, out_loc, s1 (=scratch_1), s2, s3, retdest - DUP1 - ISZERO - %jumpi(len_zero) - - // STEP 1: - // The prover provides x := (a * b) % m, which we store in output_loc. - - %build_current_general_address_no_offset - - PUSH 0 - // stack: i=0, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest -modmul_remainder_loop: - // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - PROVER_INPUT(bignum_modmul) - // stack: PI, i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP8 - DUP3 - ADD - // stack: out_loc[i], PI, i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP4 ADD // out_addr_i - %swap_mstore - // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %increment - DUP3 - DUP2 - // stack: i+1, len, i+1, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - SUB // functions as NEQ - // stack: i+1!=len, i+1, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %jumpi(modmul_remainder_loop) -// end of modmul_remainder_loop - // stack: i, base_addr, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %pop2 - // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - - // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - - // STEP 2: - // We check that x < m. - - PUSH modmul_return_1 - DUP6 - DUP6 - DUP4 - // stack: len, m_loc, out_loc, modmul_return_1, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - // Should return 1 iff the value at m_loc > the value at out_loc; in other words, if x < m. - %jump(cmp_bignum) -modmul_return_1: - // stack: cmp_result, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - PUSH 1 - %assert_eq - - // STEP 3: - // The prover provides k := (a * b) / m, which we store in scratch_1. - - // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP1 - // stack: len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %mul_const(2) - // stack: 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - - %build_current_general_address_no_offset - - PUSH 0 - // stack: i=0, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest -modmul_quotient_loop: - // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - PROVER_INPUT(bignum_modmul) - // stack: PI, i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP10 - DUP3 - ADD - // stack: s1[i], PI, i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - DUP4 ADD // s1_addr_i - %swap_mstore - // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %increment - DUP3 - DUP2 - // stack: i+1, 2*len, i+1, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - SUB // functions as NEQ - // stack: i+1!=2*len, i+1, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %jumpi(modmul_quotient_loop) -// end of modmul_quotient_loop - // stack: i, base_addr, 2*len, len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %pop3 - // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - - // STEP 4: - // We calculate x + k * m. - - // STEP 4.1: - // Multiply k with m and store k * m in scratch_2. - PUSH modmul_return_2 - %stack (return, len, a, b, m, out, s1, s2) -> (len, s1, m, s2, return, len, a, b, out, s2) - // stack: len, s1, m_loc, s2, modmul_return_2, len, a_loc, b_loc, out_loc, s2, s3, retdest - %jump(mul_bignum) -modmul_return_2: - // stack: len, a_loc, b_loc, out_loc, s2, s3, retdest - - // STEP 4.2: - // Add x into k * m (in scratch_2). - PUSH modmul_return_3 - %stack (return, len, a, b, out, s2) -> (len, s2, out, return, len, a, b, s2) - // stack: len, s2, out_loc, modmul_return_3, len, a_loc, b_loc, s2, s3, retdest - %jump(add_bignum) -modmul_return_3: - // stack: carry, len, a_loc, b_loc, s2, s3, retdest - POP - // stack: len, a_loc, b_loc, s2, s3, retdest - - // STEP 5: - // We calculate a * b. - - // Multiply a with b and store a * b in scratch_3. - PUSH modmul_return_4 - %stack (return, len, a, b, s2, s3) -> (len, a, b, s3, return, len, s2, s3) - // stack: len, a_loc, b_loc, s3, modmul_return_4, len, s2, s3, retdest - %jump(mul_bignum) -modmul_return_4: - // stack: len, s2, s3, retdest - - // STEP 6: - // Check that x + k * m = a * b. - - %build_current_general_address_no_offset - // stack: base_addr, n=len, i=s2, j=s3, retdest -modmul_check_loop: - // stack: base_addr, n, i, j, retdest - %stack (addr, l, i, j) -> (j, i, addr, addr, l, i, j) - // stack: j, i, base_addr, base_addr, n, i, j, retdest - DUP3 ADD // addr_j - MLOAD_GENERAL - // stack: mem[j], i, base_addr, base_addr, n, i, j, retdest - SWAP2 - ADD // addr_i - MLOAD_GENERAL - // stack: mem[i], mem[j], base_addr, n, i, j, retdest - %assert_eq - // stack: base_addr, n, i, j, retdest - SWAP1 - %decrement - // stack: n-1, base_addr, i, j, retdest - SWAP2 - %increment - // stack: i+1, base_addr, n-1, j, retdest - SWAP3 - %increment - // stack: j+1, base_addr, n-1, i+1, retdest - %stack (j, addr, n, i) -> (n, addr, n, i, j) - // stack: n-1, base_addr, n-1, i+1, j+1, retdest - %jumpi(modmul_check_loop) -// end of modmul_check_loop - // stack: base_addr, n-1, i+1, j+1, retdest - %pop4 - // stack: retdest - JUMP - -len_zero: - // stack: len, a_loc, b_loc, m_loc, out_loc, s1, s2, s3, retdest - %pop8 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/mul.asm b/evm/src/cpu/kernel/asm/bignum/mul.asm deleted file mode 100644 index b3269f73a9..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/mul.asm +++ /dev/null @@ -1,70 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Stores a * b in output_loc, leaving a and b unchanged. -// Both a and b have length len; a * b will have length 2 * len. -// output_loc must be initialized as 2 * len zeroes. -// TODO: possible optimization: allow output_loc to be uninitialized, and write over it with a[0:len] * b[0] (a multiplication -// with carry) in place of the first addmul. -global mul_bignum: - // stack: len, a_start_loc, b_start_loc, output_loc, retdest - DUP1 - // stack: len, len, a_start_loc, b_start_loc, output_loc, retdest - ISZERO - %jumpi(len_zero) - - %build_current_general_address_no_offset - - DUP2 - // stack: n=len, base_addr, len, a_start_loc, bi=b_start_loc, output_cur=output_loc, retdest -mul_loop: - // stack: n, base_addr, len, a_start_loc, bi, output_cur, retdest - PUSH mul_addmul_return - // stack: mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP6 - // stack: bi, mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP4 ADD // bi_addr - MLOAD_GENERAL - // stack: b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP6 - // stack: a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP9 - // stack: output_loc, a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP7 - // stack: len, output_loc, a_start_loc, b[i], mul_addmul_return, n, base_addr, len, a_start_loc, bi, output_cur, retdest - %jump(addmul_bignum) -mul_addmul_return: - // stack: carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP7 - // stack: output_cur, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP5 - // stack: len, output_cur, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest - ADD - // stack: output_cur + len, carry_limb, n, base_addr, len, a_start_loc, bi, output_cur, retdest - DUP4 ADD - %swap_mstore - // stack: n, base_addr, len, a_start_loc, bi, output_cur, retdest - %decrement - // stack: n-1, base_addr, len, a_start_loc, bi, output_cur, retdest - SWAP4 - %increment - SWAP4 - // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur, retdest - SWAP5 - %increment - SWAP5 - // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest - DUP1 - // stack: n-1, n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest - %jumpi(mul_loop) -mul_end: - // stack: n-1, base_addr, len, a_start_loc, bi+1, output_cur+1, retdest - %pop6 - // stack: retdest - JUMP - -len_zero: - // stack: len, a_start_loc, b_start_loc, output_loc, retdest - %pop4 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/shr.asm b/evm/src/cpu/kernel/asm/bignum/shr.asm deleted file mode 100644 index 88d08f05f2..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/shr.asm +++ /dev/null @@ -1,74 +0,0 @@ -// Arithmetic on little-endian integers represented with 128-bit limbs. -// All integers must be under a given length bound, and are padded with leading zeroes. - -// Shifts a given bignum right by one bit (in place). -// Assumes that len > 0. -global shr_bignum: - // stack: len, start_loc, retdest - DUP1 - // stack: len, len, start_loc, retdest - ISZERO - %jumpi(len_zero) - // stack: len, start_loc, retdest - DUP2 - // stack: start_loc, len, start_loc, retdest - ADD - // stack: start_loc + len, start_loc, retdest - %decrement - // stack: end_loc, start_loc, retdest - - %build_current_general_address_no_offset - - // stack: base_addr, end_loc, start_loc, retdest - %stack (addr, e) -> (e, addr, 0) - // stack: i=end_loc, base_addr, carry=0, start_loc, retdest -shr_loop: - // stack: i, base_addr, carry, start_loc, retdest - DUP1 - // stack: i, i, base_addr, carry, start_loc, retdest - DUP3 ADD // addr_i - MLOAD_GENERAL - // stack: a[i], i, base_addr, carry, start_loc, retdest - DUP1 - // stack: a[i], a[i], i, base_addr, carry, start_loc, retdest - %shr_const(1) - // stack: a[i] >> 1, a[i], i, base_addr, carry, start_loc, retdest - SWAP1 - // stack: a[i], a[i] >> 1, i, base_addr, carry, start_loc, retdest - %mod_const(2) - // stack: new_carry = a[i] % 2, a[i] >> 1, i, base_addr, carry, start_loc, retdest - SWAP4 - // stack: carry, a[i] >> 1, i, base_addr, new_carry, start_loc, retdest - %shl_const(127) - // stack: carry << 127, a[i] >> 1, i, base_addr, new_carry, start_loc, retdest - ADD - // stack: carry << 127 | a[i] >> 1, i, base_addr, new_carry, start_loc, retdest - DUP2 - // stack: i, carry << 127 | a[i] >> 1, i, base_addr, new_carry, start_loc, retdest - DUP4 ADD // addr_i - %swap_mstore - // stack: i, base_addr, new_carry, start_loc, retdest - PUSH 1 - DUP2 - SUB - // stack: i-1, i, base_addr, new_carry, start_loc, retdest - SWAP1 - // stack: i, i-1, base_addr, new_carry, start_loc, retdest - DUP5 - // stack: start_loc, i, i-1, base_addr, new_carry, start_loc, retdest - EQ - // stack: i == start_loc, i-1, base_addr, new_carry, start_loc, retdest - ISZERO - // stack: i != start_loc, i-1, base_addr, new_carry, start_loc, retdest - %jumpi(shr_loop) -shr_end: - // stack: i, base_addr, new_carry, start_loc, retdest - %pop4 - // stack: retdest - JUMP - -len_zero: - // stack: len, start_loc, retdest - %pop2 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/util.asm b/evm/src/cpu/kernel/asm/bignum/util.asm deleted file mode 100644 index f0a1563450..0000000000 --- a/evm/src/cpu/kernel/asm/bignum/util.asm +++ /dev/null @@ -1,21 +0,0 @@ -%macro memcpy_current_general - // stack: dst, src, len - // DST and SRC are offsets, for the same memory segment - %build_current_general_address_no_offset - %stack (addr_no_offset, dst, src, len) -> (addr_no_offset, src, addr_no_offset, dst, len, %%after) - ADD - // stack: SRC, addr_no_offset, dst, len, %%after - SWAP2 - ADD - // stack: DST, SRC, len, %%after - %jump(memcpy) -%%after: -%endmacro - -%macro clear_current_general - // stack: dst, len - %build_current_general_address - %stack (DST, len) -> (DST, len, %%after) - %jump(memset) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/bloom_filter.asm b/evm/src/cpu/kernel/asm/bloom_filter.asm deleted file mode 100644 index 35a4ebd763..0000000000 --- a/evm/src/cpu/kernel/asm/bloom_filter.asm +++ /dev/null @@ -1,166 +0,0 @@ -/// Implementation of Bloom filters for logs. - -// Adds a Bloom entry to the transaction Bloom filter and the block Bloom filter. -// -// This is calculated by taking the least significant 11 bits from -// the first 3 16-bit bytes of the keccak_256 hash of bloom_entry. -add_to_bloom: - // stack: is_topic, bloom_entry, retdest - %compute_entry_hash - // stack: hash, retdest - DUP1 - // stack: hash, hash, retdest - %shr_const(240) - // stack: hahs_shft_240, hash, retdest - %bloom_byte_indices - // stack: byte_index, byte_bit_index, hash, retdest - %bloom_write_bit - // stack: hash, retdest - - // We shift the hash by 16 bits and repeat. - DUP1 %shr_const(224) - // stack: hash_shft_224, hash, retdest - %bloom_byte_indices - // stack: byte_index, byte_bit_index, hash, retdest - %bloom_write_bit - // stack: hash, retdest - - // We shift again the hash by 16 bits and repeat. - %shr_const(208) - // stack: hash_shft_208, retdest - %bloom_byte_indices - // stack: byte_index, byte_bit_index, retdest - %bloom_write_bit - // stack: retdest - JUMP - -// The LOGS segment is [log0_ptr, log1_ptr...]. logs_len is a global metadata for the number of logs. -// A log in the LOGS_DATA segment is [log_payload_len, address, num_topics, [topics], data_len, [data]]. -global logs_bloom: - // stack: retdest - %mload_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - // stack: logs_len, retdest - PUSH 0 - -logs_bloom_loop: - // stack: i, logs_len, retdest - DUP2 DUP2 EQ - // stack: i == logs_len, i, logs_len, retdest - %jumpi(logs_bloom_end) - // stack: i, logs_len, retdest - DUP1 - %mload_kernel(@SEGMENT_LOGS) - // stack: log_payload_len_ptr, i, logs_len, retdest - - // Add address to bloom filter. - %increment - // stack: addr_ptr, i, logs_len, retdest - PUSH @SEGMENT_LOGS_DATA %build_kernel_address - DUP1 - MLOAD_GENERAL - // stack: addr, full_addr_ptr, i, logs_len, retdest - PUSH 0 - // stack: is_topic, addr, full_addr_ptr, i, logs_len, retdest - %add_to_bloom - // stack: full_addr_ptr, i, logs_len, retdest - %increment - // stack: full_num_topics_ptr, i, logs_len, retdest - DUP1 - MLOAD_GENERAL - // stack: num_topics, full_num_topics_ptr, i, logs_len, retdest - SWAP1 %increment - // stack: full_topics_ptr, num_topics, i, logs_len, retdest - PUSH 0 - -logs_bloom_topic_loop: - // stack: j, topics_ptr, num_topics, i, logs_len, retdest - DUP3 DUP2 EQ - // stack: j == num_topics, j, topics_ptr, num_topics, i, logs_len, retdest - %jumpi(logs_bloom_topic_end) - DUP2 DUP2 ADD - // stack: curr_topic_ptr, j, topics_ptr, num_topics, i, logs_len, retdest - MLOAD_GENERAL - // stack: topic, j, topics_ptr, num_topics, i, logs_len, retdest - PUSH 1 - // stack: is_topic, topic, j, topics_ptr, num_topics, i, logs_len, retdest - %add_to_bloom - // stack: j, topics_ptr, num_topics, i, logs_len, retdest - %increment - %jump(logs_bloom_topic_loop) - -logs_bloom_topic_end: - // stack: num_topics, topics_ptr, num_topics, i, logs_len, retdest - %pop3 - %increment - %jump(logs_bloom_loop) - -logs_bloom_end: - // stack: logs_len, logs_len, retdest - %pop2 - JUMP - -%macro compute_entry_hash - // stack: is_topic, bloom_entry - ISZERO - %jumpi(%%compute_entry_hash_address) - // stack: bloom_entry - %keccak256_word(32) - // stack: topic_hash - %jump(%%after) - -%%compute_entry_hash_address: - // stack: bloom_entry - %keccak256_word(20) - // stack: address_hash - -%%after: -%endmacro - -%macro add_to_bloom - %stack (is_topic, bloom_entry) -> (is_topic, bloom_entry, %%after) - %jump(add_to_bloom) - -%%after: -%endmacro - -// Computes the byte index and bit index within to update the Bloom filter with. -// The hash value must be properly shifted prior calling this macro. -%macro bloom_byte_indices - // stack: hash - %and_const(0x07FF) - PUSH 0x07FF - SUB - // stack: bit_index - DUP1 - %and_const(0x7) - SWAP1 - %shr_const(0x3) - // stack: byte_index, byte_bit_index -%endmacro - - -// Updates the corresponding bloom filter byte with provided bit. -// Also updates the block bloom filter. -%macro bloom_write_bit - // stack: byte_index, byte_bit_index - PUSH @SEGMENT_TXN_BLOOM - %build_kernel_address - PUSH 1 - DUP3 - // stack: byte_bit_index, 1, byte_addr, byte_bit_index - PUSH 7 SUB - SHL - // Updates the current txn bloom filter. - SWAP2 POP DUP1 - MLOAD_GENERAL - // stack: old_bloom_byte, byte_addr, one_shifted_by_index - DUP3 OR - // stack: new_bloom_byte, byte_addr, one_shifted_by_index - MSTORE_GENERAL - // stack: one_shifted_by_index - POP - // stack: empty -%endmacro - - - diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm deleted file mode 100644 index 30afe27c41..0000000000 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ /dev/null @@ -1,203 +0,0 @@ -/// Access lists for addresses and storage keys. -/// The access list is stored in an array. The length of the array is stored in the global metadata. -/// For storage keys, the address and key are stored as two consecutive elements. -/// The array is stored in the SEGMENT_ACCESSED_ADDRESSES segment for addresses and in the SEGMENT_ACCESSED_STORAGE_KEYS segment for storage keys. -/// Both arrays are stored in the kernel memory (context=0). -/// Searching and inserting is done by doing a linear search through the array. -/// If the address/storage key isn't found in the array, it is inserted at the end. -/// TODO: Look into using a more efficient data structure for the access lists. - -%macro insert_accessed_addresses - %stack (addr) -> (addr, %%after) - %jump(insert_accessed_addresses) -%%after: - // stack: cold_access -%endmacro - -%macro insert_accessed_addresses_no_return - %insert_accessed_addresses - POP -%endmacro - -/// Inserts the address into the access list if it is not already present. -/// Return 1 if the address was inserted, 0 if it was already present. -global insert_accessed_addresses: - // stack: addr, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) - // stack: len, addr, retdest - PUSH @SEGMENT_ACCESSED_ADDRESSES ADD - PUSH @SEGMENT_ACCESSED_ADDRESSES -insert_accessed_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_ACCESSED_ADDRESSES - %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(insert_address) - // stack: i, len, addr, retdest - DUP1 - MLOAD_GENERAL - // stack: loaded_addr, i, len, addr, retdest - DUP4 - // stack: addr, loaded_addr, i, len, addr, retdest - EQ %jumpi(insert_accessed_addresses_found) - // stack: i, len, addr, retdest - %increment - %jump(insert_accessed_addresses_loop) - -insert_address: - %stack (i, len, addr, retdest) -> (i, addr, len, retdest) - DUP2 %journal_add_account_loaded // Add a journal entry for the loaded account. - %swap_mstore // Store new address at the end of the array. - // stack: len, retdest - %increment - %sub_const(@SEGMENT_ACCESSED_ADDRESSES) // unscale `len` - %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Store new length. - PUSH 1 // Return 1 to indicate that the address was inserted. - SWAP1 JUMP - -insert_accessed_addresses_found: - %stack (i, len, addr, retdest) -> (retdest, 0) // Return 0 to indicate that the address was already present. - JUMP - -/// Remove the address from the access list. -/// Panics if the address is not in the access list. -global remove_accessed_addresses: - // stack: addr, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) - // stack: len, addr, retdest - PUSH @SEGMENT_ACCESSED_ADDRESSES ADD - PUSH @SEGMENT_ACCESSED_ADDRESSES -remove_accessed_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_ACCESSED_ADDRESSES - %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(panic) - // stack: i, len, addr, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, addr, retdest - DUP4 - // stack: addr, loaded_addr, i, len, addr, retdest - EQ %jumpi(remove_accessed_addresses_found) - // stack: i, len, addr, retdest - %increment - %jump(remove_accessed_addresses_loop) -remove_accessed_addresses_found: - %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB // len -= 1 - PUSH @SEGMENT_ACCESSED_ADDRESSES - DUP2 SUB // unscale `len` - %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN) // Decrement the access list length. - // stack: len-1, i, retdest - MLOAD_GENERAL // Load the last address in the access list. - // stack: last_addr, i, retdest - MSTORE_GENERAL - // Store the last address at the position of the removed address. - JUMP - - -%macro insert_accessed_storage_keys - %stack (addr, key, value) -> (addr, key, value, %%after) - %jump(insert_accessed_storage_keys) -%%after: - // stack: cold_access, original_value -%endmacro - -/// Inserts the storage key and value into the access list if it is not already present. -/// `value` should be the current storage value at the slot `(addr, key)`. -/// Return `1, original_value` if the storage key was inserted, `0, original_value` if it was already present. -global insert_accessed_storage_keys: - // stack: addr, key, value, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) - // stack: len, addr, key, value, retdest - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS ADD - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS -insert_accessed_storage_keys_loop: - // `i` and `len` are both scaled by SEGMENT_ACCESSED_STORAGE_KEYS - %stack (i, len, addr, key, value, retdest) -> (i, len, i, len, addr, key, value, retdest) - EQ %jumpi(insert_storage_key) - // stack: i, len, addr, key, value, retdest - DUP1 %increment MLOAD_GENERAL - // stack: loaded_key, i, len, addr, key, value, retdest - DUP2 MLOAD_GENERAL - // stack: loaded_addr, loaded_key, i, len, addr, key, value, retdest - DUP5 EQ - // stack: loaded_addr==addr, loaded_key, i, len, addr, key, value, retdest - SWAP1 DUP6 EQ - // stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, value, retdest - MUL // AND - %jumpi(insert_accessed_storage_keys_found) - // stack: i, len, addr, key, value, retdest - %add_const(3) - %jump(insert_accessed_storage_keys_loop) - -insert_storage_key: - // stack: i, len, addr, key, value, retdest - DUP4 DUP4 %journal_add_storage_loaded // Add a journal entry for the loaded storage key. - // stack: i, len, addr, key, value, retdest - - %stack(dst, len, addr, key, value) -> (addr, dst, dst, key, dst, value, dst, @SEGMENT_ACCESSED_STORAGE_KEYS, value) - MSTORE_GENERAL // Store new address at the end of the array. - // stack: dst, key, dst, value, dst, segment, value, retdest - %increment SWAP1 - MSTORE_GENERAL // Store new key after that - // stack: dst, value, dst, segment, value, retdest - %add_const(2) SWAP1 - MSTORE_GENERAL // Store new value after that - // stack: dst, segment, value, retdest - %add_const(3) - SUB // unscale dst - %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length. - %stack (value, retdest) -> (retdest, 1, value) // Return 1 to indicate that the storage key was inserted. - JUMP - -insert_accessed_storage_keys_found: - // stack: i, len, addr, key, value, retdest - %add_const(2) - MLOAD_GENERAL - %stack (original_value, len, addr, key, value, retdest) -> (retdest, 0, original_value) // Return 0 to indicate that the storage key was already present. - JUMP - -/// Remove the storage key and its value from the access list. -/// Panics if the key is not in the list. -global remove_accessed_storage_keys: - // stack: addr, key, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) - // stack: len, addr, key, retdest - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS ADD - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS -remove_accessed_storage_keys_loop: - // `i` and `len` are both scaled by SEGMENT_ACCESSED_STORAGE_KEYS - %stack (i, len, addr, key, retdest) -> (i, len, i, len, addr, key, retdest) - EQ %jumpi(panic) - // stack: i, len, addr, key, retdest - DUP1 %increment MLOAD_GENERAL - // stack: loaded_key, i, len, addr, key, retdest - DUP2 MLOAD_GENERAL - // stack: loaded_addr, loaded_key, i, len, addr, key, retdest - DUP5 EQ - // stack: loaded_addr==addr, loaded_key, i, len, addr, key, retdest - SWAP1 DUP6 EQ - // stack: loaded_key==key, loaded_addr==addr, i, len, addr, key, retdest - MUL // AND - %jumpi(remove_accessed_storage_keys_found) - // stack: i, len, addr, key, retdest - %add_const(3) - %jump(remove_accessed_storage_keys_loop) - -remove_accessed_storage_keys_found: - %stack (i, len, addr, key, retdest) -> (len, 3, i, retdest) - SUB - PUSH @SEGMENT_ACCESSED_STORAGE_KEYS - DUP2 SUB // unscale - %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Decrease the access list length. - // stack: len-3, i, retdest - DUP1 %add_const(2) MLOAD_GENERAL - // stack: last_value, len-3, i, retdest - DUP2 %add_const(1) MLOAD_GENERAL - // stack: last_key, last_value, len-3, i, retdest - DUP3 MLOAD_GENERAL - // stack: last_addr, last_key, last_value, len-3, i, retdest - DUP5 %swap_mstore // Move the last tuple to the position of the removed tuple. - // stack: last_key, last_value, len-3, i, retdest - DUP4 %add_const(1) %swap_mstore - // stack: last_value, len-3, i, retdest - DUP3 %add_const(2) %swap_mstore - // stack: len-3, i, retdest - %pop2 JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm deleted file mode 100644 index b5b8935471..0000000000 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ /dev/null @@ -1,447 +0,0 @@ -// Handlers for call-like operations, namely CALL, CALLCODE, STATICCALL and DELEGATECALL. -// Reminder: All context metadata hardcoded offsets are already scaled by `Segment::ContextMetadata`. - -// Creates a new sub context and executes the code of the given account. -global sys_call: - // Check that the value is zero if the context is static. - // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size - DUP4 ISZERO %not_bit - // stack: value≠0, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size - %mload_context_metadata(@CTX_METADATA_STATIC) - // stack: is_static, value≠0, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size - MUL // Cheaper than AND - %jumpi(fault_exception) - - %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - - SWAP2 - // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size - %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses - - %call_charge_gas(1, 1) - %check_depth - - %checkpoint // Checkpoint - DUP3 %insert_touched_addresses - - %create_context - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %copy_mem_to_calldata - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP5 DUP5 %address %transfer_eth %jumpi(call_insufficient_balance) - DUP5 DUP5 %address %journal_add_balance_transfer - DUP3 %set_new_ctx_gas_limit - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP4 - // stack: address, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %handle_precompiles - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %set_new_ctx_parent_pc(after_call_instruction) - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - // Each line in the block below does not change the stack. - %set_static - DUP4 %set_new_ctx_addr - %address %set_new_ctx_caller - DUP5 %set_new_ctx_value - DUP4 %set_new_ctx_code - - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - -> (new_ctx, kexit_info, ret_offset, ret_size) - %enter_new_ctx - -// Creates a new sub context as if calling itself, but with the code of the -// given account. In particular the storage remains the same. -global sys_callcode: - - // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size - %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - - SWAP2 - // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size - %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses - - %call_charge_gas(1, 0) - %check_depth - - %checkpoint // Checkpoint - %address %insert_touched_addresses - - // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %create_context - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %copy_mem_to_calldata - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP5 %address %address %transfer_eth %jumpi(call_insufficient_balance) - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP3 %set_new_ctx_gas_limit - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP4 - // stack: address, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %handle_precompiles - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %set_new_ctx_parent_pc(after_call_instruction) - - // Each line in the block below does not change the stack. - %set_static - %address %set_new_ctx_addr - %address %set_new_ctx_caller - DUP5 %set_new_ctx_value - DUP4 %set_new_ctx_code - - - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - -> (new_ctx, kexit_info, ret_offset, ret_size) - %enter_new_ctx - -// Creates a new sub context and executes the code of the given account. -// Equivalent to CALL, except that it does not allow any state modifying -// instructions or sending ETH in the sub context. The disallowed instructions -// are CREATE, CREATE2, LOG0, LOG1, LOG2, LOG3, LOG4, SSTORE, SELFDESTRUCT and -// CALL if the value sent is not 0. -global sys_staticcall: - // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size - %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - - SWAP2 - // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size - %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses - - // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. - %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) - %call_charge_gas(0, 1) - %check_depth - - %checkpoint // Checkpoint - DUP3 %insert_touched_addresses - - // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %create_context - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %copy_mem_to_calldata - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP3 %set_new_ctx_gas_limit - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP4 - // stack: address, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %handle_precompiles - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %set_new_ctx_parent_pc(after_call_instruction) - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - // Each line in the block below does not change the stack. - %set_static_true - DUP4 %set_new_ctx_addr - %address %set_new_ctx_caller - PUSH 0 %set_new_ctx_value - DUP4 %set_new_ctx_code - - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - -> (new_ctx, kexit_info, ret_offset, ret_size) - %enter_new_ctx - -// Creates a new sub context as if calling itself, but with the code of the -// given account. In particular the storage, the current sender and the current -// value remain the same. -global sys_delegatecall: - - // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size - %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> - (args_size, args_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - %stack (kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) -> - (ret_size, ret_offset, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) - %checked_mem_expansion - - SWAP2 - // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size - %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses - - // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. - %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) - %call_charge_gas(0, 0) - %check_depth - - %checkpoint // Checkpoint - %address %insert_touched_addresses - - // stack: kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %create_context - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %copy_mem_to_calldata - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP3 %set_new_ctx_gas_limit - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - DUP4 - // stack: address, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %handle_precompiles - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - %set_new_ctx_parent_pc(after_call_instruction) - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size - - // Each line in the block below does not change the stack. - %set_static - %address %set_new_ctx_addr - %caller %set_new_ctx_caller - %callvalue %set_new_ctx_value - %set_new_ctx_parent_pc(after_call_instruction) - DUP4 %set_new_ctx_code - - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - -> (new_ctx, kexit_info, ret_offset, ret_size) - %enter_new_ctx - -// We go here after any CALL type instruction (but not after the special call by the transaction originator). -global after_call_instruction: - // stack: success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size - DUP1 ISZERO %jumpi(after_call_instruction_failed) - %pop_checkpoint -after_call_instruction_contd: - SWAP3 - // stack: kexit_info, leftover_gas, new_ctx, success, ret_offset, ret_size - // Add the leftover gas into the appropriate bits of kexit_info. - SWAP1 %shl_const(192) SWAP1 SUB - // stack: kexit_info, new_ctx, success, ret_offset, ret_size - - // The callee's terminal instruction will have populated RETURNDATA. - %copy_returndata_to_mem - EXIT_KERNEL - -after_call_instruction_failed: - // stack: success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size - %revert_checkpoint - %jump(after_call_instruction_contd) - -call_insufficient_balance: - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (callgas, kexit_info, 0) - %shl_const(192) SWAP1 SUB - // stack: kexit_info', 0 - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - EXIT_KERNEL - -%macro check_depth - %call_depth - %gt_const(@CALL_STACK_LIMIT) - %jumpi(call_too_deep) -%endmacro - -call_too_deep: - %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (callgas, kexit_info, 0) - %shl_const(192) SWAP1 SUB - // stack: kexit_info', 0 - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - EXIT_KERNEL - -// Set @CTX_METADATA_STATIC to 1. Note that there is no corresponding set_static_false routine -// because it will already be 0 by default. -%macro set_static_true - // stack: new_ctx - DUP1 - %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) - PUSH 1 - // stack: 1, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -// Set @CTX_METADATA_STATIC of the next context to the current value. -%macro set_static - // stack: new_ctx - DUP1 - %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) - %mload_context_metadata(@CTX_METADATA_STATIC) - // stack: is_static, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_addr - // stack: called_addr, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_ADDRESS) - SWAP1 - // stack: called_addr, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_caller - // stack: sender, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_CALLER) - SWAP1 - // stack: sender, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_value - // stack: value, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_CALL_VALUE) - SWAP1 - // stack: value, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_code_size - // stack: code_size, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_CODE_SIZE) - SWAP1 - // stack: code_size, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_calldata_size - // stack: calldata_size, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) - SWAP1 - // stack: calldata_size, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_gas_limit - // stack: gas_limit, new_ctx - DUP2 - %build_address_with_ctx_no_segment(@CTX_METADATA_GAS_LIMIT) - SWAP1 - // stack: gas_limit, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_parent_ctx - // stack: new_ctx - DUP1 - %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_CONTEXT) - GET_CONTEXT - // stack: ctx, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_parent_pc(label) - // stack: new_ctx - DUP1 - %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_PC) - PUSH $label - // stack: label, addr, new_ctx - MSTORE_GENERAL - // stack: new_ctx -%endmacro - -%macro set_new_ctx_code - %stack (address, new_ctx) -> (address, new_ctx, %%after, new_ctx) - %jump(load_code_padded) -%%after: - %set_new_ctx_code_size - // stack: new_ctx -%endmacro - -%macro enter_new_ctx - // stack: new_ctx - // Switch to the new context and go to usermode with PC=0. - DUP1 // new_ctx - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // Perform jumpdest analyis - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - GET_CONTEXT - // stack: ctx, code_size, retdest - %jumpdest_analysis - PUSH 0 // jump dest - EXIT_KERNEL - // (Old context) stack: new_ctx -%endmacro - -%macro copy_mem_to_calldata - // stack: new_ctx, args_offset, args_size - GET_CONTEXT - %stack(ctx, new_ctx, args_offset, args_size) -> (ctx, @SEGMENT_MAIN_MEMORY, args_offset, args_size, %%after, new_ctx, args_size) - %build_address - // stack: SRC, args_size, %%after, new_ctx, args_size - DUP4 - %build_address_with_ctx_no_offset(@SEGMENT_CALLDATA) - // stack: DST, SRC, args_size, %%after, new_ctx, args_size - %jump(memcpy_bytes) -%%after: - // stack: new_ctx, args_size - %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) - // stack: addr, args_size - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro copy_returndata_to_mem - // stack: kexit_info, new_ctx, success, ret_offset, ret_size - SWAP4 - %returndatasize - // stack: returndata_size, ret_size, new_ctx, success, ret_offset, kexit_info - %min - GET_CONTEXT - %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> (ctx, @SEGMENT_RETURNDATA, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success) - %build_address_no_offset - // stack: SRC, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success - SWAP3 - %build_address - // stack: DST, SRC, n, %%after, kexit_info, success - %jump(memcpy_bytes) -%%after: -%endmacro - -// Checked memory expansion. -%macro checked_mem_expansion - // stack: size, offset, kexit_info - DUP1 ISZERO %jumpi(%%zero) - %add_or_fault - // stack: expanded_num_bytes, kexit_info - DUP1 %ensure_reasonable_offset - %update_mem_bytes - %jump(%%after) -%%zero: - %pop2 -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/call_gas.asm b/evm/src/cpu/kernel/asm/core/call_gas.asm deleted file mode 100644 index 3961352139..0000000000 --- a/evm/src/cpu/kernel/asm/core/call_gas.asm +++ /dev/null @@ -1,92 +0,0 @@ -%macro call_charge_gas(is_call_or_callcode, is_call_or_staticcall) - %stack (cold_access, address, gas, kexit_info, value) -> - ($is_call_or_callcode, $is_call_or_staticcall, cold_access, address, gas, kexit_info, value, %%after) - %jump(call_charge_gas) -%%after: - // stack: kexit_info, C_callgas, address, value -%endmacro - -// Charge gas for *call opcodes and return the sub-context gas limit. -// Doesn't include memory expansion costs. -global call_charge_gas: - // Compute C_access - // stack: is_call_or_callcode, is_call_or_staticcall, cold_access, address, gas, kexit_info, value, retdest - SWAP2 - // stack: cold_access, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %mul_const(@GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS) - %add_const(@GAS_WARMACCESS) - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - DUP3 - // stack: is_call_or_callcode, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jumpi(xfer_cost) -after_xfer_cost: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - DUP2 - %jumpi(new_cost) -after_new_cost: - %stack (Cextra, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest) -> - (Cextra, address, gas, kexit_info, value, retdest) - // Compute C_gascap - // stack: Cextra, address, gas, kexit_info, value, retdest - DUP4 %leftover_gas - // stack: leftover_gas, Cextra, address, gas, kexit_info, value, retdest - DUP2 DUP2 LT - // stack: leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas (Cextra, Cgascap, Cgascap) - ADD - %stack (C_call, Cgascap, address, gas, kexit_info, value) -> - (C_call, kexit_info, Cgascap, address, gas, value) - %charge_gas - - // Compute C_callgas - %stack (kexit_info, Cgascap, address, gas, value) -> - (Cgascap, address, gas, kexit_info, value) - DUP5 ISZERO %not_bit - // stack: value!=0, Cgascap, address, gas, kexit_info, value, retdest - %mul_const(@GAS_CALLSTIPEND) ADD - %stack (C_callgas, address, gas, kexit_info, value, retdest) -> - (retdest, kexit_info, C_callgas, address, value) - JUMP - -global xfer_cost: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - DUP7 - // stack: value, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jumpi(xfer_cost_nonzero) - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jump(after_xfer_cost) -xfer_cost_nonzero: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %add_const(@GAS_CALLVALUE) - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jump(after_xfer_cost) - -new_cost: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - DUP7 - // stack: value, cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jumpi(new_cost_transfers_value) - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jump(after_new_cost) -new_cost_transfers_value: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - DUP4 %is_dead - %jumpi(new_cost_nonzero) - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %jump(after_new_cost) -new_cost_nonzero: - // stack: cost, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest - %add_const(@GAS_NEWACCOUNT) - %jump(after_new_cost) diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm deleted file mode 100644 index 80f8f46188..0000000000 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ /dev/null @@ -1,291 +0,0 @@ -// The CREATE syscall. Address will be -// address = KEC(RLP(sender, nonce))[12:] -// -// Pre stack: kexit_info, value, code_offset, code_len -// Post stack: address -global sys_create: - %check_static - - %stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len) - %checked_mem_expansion - // stack: kexit_info, value, code_offset, code_len - %charge_gas_const(@GAS_CREATE) - // stack: kexit_info, value, code_offset, code_len - DUP4 - // stack: code_len, kexit_info, value, code_offset, code_len - %check_initcode_size - - %stack (kexit_info, value, code_offset, code_len) - -> (sys_create_got_address, value, code_offset, code_len, kexit_info) - %address - // stack: sender, sys_create_got_address, value, code_offset, code_len, kexit_info - DUP1 %nonce - // stack: nonce, sender, sys_create_got_address, value, code_offset, code_len, kexit_info - SWAP1 - // stack: sender, nonce, sys_create_got_address, value, code_offset, code_len, kexit_info - %jump(get_create_address) -sys_create_got_address: - // stack: address, value, code_offset, code_len, kexit_info - %jump(create_common) - -// The CREATE2 syscall; see EIP-1014. Address will be -// address = KEC(0xff || sender || salt || code_hash)[12:] -// -// Pre stack: kexit_info, value, code_offset, code_len, salt -// Post stack: address -global sys_create2: - %check_static - - // stack: kexit_info, value, code_offset, code_len, salt - %stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len) - %checked_mem_expansion - // stack: kexit_info, value, code_offset, code_len, salt - DUP4 %num_bytes_to_num_words - %mul_const(@GAS_KECCAK256WORD) %add_const(@GAS_CREATE) %charge_gas - // stack: kexit_info, value, code_offset, code_len, salt - DUP4 - // stack: code_len, kexit_info, value, code_offset, code_len, salt - %check_initcode_size - - - SWAP4 - %stack (salt) -> (salt, create_common) - // stack: salt, create_common, value, code_offset, code_len, kexit_info - - // Hash the code. - DUP5 // code_len - DUP5 // code_offset - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - KECCAK_GENERAL - // stack: hash, salt, create_common, value, code_offset, code_len, kexit_info - - %address - // stack: sender, hash, salt, create_common, value, code_offset, code_len, kexit_info - %jump(get_create2_address) - -// Pre stack: address, value, code_offset, code_len, kexit_info -// Post stack: address -global create_common: - // stack: address, value, code_offset, code_len, kexit_info - DUP1 %insert_accessed_addresses_no_return - - // Check call depth - %call_depth - %gt_const(@CALL_STACK_LIMIT) - %jumpi(create_too_deep) - - // stack: address, value, code_offset, code_len, kexit_info - DUP2 %selfbalance LT %jumpi(create_insufficient_balance) - // Increment the sender's nonce. - %address - DUP1 %nonce %eq_const(@MAX_NONCE) %jumpi(nonce_overflow) // EIP-2681 - %increment_nonce - // stack: address, value, code_offset, code_len, kexit_info - - %checkpoint - - // stack: address, value, code_offset, code_len, kexit_info - DUP2 DUP2 %address %transfer_eth %jumpi(panic) // We checked the balance above, so this should never happen. - DUP2 DUP2 %address %journal_add_balance_transfer // Add journal entry for the balance transfer. - - %create_context - // stack: new_ctx, address, value, code_offset, code_len, kexit_info - GET_CONTEXT - // stack: src_ctx, new_ctx, address, value, code_offset, code_len, kexit_info - - %stack (src_ctx, new_ctx, address, value, code_offset, code_len) -> - (code_len, new_ctx, src_ctx, new_ctx, address, value, code_offset, code_len) - %set_new_ctx_code_size POP - // Copy the code from memory to the new context's code segment. - %stack (src_ctx, new_ctx, address, value, code_offset, code_len) - -> (src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC - new_ctx, // DST (SEGMENT_CODE == virt == 0) - code_len, - run_constructor, - new_ctx, value, address) - %build_address - // stack: SRC, DST, code_len, run_constructor, new_ctx, value, address - SWAP1 - // stack: DST, SRC, code_len, run_constructor, new_ctx, value, address - %jump(memcpy_bytes) - -run_constructor: - // stack: new_ctx, value, address, kexit_info - SWAP1 %set_new_ctx_value - // stack: new_ctx, address, kexit_info - - // Each line in the block below does not change the stack. - DUP2 %set_new_ctx_addr - %address %set_new_ctx_caller - %set_new_ctx_parent_pc(after_constructor) - // stack: new_ctx, address, kexit_info - - // All but 1/64 of the sender's remaining gas goes to the constructor. - SWAP2 - // stack: kexit_info, address, new_ctx - %drain_all_but_one_64th_gas - %stack (kexit_info, drained_gas, address, new_ctx) -> (drained_gas, new_ctx, address, kexit_info) - %set_new_ctx_gas_limit - // stack: new_ctx, address, kexit_info - - // Create the new contract account in the state trie. - DUP2 - %create_contract_account - // stack: status, new_ctx, address, kexit_info - %jumpi(create_collision) - - %enter_new_ctx - // (Old context) stack: new_ctx, address, kexit_info - -after_constructor: - // stack: success, leftover_gas, new_ctx, address, kexit_info - DUP1 ISZERO %jumpi(after_constructor_failed) - - // stack: success, leftover_gas, new_ctx, address, kexit_info - SWAP2 - // stack: new_ctx, leftover_gas, success, address, kexit_info - POP - - // EIP-3541: Reject new contract code starting with the 0xEF byte - PUSH @SEGMENT_RETURNDATA - GET_CONTEXT - %build_address_no_offset - MLOAD_GENERAL - %eq_const(0xEF) %jumpi(create_first_byte_ef) - - // Charge gas for the code size. - // stack: leftover_gas, success, address, kexit_info - %returndatasize // Size of the code. - // stack: code_size, leftover_gas, success, address, kexit_info - DUP1 %gt_const(@MAX_CODE_SIZE) %jumpi(create_code_too_large) - // stack: code_size, leftover_gas, success, address, kexit_info - %mul_const(@GAS_CODEDEPOSIT) - // stack: code_size_cost, leftover_gas, success, address, kexit_info - DUP2 DUP2 GT %jumpi(create_oog) - SWAP1 SUB - // stack: leftover_gas, success, address, kexit_info - %pop_checkpoint - - // Store the code hash of the new contract. - %returndatasize - PUSH @SEGMENT_RETURNDATA GET_CONTEXT %build_address_no_offset - // stack: addr, len - KECCAK_GENERAL - // stack: codehash, leftover_gas, success, address, kexit_info - %observe_new_contract - DUP4 - // stack: address, codehash, leftover_gas, success, address, kexit_info - %set_codehash - - // Set the return data size to 0. - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - -after_constructor_contd: - // stack: leftover_gas, success, address, kexit_info - %shl_const(192) - // stack: leftover_gas << 192, success, address, kexit_info - SWAP2 - // stack: address, success, leftover_gas << 192, kexit_info - MUL - // stack: address_if_success, leftover_gas << 192, kexit_info - SWAP2 - // stack: kexit_info, leftover_gas << 192, address_if_success - SUB - // stack: kexit_info, address_if_success - EXIT_KERNEL - -after_constructor_failed: - %revert_checkpoint - %stack (success, leftover_gas, new_ctx, address, kexit_info) -> (leftover_gas, success, address, kexit_info) - %jump(after_constructor_contd) - -create_insufficient_balance: - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %stack (address, value, code_offset, code_len, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -nonce_overflow: - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %stack (sender, address, value, code_offset, code_len, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -create_collision: - %revert_checkpoint - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %stack (new_ctx, address, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -create_first_byte_ef: - %revert_checkpoint - %stack (leftover_gas, success, address, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -create_code_too_large: - %revert_checkpoint - %stack (code_size, leftover_gas, success, address, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -create_oog: - %revert_checkpoint - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %stack (code_size_cost, leftover_gas, success, address, kexit_info) -> (kexit_info, 0) - EXIT_KERNEL - -create_too_deep: - %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %stack (address, value, code_offset, code_len, kexit_info) -> (kexit_info, 0) - // stack: kexit_info, 0 - EXIT_KERNEL - -%macro set_codehash - %stack (addr, codehash) -> (addr, codehash, %%after) - %jump(set_codehash) -%%after: - // stack: (empty) -%endmacro - -// Pre stack: addr, codehash, redest -// Post stack: (empty) -global set_codehash: - // stack: addr, codehash, retdest - DUP1 %insert_touched_addresses - DUP1 %mpt_read_state_trie - // stack: account_ptr, addr, codehash, retdest - %add_const(3) - // stack: codehash_ptr, addr, codehash, retdest - DUP1 %mload_trie_data - // stack: prev_codehash, codehash_ptr, addr, codehash, retdest - DUP3 %journal_add_code_change // Add the code change to the journal. - %stack (codehash_ptr, addr, codehash) -> (codehash_ptr, codehash) - %mstore_trie_data - // stack: retdest - JUMP - -// Check and charge gas cost for initcode size. See EIP-3860. -// Pre stack: code_size, kexit_info -// Post stack: kexit_info -%macro check_initcode_size - DUP1 %gt_const(@MAX_INITCODE_SIZE) %jumpi(fault_exception) - // stack: code_size, kexit_info - %num_bytes_to_num_words %mul_const(@INITCODE_WORD_COST) - %charge_gas -%endmacro - - -// This should be called whenever a new contract is created. -// It does nothing, but just provides a single hook where code can react to newly created contracts. -// When called, the code corresponding to `codehash` should be stored in the return data. -// Pre stack: codehash, retdest -// Post stack: codehash -global observe_new_contract: - // stack codehash, retdest - SWAP1 JUMP - -%macro observe_new_contract - %stack (codehash) -> (codehash, %%after) - %jump(observe_new_contract) -%%after: - // stack: codehash -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm deleted file mode 100644 index 8c2de08bd2..0000000000 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ /dev/null @@ -1,76 +0,0 @@ -// Computes the address of a contract based on the conventional scheme, i.e. -// address = KEC(RLP(sender, nonce))[12:] -// -// Pre stack: sender, nonce, retdest -// Post stack: address -global get_create_address: - // stack: sender, nonce, retdest - %alloc_rlp_block - // stack: rlp_start, sender, nonce, retdest - %stack (rlp_start, sender, nonce) -> (rlp_start, sender, nonce, rlp_start) - // stack: rlp_start, sender, nonce, rlp_start, retdest - %encode_rlp_160 // TODO: or encode_rlp_scalar? - // stack: rlp_pos, nonce, rlp_start, retdest - %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest - %prepend_rlp_list_prefix - // stack: RLP_ADDR, rlp_len, retdest - KECCAK_GENERAL - // stack: hash, retdest - %u256_to_addr - // stack: address, retdest - %observe_new_address - SWAP1 - JUMP - -// Convenience macro to call get_create_address and return where we left off. -%macro get_create_address - %stack (sender, nonce) -> (sender, nonce, %%after) - %jump(get_create_address) -%%after: -%endmacro - -// Computes the address for a contract based on the CREATE2 rule, i.e. -// address = KEC(0xff || sender || salt || code_hash)[12:] -// Clobbers @SEGMENT_KERNEL_GENERAL. -// Pre stack: sender, code_hash, salt, retdest -// Post stack: address -global get_create2_address: - // stack: sender, code_hash, salt, retdest - PUSH @SEGMENT_KERNEL_GENERAL - DUP1 - PUSH 0xff - MSTORE_GENERAL - // stack: addr, sender, code_hash, salt, retdest - %increment - %stack (addr, sender, code_hash, salt, retdest) -> (addr, sender, salt, code_hash, retdest) - MSTORE_32BYTES_20 - // stack: addr, salt, code_hash, retdest - MSTORE_32BYTES_32 - // stack: addr, code_hash, retdest - MSTORE_32BYTES_32 - POP - %stack (retdest) -> (@SEGMENT_KERNEL_GENERAL, 85, retdest) // offset == context == 0 - // addr, len, retdest - KECCAK_GENERAL - // stack: hash, retdest - %u256_to_addr - // stack: address, retdest - %observe_new_address - SWAP1 - JUMP - -// This should be called whenever a new address is created. This is only for debugging. It does -// nothing, but just provides a single hook where code can react to newly created addresses. -global observe_new_address: - // stack: address, retdest - SWAP1 - // stack: retdest, address - JUMP - -// Convenience macro to call observe_new_address and return where we left off. -%macro observe_new_address - %stack (address) -> (address, %%after) - %jump(observe_new_address) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/create_contract_account.asm b/evm/src/cpu/kernel/asm/core/create_contract_account.asm deleted file mode 100644 index b45d45ca5c..0000000000 --- a/evm/src/cpu/kernel/asm/core/create_contract_account.asm +++ /dev/null @@ -1,62 +0,0 @@ -// Create a smart contract account with the given address and the given endowment value. -// Pre stack: address -// Post stack: status -%macro create_contract_account - // stack: address - DUP1 %insert_touched_addresses - DUP1 %mpt_read_state_trie - // stack: existing_account_ptr, address - // If the account doesn't exist, there's no need to check its balance or nonce, - // so we can skip ahead, setting existing_balance = existing_account_ptr = 0. - DUP1 ISZERO %jumpi(%%add_account) - - // Check that the nonce is 0. - // stack: existing_account_ptr, address - DUP1 %mload_trie_data // nonce = account[0] - // stack: nonce, existing_account_ptr, address - %jumpi(%%error_collision) - // stack: existing_account_ptr, address - // Check that the code is empty. - %add_const(3) - // stack: existing_codehash_ptr, address - DUP1 %mload_trie_data // codehash = account[3] - %eq_const(@EMPTY_STRING_HASH) ISZERO %jumpi(%%error_collision) - // stack: existing_codehash_ptr, address - %sub_const(2) %mload_trie_data // balance = account[1] - %jump(%%do_insert) - -%%add_account: - // stack: existing_balance, address - DUP2 %journal_add_account_created -%%do_insert: - // stack: new_acct_value, address - // Write the new account's data to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: account_ptr, new_acct_value, address - PUSH 0 DUP4 %journal_add_nonce_change - PUSH 1 %append_to_trie_data // nonce = 1 - // stack: account_ptr, new_acct_value, address - SWAP1 %append_to_trie_data // balance = new_acct_value - // stack: account_ptr, address - PUSH 0 %append_to_trie_data // storage_root = nil - // stack: account_ptr, address - PUSH @EMPTY_STRING_HASH %append_to_trie_data // code_hash = keccak('') - // stack: account_ptr, address - SWAP1 - // stack: address, account_ptr - %addr_to_state_key - // stack: state_key, account_ptr - %mpt_insert_state_trie - // stack: (empty) - PUSH 0 // success - %jump(%%end) - -// If the nonce is nonzero or the code is non-empty, that means a contract has already been deployed to this address. -// (This should be impossible with contract creation transactions or CREATE, but possible with CREATE2.) -// So we return 1 to indicate an error. -%%error_collision: - %stack (existing_account_ptr, address) -> (1) - -%%end: - // stack: status -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/create_receipt.asm b/evm/src/cpu/kernel/asm/core/create_receipt.asm deleted file mode 100644 index 60e9264739..0000000000 --- a/evm/src/cpu/kernel/asm/core/create_receipt.asm +++ /dev/null @@ -1,249 +0,0 @@ -// Pre-stack: status, leftover_gas, prev_cum_gas, txn_nb, num_nibbles, retdest -// Post stack: new_cum_gas, txn_nb -// A receipt is stored in MPT_TRIE_DATA as: -// [payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]] -// -// In this function, we: -// - compute cum_gas, -// - check if the transaction failed and set number of logs to 0 if it is the case, -// - compute the bloom filter, -// - write the receipt in MPT_TRIE_DATA , -// - insert a new node in receipt_trie, -// - set the bloom filter back to 0 -global process_receipt: - // stack: status, leftover_gas, prev_cum_gas, txn_nb, num_nibbles, retdest - DUP2 DUP4 - // stack: prev_cum_gas, leftover_gas, status, leftover_gas, prev_cum_gas, txn_nb, num_nibbles, retdest - %compute_cumulative_gas - // stack: new_cum_gas, status, leftover_gas, prev_cum_gas, txn_nb, num_nibbles, retdest - SWAP3 POP - // stack: status, leftover_gas, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP1 POP - // stack: status, new_cum_gas, txn_nb, num_nibbles, retdest - // Now, we need to check whether the transaction has failed. - DUP1 ISZERO %jumpi(failed_receipt) - -process_receipt_after_status: - // stack: status, new_cum_gas, txn_nb, num_nibbles, retdest - PUSH process_receipt_after_bloom - %jump(logs_bloom) - -process_receipt_after_bloom: - // stack: status, new_cum_gas, txn_nb, num_nibbles, retdest - DUP2 DUP4 - // stack: txn_nb, new_cum_gas, status, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP2 - // stack: status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - - // Compute the total RLP payload length of the receipt. - PUSH 1 // status is always 1 byte. - // stack: payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP3 - %rlp_scalar_len // cum_gas is a simple scalar. - ADD - // stack: payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Next is the bloom_filter, which is a 256-byte array. Its RLP encoding is - // 1 + 2 + 256 bytes. - %add_const(259) - // stack: payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Last is the logs. - %mload_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - %rlp_list_len - ADD - // stack: payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Now we can write the receipt in MPT_TRIE_DATA. - %get_trie_data_size - // stack: receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write transaction type if necessary. RLP_RAW contains, at index 0, the current transaction type. - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - MLOAD_GENERAL - // stack: first_txn_byte, receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 %eq_const(1) %jumpi(receipt_nonzero_type) - DUP1 %eq_const(2) %jumpi(receipt_nonzero_type) - // If we are here, we are dealing with a legacy transaction, and we do not need to write the type. - POP - -process_receipt_after_type: - // stack: receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write payload_len. - SWAP1 - %append_to_trie_data - // stack: receipt_ptr, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write status. - SWAP1 - %append_to_trie_data - // stack: receipt_ptr, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write cum_gas_used. - SWAP1 - %append_to_trie_data - // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write Bloom filter. - PUSH 256 // Bloom length. - PUSH @SEGMENT_TXN_BLOOM // ctx == virt == 0 - // stack: bloom_addr, 256, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %get_trie_data_size - PUSH @SEGMENT_TRIE_DATA ADD // MPT dest address. - // stack: DST, SRC, 256, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %memcpy_bytes - // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Update trie data size. - %get_trie_data_size - %add_const(256) - %set_trie_data_size - - // Now we write logs. - // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // We start with the logs payload length. - %mload_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - %append_to_trie_data - // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %mload_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - // Then the number of logs. - // stack: num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 %append_to_trie_data - PUSH 0 - -// Each log is written in MPT_TRIE_DATA as: -// [payload_len, address, num_topics, [topics], data_len, [data]]. -process_receipt_logs_loop: - // stack: i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP2 DUP2 - EQ - // stack: i == num_logs, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %jumpi(process_receipt_after_write) - // stack: i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 - %mload_kernel(@SEGMENT_LOGS) - // stack: log_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write payload_len. - PUSH @SEGMENT_LOGS_DATA %build_kernel_address - DUP1 - MLOAD_GENERAL - %append_to_trie_data - // stack: log_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write address. - %increment - // stack: addr_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 - MLOAD_GENERAL - %append_to_trie_data - // stack: addr_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - //Write num_topics. - %increment - // stack: num_topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 - MLOAD_GENERAL - // stack: num_topics, num_topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 - %append_to_trie_data - // stack: num_topics, num_topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP1 %increment SWAP1 - // stack: num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - PUSH 0 - -process_receipt_topics_loop: - // stack: j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP2 DUP2 - EQ - // stack: j == num_topics, j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %jumpi(process_receipt_topics_end) - // stack: j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write j-th topic. - DUP3 DUP2 - ADD - // stack: cur_topic_ptr, j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - MLOAD_GENERAL - %append_to_trie_data - // stack: j, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %increment - %jump(process_receipt_topics_loop) - -process_receipt_topics_end: - // stack: num_topics, num_topics, topics_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - POP - ADD - // stack: data_len_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write data_len - DUP1 - MLOAD_GENERAL - // stack: data_len, data_len_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP1 - %append_to_trie_data - // stack: data_len, data_len_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP1 %increment SWAP1 - // stack: data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - PUSH 0 - -process_receipt_data_loop: - // stack: j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - DUP2 DUP2 - EQ - // stack: j == data_len, j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %jumpi(process_receipt_data_end) - // stack: j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - // Write j-th data byte. - DUP3 DUP2 - ADD - // stack: cur_data_ptr, j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - MLOAD_GENERAL - %append_to_trie_data - // stack: j, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %increment - %jump(process_receipt_data_loop) - -process_receipt_data_end: - // stack: data_len, data_len, data_ptr, i, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %pop3 - %increment - %jump(process_receipt_logs_loop) - -process_receipt_after_write: - // stack: num_logs, num_logs, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - %pop2 - // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest - SWAP1 - // stack: txn_nb, receipt_ptr, new_cum_gas, txn_nb, num_nibbles, retdest - DUP5 - %mpt_insert_receipt_trie - // stack: new_cum_gas, txn_nb, num_nibbles, retdest - - // We don't need to reset the bloom filter segment as we only process a single transaction. - // TODO: Revert in case we add back support for multi-txn proofs. - - %stack (new_cum_gas, txn_nb, num_nibbles, retdest) -> (retdest, new_cum_gas) - JUMP - -receipt_nonzero_type: - // stack: txn_type, receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, retdest - %append_to_trie_data - %jump(process_receipt_after_type) - -failed_receipt: - // stack: status, new_cum_gas, num_nibbles, txn_nb - // It is the receipt of a failed transaction, so set num_logs to 0. This will also lead to Bloom filter = 0. - PUSH 0 - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - PUSH 0 %mstore_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - // stack: status, new_cum_gas, num_nibbles, txn_nb - %jump(process_receipt_after_status) - -%macro process_receipt - // stack: success, leftover_gas, cur_cum_gas, txn_nb, num_nibbles - %stack (success, leftover_gas, cur_cum_gas, txn_nb, num_nibbles) -> (success, leftover_gas, cur_cum_gas, txn_nb, num_nibbles, %%after) - %jump(process_receipt) -%%after: -%endmacro - -%macro compute_cumulative_gas - // stack: cur_cum_gas, leftover_gas - DUP2 - // stack: leftover_gas, prev_cum_gas, leftover_gas - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - // stack: gas_limit, leftover_gas, prev_cum_gas, leftover_gas - DUP2 DUP2 LT %jumpi(panic) - // stack: gas_limit, leftover_gas, prev_cum_gas, leftover_gas - SUB - // stack: used_txn_gas, prev_cum_gas, leftover_gas - ADD SWAP1 POP - // stack: new_cum_gas -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/exception.asm b/evm/src/cpu/kernel/asm/core/exception.asm deleted file mode 100644 index 6ce2d676d3..0000000000 --- a/evm/src/cpu/kernel/asm/core/exception.asm +++ /dev/null @@ -1,436 +0,0 @@ -// These exception codes are arbitrary and assigned by us. -// Note that exceptions can only be triggered in user mode. Triggering an exception -// in kernel mode wwill fail the constraints. -global exception_jumptable: - // exception 0: out of gas - JUMPTABLE exc_out_of_gas - - // exception 1: invalid opcode - JUMPTABLE exc_invalid_opcode - - // exception 2: stack underflow - JUMPTABLE exc_stack_underflow - - // exception 3: invalid jump destination - JUMPTABLE exc_invalid_jump_destination - - // exception 4: invalid jumpi destination - JUMPTABLE exc_invalid_jumpi_destination - - // exception 5: stack overflow - JUMPTABLE exc_stack_overflow - - // exceptions 6 and 7: unused - JUMPTABLE panic - JUMPTABLE panic - - -global exc_out_of_gas: - // stack: trap_info - %ctx_gas_limit - // stack: gas_limit, trap_info - DUP2 %shr_const(192) - // stack: gas_used, gas_limit, trap_info - DUP2 DUP2 - // stack: gas_used, gas_limit, gas_used, gas_limit, trap_info - // If gas_used is already over the limit, panic. The exception should have - // been raised earlier. - GT %jumpi(panic) - // stack: gas_used, gas_limit, trap_info - DUP3 %opcode_from_exp_trap_info - // stack: opcode, gas_used, gas_limit, trap_info - %add_const(gas_cost_for_opcode) - %mload_kernel_code - // stack: gas_cost, gas_used, gas_limit, trap_info - ADD - // stack: new_gas_used, gas_limit, trap_info - GT - // stack: is_oog, trap_info - SWAP1 POP - // stack: is_oog - %jumpi(fault_exception) - // If we didn't jump, we shouldn't have raised the exception. - PANIC - - -global exc_invalid_opcode: - // stack: trap_info - // check if the opcode that triggered this trap is _actually_ invalid - %opcode_from_exp_trap_info - PUSH @INVALID_OPCODES_USER - // stack: invalid_opcodes_user, opcode - SWAP1 - // stack: opcode, invalid_opcodes_user - SHR - %mod_const(2) - // stack: opcode_is_invalid - // if the opcode is indeed invalid, then perform an exceptional exit - %jumpi(fault_exception) - // otherwise, panic because this trap should not have been entered - PANIC - - -global exc_stack_underflow: - // stack: trap_info - %opcode_from_exp_trap_info - // stack: opcode - %add_const(min_stack_len_for_opcode) - %mload_kernel_code - // stack: min_stack_length - %stack_length - // stack: user_stack_length + 1, min_stack_length - GT - // stack: user_stack_length >= min_stack_length - %jumpi(panic) - %jump(fault_exception) - - -// Debugging note: this will underflow if entered without at least one item on the stack (in -// addition to trap_info). This is expected; it means that the exc_stack_underflow handler should -// have been used instead. -global exc_invalid_jump_destination: - // stack: trap_info, jump_dest - // check that the triggering opcode is indeed JUMP - %opcode_from_exp_trap_info - // stack: opcode, jump_dest - %eq_const(0x56) - // if it's JUMP, then verify that we're actually jumping to an invalid address - %jumpi(invalid_jump_jumpi_destination_common) - // otherwise, panic - PANIC - - -// Debugging note: this will underflow if entered without at least two items on the stack (in -// addition to trap_info). This is expected; it means that the exc_stack_underflow handler should -// have been used instead. -global exc_invalid_jumpi_destination: - // stack: trap_info, jump_dest, condition - // check that the triggering opcode is indeed JUMPI - %opcode_from_exp_trap_info - // stack: opcode, jump_dest, condition - %sub_const(0x57) - // if it's not JUMPI, then panic - %jumpi(panic) - // otherwise, verify that the condition is nonzero - // stack: jump_dest, condition - SWAP1 - // if it's nonzero, then verify that we're actually jumping to an invalid address - %jumpi(invalid_jump_jumpi_destination_common) - // otherwise, panic - PANIC - - -global invalid_jump_jumpi_destination_common: - // We have a jump destination on the stack. We want to `PANIC` if it is valid, and jump to - // `fault_exception` if it is not. An address is a valid jump destination if it points to a - // `JUMPDEST` instruction. In practice, since in this implementation memory addresses are - // limited to 32 bits, we check two things: - // 1. the address is no more than 32 bits long, and - // 2. it points to a `JUMPDEST` instruction. - // stack: jump_dest - DUP1 - %shr_const(32) - %jumpi(fault_exception) // This keeps one copy of jump_dest on the stack, but that's fine. - // jump_dest is a valid address; check if it points to a `JUMP_DEST`. - %mload_current(@SEGMENT_JUMPDEST_BITS) - // stack: is_valid_jumpdest - %jumpi(panic) // Trap should never have been entered. - %jump(fault_exception) - - -global exc_stack_overflow: - // stack: trap_info - // check that the triggering opcode _can_ overflow (i.e., it increases the stack size by 1) - %opcode_from_exp_trap_info - PUSH @STACK_LENGTH_INCREASING_OPCODES_USER - // stack: stack_length_increasing_opcodes_user, opcode - SWAP1 - // stack: opcode, stack_length_increasing_opcodes_user - SHR - %mod_const(2) - // stack: opcode_increases_stack_length - // if the opcode indeed increases the stack length, then check whether the stack size is at its - // maximum value - %jumpi(exc_stack_overflow_check_stack_length) - // otherwise, panic because this trap should not have been entered - PANIC -global exc_stack_overflow_check_stack_length: - // stack: (empty) - %stack_length - %eq_const(1024) - // if true, stack length is at its maximum allowed value, so the instruction would indeed cause - // an overflow. - %jumpi(fault_exception) - PANIC - - -// Given the exception trap info, load the opcode that caused the exception -%macro opcode_from_exp_trap_info - %mod_const(0x100000000) // get program counter from low 32 bits of trap_info - %mload_current_code -%endmacro - - -min_stack_len_for_opcode: - BYTES 0 // 0x00, STOP - BYTES 2 // 0x01, ADD - BYTES 2 // 0x02, MUL - BYTES 2 // 0x03, SUB - BYTES 2 // 0x04, DIV - BYTES 2 // 0x05, SDIV - BYTES 2 // 0x06, MOD - BYTES 2 // 0x07, SMOD - BYTES 3 // 0x08, ADDMOD - BYTES 3 // 0x09, MULMOD - BYTES 2 // 0x0a, EXP - BYTES 2 // 0x0b, SIGNEXTEND - %rep 4 // 0x0c-0x0f, invalid - BYTES 0 - %endrep - - BYTES 2 // 0x10, LT - BYTES 2 // 0x11, GT - BYTES 2 // 0x12, SLT - BYTES 2 // 0x13, SGT - BYTES 2 // 0x14, EQ - BYTES 1 // 0x15, ISZERO - BYTES 2 // 0x16, AND - BYTES 2 // 0x17, OR - BYTES 2 // 0x18, XOR - BYTES 1 // 0x19, NOT - BYTES 2 // 0x1a, BYTE - BYTES 2 // 0x1b, SHL - BYTES 2 // 0x1c, SHR - BYTES 2 // 0x1d, SAR - BYTES 0 // 0x1e, invalid - BYTES 0 // 0x1f, invalid - - BYTES 2 // 0x20, KECCAK256 - %rep 15 // 0x21-0x2f, invalid - BYTES 0 - %endrep - - BYTES 0 // 0x30, ADDRESS - BYTES 1 // 0x31, BALANCE - BYTES 0 // 0x32, ORIGIN - BYTES 0 // 0x33, CALLER - BYTES 0 // 0x34, CALLVALUE - BYTES 1 // 0x35, CALLDATALOAD - BYTES 0 // 0x36, CALLDATASIZE - BYTES 3 // 0x37, CALLDATACOPY - BYTES 0 // 0x38, CODESIZE - BYTES 3 // 0x39, CODECOPY - BYTES 0 // 0x3a, GASPRICE - BYTES 1 // 0x3b, EXTCODESIZE - BYTES 4 // 0x3c, EXTCODECOPY - BYTES 0 // 0x3d, RETURNDATASIZE - BYTES 3 // 0x3e, RETURNDATACOPY - BYTES 1 // 0x3f, EXTCODEHASH - - BYTES 1 // 0x40, BLOCKHASH - BYTES 0 // 0x41, COINBASE - BYTES 0 // 0x42, TIMESTAMP - BYTES 0 // 0x43, NUMBER - BYTES 0 // 0x44, DIFFICULTY - BYTES 0 // 0x45, GASLIMIT - BYTES 0 // 0x46, CHAINID - BYTES 0 // 0x47, SELFBALANCE - BYTES 0 // 0x48, BASEFEE - %rep 7 // 0x49-0x4f, invalid - BYTES 0 - %endrep - - BYTES 1 // 0x50, POP - BYTES 1 // 0x51, MLOAD - BYTES 2 // 0x52, MSTORE - BYTES 2 // 0x53, MSTORE8 - BYTES 1 // 0x54, SLOAD - BYTES 2 // 0x55, SSTORE - BYTES 1 // 0x56, JUMP - BYTES 2 // 0x57, JUMPI - BYTES 0 // 0x58, PC - BYTES 0 // 0x59, MSIZE - BYTES 0 // 0x5a, GAS - BYTES 0 // 0x5b, JUMPDEST - %rep 3 // 0x5c-0x5e, invalid - BYTES 0 - %endrep - - %rep 33 // 0x5f-0x7f, PUSH0-PUSH32 - BYTES 0 - %endrep - - BYTES 1 // 0x80, DUP1 - BYTES 2 // 0x81, DUP2 - BYTES 3 // 0x82, DUP3 - BYTES 4 // 0x83, DUP4 - BYTES 5 // 0x84, DUP5 - BYTES 6 // 0x85, DUP6 - BYTES 7 // 0x86, DUP7 - BYTES 8 // 0x87, DUP8 - BYTES 9 // 0x88, DUP9 - BYTES 10 // 0x89, DUP10 - BYTES 11 // 0x8a, DUP11 - BYTES 12 // 0x8b, DUP12 - BYTES 13 // 0x8c, DUP13 - BYTES 14 // 0x8d, DUP14 - BYTES 15 // 0x8e, DUP15 - BYTES 16 // 0x8f, DUP16 - - BYTES 2 // 0x90, SWAP1 - BYTES 3 // 0x91, SWAP2 - BYTES 4 // 0x92, SWAP3 - BYTES 5 // 0x93, SWAP4 - BYTES 6 // 0x94, SWAP5 - BYTES 7 // 0x95, SWAP6 - BYTES 8 // 0x96, SWAP7 - BYTES 9 // 0x97, SWAP8 - BYTES 10 // 0x98, SWAP9 - BYTES 11 // 0x99, SWAP10 - BYTES 12 // 0x9a, SWAP11 - BYTES 13 // 0x9b, SWAP12 - BYTES 14 // 0x9c, SWAP13 - BYTES 15 // 0x9d, SWAP14 - BYTES 16 // 0x9e, SWAP15 - BYTES 17 // 0x9f, SWAP16 - - BYTES 2 // 0xa0, LOG0 - BYTES 3 // 0xa1, LOG1 - BYTES 4 // 0xa2, LOG2 - BYTES 5 // 0xa3, LOG3 - BYTES 6 // 0xa4, LOG4 - - %rep 27 // 0xa5-0xbf, invalid - BYTES 0 - %endrep - - %rep 32 // 0xc0-0xdf, MSTORE_32BYTES - BYTES 4 - %endrep - - %rep 16 // 0xe0-0xef, invalid - BYTES 0 - %endrep - - BYTES 3 // 0xf0, CREATE - BYTES 7 // 0xf1, CALL - BYTES 7 // 0xf2, CALLCODE - BYTES 2 // 0xf3, RETURN - BYTES 6 // 0xf4, DELEGATECALL - BYTES 4 // 0xf5, CREATE2 - %rep 4 // 0xf6-0xf9, invalid - BYTES 0 - %endrep - BYTES 6 // 0xfa, STATICCALL - BYTES 0 // 0xfb, invalid - BYTES 0 // 0xfc, invalid - BYTES 2 // 0xfd, REVERT - BYTES 0 // 0xfe, invalid - BYTES 1 // 0xff, SELFDESTRUCT - -// A zero indicates either that the opcode is kernel-only, -// or that it's handled with a syscall. -gas_cost_for_opcode: - BYTES 0 // 0x00, STOP - BYTES @GAS_VERYLOW // 0x01, ADD - BYTES @GAS_LOW // 0x02, MUL - BYTES @GAS_VERYLOW // 0x03, SUB - BYTES @GAS_LOW // 0x04, DIV - BYTES @GAS_LOW // 0x05, SDIV - BYTES @GAS_LOW // 0x06, MOD - BYTES @GAS_LOW // 0x07, SMOD - BYTES @GAS_MID // 0x08, ADDMOD - BYTES @GAS_MID // 0x09, MULMOD - BYTES 0 // 0x0a, EXP - BYTES 0 // 0x0b, SIGNEXTEND - %rep 4 // 0x0c-0x0f, invalid - BYTES 0 - %endrep - - BYTES @GAS_VERYLOW // 0x10, LT - BYTES @GAS_VERYLOW // 0x11, GT - BYTES @GAS_VERYLOW // 0x12, SLT - BYTES @GAS_VERYLOW // 0x13, SGT - BYTES @GAS_VERYLOW // 0x14, EQ - BYTES @GAS_VERYLOW // 0x15, ISZERO - BYTES @GAS_VERYLOW // 0x16, AND - BYTES @GAS_VERYLOW // 0x17, OR - BYTES @GAS_VERYLOW // 0x18, XOR - BYTES @GAS_VERYLOW // 0x19, NOT - BYTES @GAS_VERYLOW // 0x1a, BYTE - BYTES @GAS_VERYLOW // 0x1b, SHL - BYTES @GAS_VERYLOW // 0x1c, SHR - BYTES @GAS_VERYLOW // 0x1d, SAR - BYTES 0 // 0x1e, invalid - BYTES 0 // 0x1f, invalid - - BYTES 0 // 0x20, KECCAK256 - %rep 15 // 0x21-0x2f, invalid - BYTES 0 - %endrep - - %rep 25 //0x30-0x48, only syscalls - BYTES 0 - %endrep - - %rep 7 // 0x49-0x4f, invalid - BYTES 0 - %endrep - - BYTES @GAS_BASE // 0x50, POP - BYTES 0 // 0x51, MLOAD - BYTES 0 // 0x52, MSTORE - BYTES 0 // 0x53, MSTORE8 - BYTES 0 // 0x54, SLOAD - BYTES 0 // 0x55, SSTORE - BYTES @GAS_MID // 0x56, JUMP - BYTES @GAS_HIGH // 0x57, JUMPI - BYTES @GAS_BASE // 0x58, PC - BYTES 0 // 0x59, MSIZE - BYTES 0 // 0x5a, GAS - BYTES @GAS_JUMPDEST // 0x5b, JUMPDEST - %rep 3 // 0x5c-0x5e, invalid - BYTES 0 - %endrep - - BYTES @GAS_BASE // 0x5f, PUSH0 - %rep 32 // 0x60-0x7f, PUSH1-PUSH32 - BYTES @GAS_VERYLOW - %endrep - - %rep 16 // 0x80-0x8f, DUP1-DUP16 - BYTES @GAS_VERYLOW - %endrep - - %rep 16 // 0x90-0x9f, SWAP1-SWAP16 - BYTES @GAS_VERYLOW - %endrep - - BYTES 0 // 0xa0, LOG0 - BYTES 0 // 0xa1, LOG1 - BYTES 0 // 0xa2, LOG2 - BYTES 0 // 0xa3, LOG3 - BYTES 0 // 0xa4, LOG4 - %rep 11 // 0xa5-0xaf, invalid - BYTES 0 - %endrep - - %rep 64 // 0xb0-0xef, invalid - BYTES 0 - %endrep - - BYTES 0 // 0xf0, CREATE - BYTES 0 // 0xf1, CALL - BYTES 0 // 0xf2, CALLCODE - BYTES 0 // 0xf3, RETURN - BYTES 0 // 0xf4, DELEGATECALL - BYTES 0 // 0xf5, CREATE2 - %rep 4 // 0xf6-0xf9, invalid - BYTES 0 - %endrep - BYTES 0 // 0xfa, STATICCALL - BYTES 0 // 0xfb, invalid - BYTES 0 // 0xfc, invalid - BYTES 0 // 0xfd, REVERT - BYTES 0 // 0xfe, invalid - BYTES 0 // 0xff, SELFDESTRUCT diff --git a/evm/src/cpu/kernel/asm/core/gas.asm b/evm/src/cpu/kernel/asm/core/gas.asm deleted file mode 100644 index 2e16c373e3..0000000000 --- a/evm/src/cpu/kernel/asm/core/gas.asm +++ /dev/null @@ -1,129 +0,0 @@ -global sys_gas: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - DUP1 %shr_const(192) - // stack: gas_used, kexit_info - %ctx_gas_limit - // stack: gas_limit, gas_used, kexit_info - SUB - // stack: gas_remaining, kexit_info - SWAP1 - EXIT_KERNEL - -%macro ctx_gas_limit - %mload_context_metadata(@CTX_METADATA_GAS_LIMIT) -%endmacro - - -// TODO: `%refund_gas` and `refund_gas_hook` are hooks used for debugging. They should be removed at some point and `refund_gas_original` renamed to `refund_gas`. -%macro refund_gas - PUSH %%after %jump(refund_gas_hook) -%%after: - %refund_gas_original -%endmacro - -global refund_gas_hook: - JUMP - -%macro refund_gas_original - // stack: amount - DUP1 %journal_refund - %mload_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) - ADD - %mstore_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) -%endmacro - -// TODO: `%charge_gas` and `charge_gas_hook` are hooks used for debugging. They should be removed at some point and `charge_gas_original` renamed to `charge_gas`. -%macro charge_gas - PUSH %%after %jump(charge_gas_hook) -%%after: - %charge_gas_original -%endmacro - -global charge_gas_hook: - JUMP - -// Charge gas. Faults if we exceed the limit for the current context. -%macro charge_gas_original - // stack: gas, kexit_info - %shl_const(192) - ADD - // stack: kexit_info' - %ctx_gas_limit - // stack: gas_limit, kexit_info' - DUP2 %shr_const(192) - // stack: gas_used, gas_limit, kexit_info' - GT - // stack: out_of_gas, kexit_info' - %jumpi(fault_exception) - // stack: kexit_info' -%endmacro - -// Charge a constant amount of gas. -%macro charge_gas_const(gas) - // stack: kexit_info - PUSH $gas - // stack: gas, kexit_info - %charge_gas - // stack: kexit_info' -%endmacro - -// Charge gas and exit kernel code. -%macro charge_gas_and_exit - // stack: gas, kexit_info - %charge_gas - // stack: kexit_info' - EXIT_KERNEL -%endmacro - -global sys_gasprice: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) - // stack: gas_price, kexit_info - SWAP1 - EXIT_KERNEL - -// Checks how much gas is remaining in this context, given the current kexit_info. -%macro leftover_gas - // stack: kexit_info - %shr_const(192) - // stack: gas_used - %mload_context_metadata(@CTX_METADATA_GAS_LIMIT) - // stack: gas_limit, gas_used - SWAP1 - // stack: gas_used, gas_limit - DUP2 DUP2 LT - // stack: gas_used < gas_limit, gas_used, gas_limit - SWAP2 - // stack: gas_limit, gas_used, gas_used < gas_limit - SUB - // stack: gas_limit - gas_used, gas_used < gas_limit - MUL - // stack: leftover_gas = (gas_limit - gas_used) * (gas_used < gas_limit) -%endmacro - -// Given the current kexit_info, drains all but one 64th of its remaining gas. -// Returns how much gas was drained. -%macro drain_all_but_one_64th_gas - // stack: kexit_info - DUP1 %leftover_gas - // stack: leftover_gas, kexit_info - %all_but_one_64th - // stack: all_but_one_64th, kexit_info - %stack (all_but_one_64th, kexit_info) -> (all_but_one_64th, kexit_info, all_but_one_64th) - %charge_gas - // stack: kexit_info, drained_gas -%endmacro - -// This is L(n), the "all but one 64th" function in the yellowpaper, i.e. -// L(n) = n - floor(n / 64) -%macro all_but_one_64th - // stack: n - DUP1 %shr_const(6) - // stack: floor(n / 64), n - SWAP1 SUB - // stack: n - floor(n / 64) -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm deleted file mode 100644 index bb7a21b5d4..0000000000 --- a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm +++ /dev/null @@ -1,84 +0,0 @@ -global intrinsic_gas: - // stack: retdest - // Calculate the number of zero and nonzero bytes in the txn data. - PUSH 0 // zeros = 0 - PUSH 0 // i = 0 - -count_zeros_loop: - // stack: i, zeros, retdest - DUP1 - %mload_txn_field(@TXN_FIELD_DATA_LEN) - EQ - // stack: i == data.len, i, zeros, retdest - %jumpi(count_zeros_finish) - - // stack: i, zeros, retdest - DUP1 - %mload_kernel(@SEGMENT_TXN_DATA) - ISZERO - // stack: data[i] == 0, i, zeros - %stack (data_i_is_zero, i, zeros) -> (data_i_is_zero, zeros, i) - ADD - // stack: zeros', i, retdest - SWAP1 - // stack: i, zeros', retdest - %increment - // stack: i', zeros', retdest - %jump(count_zeros_loop) - -count_zeros_finish: - // stack: i, zeros, retdest - POP - // stack: zeros, retdest - DUP1 - // stack: zeros, zeros, retdest - %mload_txn_field(@TXN_FIELD_DATA_LEN) - // stack: data.len, zeros, zeros, retdest - SUB - // stack: nonzeros, zeros, retdest - %mul_const(@GAS_TXDATANONZERO) - // stack: gas_nonzeros, zeros, retdest - SWAP1 - %mul_const(@GAS_TXDATAZERO) - // stack: gas_zeros, gas_nonzeros, retdest - ADD - // stack: gas_txndata, retdest - - %is_contract_creation - DUP1 - %mul_const(@GAS_TXCREATE) - // stack: gas_creation, is_creation, gas_txndata, retdest - SWAP1 - // stack: is_creation, gas_creation, gas_txndata, retdest - DUP1 - // stack: is_creation, is_creation, gas_creation, gas_txndata, retdest - %mload_txn_field(@TXN_FIELD_DATA_LEN) %gt_const(@MAX_INITCODE_SIZE) - // stack: initcode_size > max, is_creation, is_creation, gas_creation, gas_txndata, retdest - MUL // Cheaper than AND - %assert_zero - // stack: is_creation, gas_creation, gas_txndata, retdest - %mload_txn_field(@TXN_FIELD_DATA_LEN) %num_bytes_to_num_words - // stack: initcode_words, is_creation, gas_creation, gas_txndata, retdest - %mul_const(@INITCODE_WORD_COST) MUL ADD - // stack: gas_creation, gas_txndata, retdest - - PUSH @GAS_TRANSACTION - // stack: gas_txn, gas_creation, gas_txndata, retdest - - ADD - ADD - // stack: total_gas, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) - ADD - - SWAP1 - JUMP - -// Convenience macro to call intrinsic_gas and return where we left off. -%macro intrinsic_gas - // stack: (empty) - PUSH %%after - %jump(intrinsic_gas) -%%after: - // stack: (empty) -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm deleted file mode 100644 index 934d1f6297..0000000000 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ /dev/null @@ -1,344 +0,0 @@ -// Set @SEGMENT_JUMPDEST_BITS to one between positions [init_pos, final_pos], -// for the given context's code. -// Pre stack: init_pos, ctx, final_pos, retdest -// Post stack: (empty) -global verify_path_and_write_jumpdest_table: - SWAP2 - DUP2 - ADD // final_addr - // stack: final_addr, ctx, i, retdest - SWAP2 - ADD // init_addr -loop: - // stack: i, final_pos, retdest - DUP2 DUP2 EQ // i == final_pos - %jumpi(proof_ok) - DUP2 DUP2 GT // i > final_pos - %jumpi(proof_not_ok) - - // stack: i, final_pos, retdest - DUP1 - MLOAD_GENERAL // SEGMENT_CODE == 0 - // stack: opcode, i, final_pos, retdest - - DUP1 - // Slightly more efficient than `%eq_const(0x5b) ISZERO` - PUSH 0x5b - SUB - // stack: opcode != JUMPDEST, opcode, i, final_pos, retdest - %jumpi(continue) - - // stack: JUMPDEST, i, code_len, retdest - %stack (JUMPDEST, i) -> (@SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i) - ADD // address to write jumpdest bit, i already contains the context - PUSH 1 - // stack: 1, addr, JUMPDEST, i - MSTORE_GENERAL - -continue: - // stack: opcode, i, final_pos, retdest - %add_const(code_bytes_to_skip) - %mload_kernel_code - // stack: bytes_to_skip, i, final_pos, retdest - ADD - // stack: i, final_pos, retdest - %jump(loop) - -proof_ok: - // stack: i, final_pos, retdest - // We already know final_pos is a jumpdest - %stack (i, final_pos) -> (@SEGMENT_JUMPDEST_BITS, final_pos) - ADD // final_pos already contains the context - PUSH 1 - MSTORE_GENERAL - JUMP -proof_not_ok: - %pop2 - JUMP - -// Determines how many bytes away is the next opcode, based on the opcode we read. -// If we read a PUSH opcode, next opcode is in n + 1 bytes, otherwise it's the next one. -// -// Note that the range of PUSH opcodes is [0x60, 0x80). I.e. PUSH1 is 0x60 -// and PUSH32 is 0x7f. -code_bytes_to_skip: - %rep 96 - BYTES 1 // 0x00-0x5f - %endrep - - BYTES 2 - BYTES 3 - BYTES 4 - BYTES 5 - BYTES 6 - BYTES 7 - BYTES 8 - BYTES 9 - BYTES 10 - BYTES 11 - BYTES 12 - BYTES 13 - BYTES 14 - BYTES 15 - BYTES 16 - BYTES 17 - BYTES 18 - BYTES 19 - BYTES 20 - BYTES 21 - BYTES 22 - BYTES 23 - BYTES 24 - BYTES 25 - BYTES 26 - BYTES 27 - BYTES 28 - BYTES 29 - BYTES 30 - BYTES 31 - BYTES 32 - BYTES 33 - - %rep 128 - BYTES 1 // 0x80-0xff - %endrep - - -// A proof attesting that jumpdest is a valid jump destination is -// either 0 or an index 0 < i <= jumpdest - 32. -// A proof is valid if: -// - i == 0 and we can go from the first opcode to jumpdest and code[jumpdest] = 0x5b -// - i > 0 and: -// a) for j in {i+0,..., i+31} code[j] != PUSHk for all k >= 32 - j - i, -// b) we can go from opcode i+32 to jumpdest, -// c) code[jumpdest] = 0x5b. -// To reduce the number of instructions, when i > 32 we load all the bytes code[j], ..., -// code[j + 31] in a single 32-byte word, and check a) directly on the packed bytes. -// We perform the "packed verification" computing a boolean formula evaluated on the bits of -// code[j],..., code[j+31] of the form p_1 AND p_2 AND p_3 AND p_4 AND p_5, where: -// - p_k is either TRUE, for one subset of the j's which depends on k (for example, -// for k = 1, it is TRUE for the first 15 positions), or has_prefix_k => bit_{k + 1}_is_0 -// for the j's not in the subset. -// - has_prefix_k is a predicate that is TRUE if and only if code[j] has the same prefix of size k + 2 -// as PUSH{32-(j-i)}. -// stack: proof_prefix_addr, jumpdest, ctx, retdest -// stack: (empty) -global write_table_if_jumpdest: - // stack: proof_prefix_addr, jumpdest, ctx, retdest - %stack - (proof_prefix_addr, jumpdest, ctx) -> - (ctx, jumpdest, jumpdest, ctx, proof_prefix_addr) - ADD // combine context and offset to make an address (SEGMENT_CODE == 0) - MLOAD_GENERAL - // stack: opcode, jumpdest, ctx, proof_prefix_addr, retdest - - %jump_neq_const(0x5b, return) - - //stack: jumpdest, ctx, proof_prefix_addr, retdest - SWAP2 DUP1 - // stack: proof_prefix_addr, proof_prefix_addr, ctx, jumpdest - ISZERO - %jumpi(verify_path_and_write_jumpdest_table) - - - // stack: proof_prefix_addr, ctx, jumpdest, retdest - // If we are here we need to check that the next 32 bytes are less - // than JUMPXX for XX < 32 - i <=> opcode < 0x7f - i = 127 - i, 0 <= i < 32, - // or larger than 127 - - %stack - (proof_prefix_addr, ctx) -> - (ctx, proof_prefix_addr, 32, proof_prefix_addr, ctx) - ADD // combine context and offset to make an address (SEGMENT_CODE == 0) - MLOAD_32BYTES - // packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP1 %shl_const(1) - DUP2 %shl_const(2) - AND - // stack: (is_1_at_pos_2_and_3|(X)⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // X denotes any value in {0,1} and Z^i is Z repeated i times - NOT - // stack: (is_0_at_2_or_3|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP2 - OR - // stack: (is_1_at_1 or is_0_at_2_or_3|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // stack: (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Compute in_range and has_prefix' = - // - in_range = (0xFF|X⁷)³² and ~has_prefix' = ~has_prefix OR is_0_at_4, for the first 15 bytes - // - in_range = (has_prefix => is_0_at_4 |X⁷)³² and ~has_prefix' = ~has_prefix, for the next 15 bytes - // - in_range = (~has_prefix|X⁷)³² and ~has_prefix' = ~has_prefix, for the last byte. - DUP2 %shl_const(3) - NOT - // stack: (is_0_at_4|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00 - AND - // stack: (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP1 - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000 - AND - // stack: (is_0_at_4|X⁷)¹⁵|(0⁸)¹⁷, (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP3 - OR - // (~has_prefix'|X⁷)³², (is_0_at_4|X⁷)³¹|0⁸, (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP2 - OR - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000 - OR - // stack: (in_range|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Compute in_range' and ~has_prefix as - // - in_range' = in_range and has_prefix' = ~has_prefix OR is_0_at_5, for bytes in positions 1-7 and 16-23 - // - in_range' = in_range AND (has_prefix => is_0_at_5 |X⁷)³² and has_prefix' = ~has_prefix, for the rest. - - DUP3 %shl_const(4) - NOT - // stack: (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP1 - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000 - AND - // stack: (is_0_at_5|X⁷)⁷|(0⁸)⁸|(is_0_at_5|X⁷)⁸|(0⁸)⁸, (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP4 - OR - // stack: (~has_prefix'|X⁷)³², (is_0_at_5|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP3 - OR - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF000000000000000000 - OR - AND - // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Compute in_range' and ~has_prefix' as - // - in_range' = in_range and ~has_prefix' = ~has_prefix OR is_0_at_6, for bytes in positions 1-3, 8-11, 16-19, and 24-27 - // - in_range' = in_range AND (has_prefix => is_0_at_6 |X⁷)³² and ~has_prefix' = has_prefix, for the rest. - DUP3 %shl_const(5) - NOT - // stack: (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP1 - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF0000000000 - AND - // stack: (is_0_at_6|X⁷)³|(0⁸)⁴|((is_0_at_6|X⁷)⁴|(0⁸)⁴)³, (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP4 - OR - // stack: (~has_prefix'|X⁷)³², (is_0_at_6|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP3 - OR - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF0000000000 - OR - AND - // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Compute in_range' and ~has_prefix' as - // - in_range' = in_range and ~has_prefix' = has_prefix OR is_0_at_7, for bytes in 1, 4-5, 8-9, 12-13, 16-17, 20-21, 24-25, 28-29 - // - in_range' = in_range AND (has_prefix => is_0_at_7 |X⁷)³² and ~has_prefix' = ~has_prefix, for the rest. - DUP3 %shl_const(6) - NOT - // stack: (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP1 - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF000000 - AND - // stack: is_0_at_7|X⁷|(0⁸)²|((is_0_at_7|X⁷)²|(0⁸)²)⁷, (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP4 - OR - // (~has_prefix'|X⁷)³², (is_0_at_7|X⁷)³², (in_range|X⁷)³², (~has_prefix|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - SWAP3 - OR - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0xFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF000000 - OR - AND - // stack: (in_range'|X⁷)³², (~has_prefix'|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Compute in_range' as - // - in_range' = in_range, for odd positions - // - in_range' = in_range AND (has_prefix => is_0_at_8 |X⁷)³², for the rest - - SWAP1 - // stack: (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - DUP3 %shl_const(7) - NOT - // stack: (is_0_at_8|X⁷)³², (~has_prefix|X⁷)³², (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - OR - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF - OR - AND - // stack: (in_range|X⁷)³², packed_opcodes, proof_prefix_addr, ctx, jumpdest, retdest - - // Get rid of the irrelevant bits - // pos 0102030405060708091011121314151617181920212223242526272829303132 - PUSH 0x8080808080808080808080808080808080808080808080808080808080808080 - AND - %jump_neq_const(0x8080808080808080808080808080808080808080808080808080808080808080, return_pop_opcode) - POP - %add_const(32) - - // check the remaining path - %jump(verify_path_and_write_jumpdest_table) -return_pop_opcode: - POP -return: - // stack: proof_prefix_addr, ctx, jumpdest, retdest - // or - // stack: jumpdest, ctx, proof_prefix_addr, retdest - %pop3 - JUMP - -%macro write_table_if_jumpdest - %stack (proof_prefix_addr, jumpdest, ctx) -> (proof_prefix_addr, jumpdest, ctx, %%after) - %jump(write_table_if_jumpdest) -%%after: -%endmacro - -// Write the jumpdest table. This is done by -// non-deterministically guessing the sequence of jumpdest -// addresses used during program execution within the current context. -// For each jumpdest address we also non-deterministically guess -// a proof, which is another address in the code such that -// is_jumpdest doesn't abort, when the proof is at the top of the stack -// an the jumpdest address below. If that's the case we set the -// corresponding bit in @SEGMENT_JUMPDEST_BITS to 1. -// -// stack: ctx, code_len, retdest -// stack: (empty) -global jumpdest_analysis: - // If address > 0 then address is interpreted as address' + 1 - // and the next prover input should contain a proof for address'. - PROVER_INPUT(jumpdest_table::next_address) - DUP1 %jumpi(check_proof) - // If address == 0 there are no more jump destinations to check - POP -// This is just a hook used for avoiding verification of the jumpdest -// table in another context. It is useful during proof generation, -// allowing the avoidance of table verification when simulating user code. -global jumpdest_analysis_end: - %pop2 - JUMP -check_proof: - // stack: address, ctx, code_len, retdest - DUP3 DUP2 %assert_le - %decrement - // stack: proof, ctx, code_len, retdest - DUP2 SWAP1 - // stack: address, ctx, ctx, code_len, retdest - // We read the proof - PROVER_INPUT(jumpdest_table::next_proof) - // stack: proof, address, ctx, ctx, code_len, retdest - %write_table_if_jumpdest - // stack: ctx, code_len, retdest - - %jump(jumpdest_analysis) - -%macro jumpdest_analysis - %stack (ctx, code_len) -> (ctx, code_len, %%after) - %jump(jumpdest_analysis) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/log.asm b/evm/src/cpu/kernel/asm/core/log.asm deleted file mode 100644 index f23d5e174c..0000000000 --- a/evm/src/cpu/kernel/asm/core/log.asm +++ /dev/null @@ -1,272 +0,0 @@ -global sys_log0: - %check_static - // stack: kexit_info, offset, size - DUP3 ISZERO %jumpi(log0_after_mem_gas) - DUP3 DUP3 - %add_or_fault - // stack: offset+size, kexit_info, offset, size - DUP1 %ensure_reasonable_offset - %update_mem_bytes -log0_after_mem_gas: - // stack: kexit_info, offset, size - DUP3 %mul_const(@GAS_LOGDATA) %add_const(@GAS_LOG) - // stack: gas, kexit_info, offset, size - %charge_gas - %address - PUSH 0 - %stack (zero, address, kexit_info, offset, size) -> (address, zero, size, offset, finish_sys_log, kexit_info) - %jump(log_n_entry) - -global sys_log1: - %check_static - // stack: kexit_info, offset, size, topic - DUP3 ISZERO %jumpi(log1_after_mem_gas) - DUP3 DUP3 - %add_or_fault - // stack: offset+size, kexit_info, offset, size, topic - DUP1 %ensure_reasonable_offset - %update_mem_bytes -log1_after_mem_gas: - // stack: kexit_info, offset, size, topic - DUP3 %mul_const(@GAS_LOGDATA) %add_const(@GAS_LOG) %add_const(@GAS_LOGTOPIC) - // stack: gas, kexit_info, offset, size, topic - %charge_gas - %address - PUSH 1 - %stack (one, address, kexit_info, offset, size, topic) -> (address, one, topic, size, offset, finish_sys_log, kexit_info) - %jump(log_n_entry) - -global sys_log2: - %check_static - // stack: kexit_info, offset, size, topic1, topic2 - DUP3 ISZERO %jumpi(log2_after_mem_gas) - DUP3 DUP3 - %add_or_fault - // stack: offset+size, kexit_info, offset, size, topic1, topic2 - DUP1 %ensure_reasonable_offset - %update_mem_bytes -log2_after_mem_gas: - // stack: kexit_info, offset, size, topic1, topic2 - DUP3 %mul_const(@GAS_LOGDATA) %add_const(@GAS_LOG) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) - // stack: gas, kexit_info, offset, size, topic1, topic2 - %charge_gas - %address - PUSH 2 - %stack (two, address, kexit_info, offset, size, topic1, topic2) -> (address, two, topic1, topic2, size, offset, finish_sys_log, kexit_info) - %jump(log_n_entry) - -global sys_log3: - %check_static - // stack: kexit_info, offset, size, topic1, topic2, topic3 - DUP3 ISZERO %jumpi(log3_after_mem_gas) - DUP3 DUP3 - %add_or_fault - // stack: offset+size, kexit_info, offset, size, topic1, topic2, topic3 - DUP1 %ensure_reasonable_offset - %update_mem_bytes -log3_after_mem_gas: - // stack: kexit_info, offset, size, topic1, topic2, topic3 - DUP3 %mul_const(@GAS_LOGDATA) %add_const(@GAS_LOG) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) - // stack: gas, kexit_info, offset, size, topic1, topic2, topic3 - %charge_gas - %address - PUSH 3 - %stack (three, address, kexit_info, offset, size, topic1, topic2, topic3) -> (address, three, topic1, topic2, topic3, size, offset, finish_sys_log, kexit_info) - %jump(log_n_entry) - -global sys_log4: - %check_static - // stack: kexit_info, offset, size, topic1, topic2, topic3, topic4 - DUP3 ISZERO %jumpi(log4_after_mem_gas) - DUP3 DUP3 - %add_or_fault - // stack: offset+size, kexit_info, offset, size, topic1, topic2, topic3, topic4 - DUP1 %ensure_reasonable_offset - %update_mem_bytes -log4_after_mem_gas: - // stack: kexit_info, offset, size, topic1, topic2, topic3, topic4 - DUP3 %mul_const(@GAS_LOGDATA) %add_const(@GAS_LOG) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) %add_const(@GAS_LOGTOPIC) - // stack: gas, kexit_info, offset, size, topic1, topic2, topic3, topic4 - %charge_gas - %address - PUSH 4 - %stack (four, address, kexit_info, offset, size, topic1, topic2, topic3, topic4) -> (address, four, topic1, topic2, topic3, topic4, size, offset, finish_sys_log, kexit_info) - %jump(log_n_entry) - -finish_sys_log: - // stack: kexit_info - EXIT_KERNEL - -global log_n_entry: - // stack: address, num_topics, topics, data_len, data_offset, retdest - %mload_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - %mload_global_metadata(@GLOBAL_METADATA_LOGS_DATA_LEN) - // stack: log_ptr, logs_len, address, num_topics, topics, data_len, data_offset, retdest - DUP1 DUP3 - // stack: log_ptr, logs_len, log_ptr, logs_len, address, num_topics, topics, data_len, data_offset, retdest - %mstore_kernel(@SEGMENT_LOGS) - // stack: log_ptr, logs_len, address, num_topics, topics, data_len, data_offset, retdest - SWAP1 %increment - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - // stack: log_ptr, address, num_topics, topics, data_len, data_offset, retdest - %increment - // stack: addr_ptr, address, num_topics, topics, data_len, data_offset, retdest - // Store the address. - DUP2 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - // stack: num_topics_ptr, address, num_topics, topics, data_len, data_offset, retdest - SWAP1 POP - // stack: num_topics_ptr, num_topics, topics, data_len, data_offset, retdest - // Store num_topics. - DUP2 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - // stack: topics_ptr, num_topics, topics, data_len, data_offset, retdest - DUP2 - // stack: num_topics, topics_ptr, num_topics, topics, data_len, data_offset, retdest - ISZERO - %jumpi(log_after_topics) - // stack: topics_ptr, num_topics, topics, data_len, data_offset, retdest - // Store the first topic. - DUP3 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - %stack (curr_topic_ptr, num_topics, topic1) -> (curr_topic_ptr, num_topics) - DUP2 %eq_const(1) - %jumpi(log_after_topics) - // stack: curr_topic_ptr, num_topics, remaining_topics, data_len, data_offset, retdest - // Store the second topic. - DUP3 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - %stack (curr_topic_ptr, num_topics, topic2) -> (curr_topic_ptr, num_topics) - DUP2 %eq_const(2) - %jumpi(log_after_topics) - // stack: curr_topic_ptr, num_topics, remaining_topics, data_len, data_offset, retdest - // Store the third topic. - DUP3 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - %stack (curr_topic_ptr, num_topics, topic3) -> (curr_topic_ptr, num_topics) - DUP2 %eq_const(3) - %jumpi(log_after_topics) - // stack: curr_topic_ptr, num_topics, remaining_topic, data_len, data_offset, retdest - // Store the fourth topic. - DUP3 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - %stack (data_len_ptr, num_topics, topic4) -> (data_len_ptr, num_topics) - DUP2 %eq_const(4) - %jumpi(log_after_topics) - // Invalid num_topics. - PANIC - -log_after_topics: - // stack: data_len_ptr, num_topics, data_len, data_offset, retdest - // Compute RLP length of the log. - DUP3 - // stack: data_len, data_len_ptr, num_topics, data_len, data_offset, retdest - DUP5 SWAP1 - %rlp_data_len - // stack: rlp_data_len, data_len_ptr, num_topics, data_len, data_offset, retdest - DUP3 - // stack: num_topics, rlp_data_len, data_len_ptr, num_topics, data_len, data_offset, retdest - // Each topic is encoded with 1+32 bytes. - %mul_const(33) - %rlp_list_len - // stack: rlp_topics_len, rlp_data_len, data_len_ptr, num_topics, data_len, data_offset, retdest - ADD - // The address is encoded with 1+20 bytes. - %add_const(21) - // stack: log_payload_len, data_len_ptr, num_topics, data_len, data_offset, retdest - %mload_global_metadata(@GLOBAL_METADATA_LOGS_DATA_LEN) - DUP2 SWAP1 - // stack: log_ptr, log_payload_len, log_payload_len, data_len_ptr, num_topics, data_len, data_offset, retdest - %mstore_kernel(@SEGMENT_LOGS_DATA) - // stack: log_payload_len, data_len_ptr, num_topics, data_len, data_offset, retdest - %rlp_list_len - // stack: rlp_log_len, data_len_ptr, num_topics, data_len, data_offset, retdest - %mload_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - // Add payload length and logs_data_len to journal. - DUP1 %mload_global_metadata(@GLOBAL_METADATA_LOGS_DATA_LEN) %journal_add_log - ADD - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - // stack: data_len_ptr, num_topics, data_len, data_offset, retdest - // Store data_len. - DUP3 DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - %increment - // stack: data_ptr, num_topics, data_len, data_offset, retdest - SWAP1 POP - // stack: data_ptr, data_len, data_offset, retdest - DUP1 SWAP2 - // stack: data_len, data_ptr, data_ptr, data_offset, retdest - ADD - // stack: next_log_ptr, data_ptr, data_offset, retdest - SWAP1 - // stack: data_ptr, next_log_ptr, data_offset, retdest - SWAP2 - PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT %build_address - SWAP2 - // stack: data_ptr, next_log_ptr, data_addr, retdest - - -store_log_data_loop: - // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest - DUP2 DUP2 EQ - // stack: cur_data_ptr == next_log_ptr, cur_data_ptr, next_log_ptr, cur_data_addr, retdest - %jumpi(store_log_data_loop_end) - // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest - DUP3 - MLOAD_GENERAL - // stack: cur_data, cur_data_ptr, next_log_ptr, cur_data_addr, retdest - // Store current data byte. - DUP2 - %mstore_kernel(@SEGMENT_LOGS_DATA) - // stack: cur_data_ptr, next_log_ptr, cur_data_addr, retdest - SWAP2 %increment SWAP2 - // stack: cur_data_ptr, next_log_ptr, next_data_addr, retdest - %increment - %jump(store_log_data_loop) - -store_log_data_loop_end: - // stack: cur_data_ptr, next_log_ptr, cur_data_offset, retdest - POP - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_DATA_LEN) - POP - JUMP - -rlp_data_len: - // stack: data_len, data_ptr, retdest - DUP1 ISZERO %jumpi(data_single_byte) // data will be encoded with a single byte - DUP1 PUSH 1 EQ %jumpi(one_byte_data) // data is encoded with either 1 or 2 bytes - // If we are here, data_len >= 2, and we can use rlp_list_len to determine the encoding length - %rlp_list_len - // stack: rlp_data_len, data_ptr, retdest - SWAP1 POP SWAP1 - JUMP - -data_single_byte: - // stack: data_len, data_ptr, retdest - %pop2 - PUSH 1 - SWAP1 - JUMP - -one_byte_data: - // stack: data_len, data_ptr, retdest - DUP2 - %mload_current(@SEGMENT_MAIN_MEMORY) - // stack: data_byte, data_len, data_ptr, retdest - %lt_const(0x80) %jumpi(data_single_byte) // special byte that only requires one byte to be encoded - %pop2 - PUSH 2 SWAP1 - JUMP - -%macro rlp_data_len - // stack: data_len, data_ptr - %stack (data_len, data_ptr) -> (data_len, data_ptr, %%after) - %jump(rlp_data_len) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/nonce.asm b/evm/src/cpu/kernel/asm/core/nonce.asm deleted file mode 100644 index 48486be9e2..0000000000 --- a/evm/src/cpu/kernel/asm/core/nonce.asm +++ /dev/null @@ -1,49 +0,0 @@ -// Get the nonce of the given account. -// Pre stack: address, retdest -// Post stack: (empty) -global nonce: - // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - // The nonce is the first account field, so we deref the account pointer itself. - // Note: We don't need to handle account_ptr=0, as trie_data[0] = 0, - // so the deref will give 0 (the default nonce) as desired. - %mload_trie_data - // stack: nonce, retdest - SWAP1 JUMP - -// Convenience macro to call nonce and return where we left off. -%macro nonce - %stack (address) -> (address, %%after) - %jump(nonce) -%%after: -%endmacro - -// Increment the given account's nonce. Assumes the account already exists; panics otherwise. -global increment_nonce: - // stack: address, retdest - DUP1 - %mpt_read_state_trie - // stack: account_ptr, address, retdest - DUP1 ISZERO %jumpi(increment_nonce_no_such_account) - // stack: nonce_ptr, address, retdest - DUP1 %mload_trie_data - // stack: nonce, nonce_ptr, address, retdest - DUP1 DUP4 %journal_add_nonce_change - // stack: nonce, nonce_ptr, address, retdest - %increment - SWAP1 - // stack: nonce_ptr, nonce', address, retdest - %mstore_trie_data - // stack: address, retdest - POP - JUMP -global increment_nonce_no_such_account: - PANIC - -// Convenience macro to call increment_nonce and return where we left off. -%macro increment_nonce - %stack (address) -> (address, %%after) - %jump(increment_nonce) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm deleted file mode 100644 index 91d4b3960f..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm +++ /dev/null @@ -1,139 +0,0 @@ -global precompile_blake2_f: - // stack: retdest, new_ctx, (old stack) - POP - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - PUSH blake2_f_contd - // stack: blake2_f_contd, kexit_info - - // Load inputs from calldata memory into stack. - - %calldatasize - // stack: calldatasize, blake2_f_contd, kexit_info - DUP1 - // stack: calldatasize, calldatasize, blake2_f_contd, kexit_info - %eq_const(213) ISZERO %jumpi(fault_exception) - // stack: calldatasize, blake2_f_contd, kexit_info - %decrement - // stack: flag_addr=212, blake2_f_contd, kexit_info - DUP1 - // stack: flag_addr, flag_addr, blake2_f_contd, kexit_info - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - %build_address - // stack: addr, flag_addr, blake2_f_contd, kexit_info - MLOAD_GENERAL - // stack: flag, flag_addr, blake2_f_contd, kexit_info - DUP1 - // stack: flag, flag, flag_addr, blake2_f_contd, kexit_info - %gt_const(1) %jumpi(fault_exception) // Check flag < 2 (flag = 0 or flag = 1) - // stack: flag, flag_addr, blake2_f_contd, kexit_info - SWAP1 - // stack: flag_addr, flag, blake2_f_contd, kexit_info - %sub_const(8) - // stack: t1_addr=flag_addr-8, flag, blake2_f_contd, kexit_info - - %stack (t1_addr) -> (@SEGMENT_CALLDATA, t1_addr, t1_addr) - // stack: @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info - %build_address - %mload_packing_u64_LE - // stack: t_1, t1_addr, flag, blake2_f_contd, kexit_info - SWAP1 - // stack: t1_addr, t_1, flag, blake2_f_contd, kexit_info - %sub_const(8) - // stack: t0_addr=t1_addr-8, t_1, flag, blake2_f_contd, kexit_info - - %stack (t0_addr) -> (@SEGMENT_CALLDATA, t0_addr, t0_addr) - // stack: @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info - %build_address - %mload_packing_u64_LE - // stack: t_0, t0_addr, t_1, flag, blake2_f_contd, kexit_info - SWAP1 - // stack: t0_addr = m0_addr + 8 * 16, t_0, t_1, flag, blake2_f_contd, kexit_info - - %rep 16 - // stack: m0_addr + 8 * (16 - i), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %sub_const(8) - // stack: m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - DUP1 - // stack: m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - PUSH @SEGMENT_CALLDATA - // stack: @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %build_address - %mload_packing_u64_LE - // stack: m_i, m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - SWAP1 - // stack: m0_addr + 8 * (16 - i - 1), m_i, m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %endrep - // stack: m0_addr = h0_addr + 8 * 8, m_0, ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - - %rep 8 - // stack: h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %sub_const(8) - // stack: h0_addr + 8 * (8 - i - 1), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - DUP1 - // stack: h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - PUSH @SEGMENT_CALLDATA - // stack: @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %build_address - %mload_packing_u64_LE - // stack: h_i, h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - SWAP1 - // stack: h0_addr + 8 * (8 - i), h_i, h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %endrep - // stack: h0_addr + 8 * 8 = 68, h_0, ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - POP - - %stack () -> (@SEGMENT_CALLDATA, 4) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %build_address_no_offset - MLOAD_32BYTES - // stack: rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - - DUP1 - // stack: rounds, rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %charge_gas - - // stack: rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info - %jump(blake2_f) -blake2_f_contd: - // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info - // Store the result hash to the parent's return data using `mstore_unpacking_u64_LE`. - - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) - // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info - PUSH @SEGMENT_RETURNDATA - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - // stack: parent_ctx, segment, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info - %build_address_no_offset - // stack: addr0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info - - %rep 8 - // stack: addri, h_i', ..., h_7', kexit_info - %stack (addr, h_i) -> (addr, h_i, addr) - %mstore_unpacking_u64_LE - // stack: addr_i, h_(i+1)', ..., h_7', kexit_info - %add_const(8) - // stack: addr_(i+1), h_(i+1)', ..., h_7', kexit_info - %endrep - - // stack: kexit_info - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm deleted file mode 100644 index 9554044eff..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm +++ /dev/null @@ -1,63 +0,0 @@ -global precompile_bn_add: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %charge_gas_const(@BN_ADD_GAS) - - // Load x0, y0, x1, y1 from the call data using `MLOAD_32BYTES`. - PUSH bn_add_return - // stack: bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 96, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 96, 32, bn_add_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: y1, bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 64, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 64, 32, y1, bn_add_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: x1, y1, bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, 32, x1, y1, bn_add_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: y0, x1, y1, bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, y0, x1, y1, bn_add_return, kexit_info - %build_address_no_offset - MLOAD_32BYTES - // stack: x0, y0, x1, y1, bn_add_return, kexit_info - %jump(bn_add) -bn_add_return: - // stack: x, y, kexit_info - DUP2 %eq_const(@U256_MAX) // bn_add returns (U256_MAX, U256_MAX) on bad input. - DUP2 %eq_const(@U256_MAX) // bn_add returns (U256_MAX, U256_MAX) on bad input. - MUL // Cheaper than AND - %jumpi(fault_exception) - // stack: x, y, kexit_info - - // Store the result (x, y) to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, x, parent_ctx, y) - %build_address_no_offset - MSTORE_32BYTES_32 - POP - %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y) - %build_address - MSTORE_32BYTES_32 - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm deleted file mode 100644 index 5872e17f26..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm +++ /dev/null @@ -1,58 +0,0 @@ -global precompile_bn_mul: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %charge_gas_const(@BN_MUL_GAS) - - // Load x, y, n from the call data using `MLOAD_32BYTES`. - PUSH bn_mul_return - // stack: bn_mul_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 64, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 64, 32, bn_mul_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: n, bn_mul_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, 32, n, bn_mul_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: y, n, bn_mul_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, y, n, bn_mul_return, kexit_info - %build_address_no_offset - MLOAD_32BYTES - // stack: x, y, n, bn_mul_return, kexit_info - %jump(bn_mul) -bn_mul_return: - // stack: Px, Py, kexit_info - DUP2 %eq_const(@U256_MAX) // bn_mul returns (U256_MAX, U256_MAX) on bad input. - DUP2 %eq_const(@U256_MAX) // bn_mul returns (U256_MAX, U256_MAX) on bad input. - MUL // Cheaper than AND - %jumpi(fault_exception) - // stack: Px, Py, kexit_info - - // Store the result (Px, Py) to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, Px, parent_ctx, Py) - %build_address_no_offset - MSTORE_32BYTES_32 -bn_mul_contd6: - POP - %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py) - %build_address - MSTORE_32BYTES_32 - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm deleted file mode 100644 index 6c141aabc5..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm +++ /dev/null @@ -1,60 +0,0 @@ -global precompile_ecrec: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %charge_gas_const(@ECREC_GAS) - - // Load hash, v, r, s from the call data using `MLOAD_32BYTES`. - PUSH ecrec_return - // stack: ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 96, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 96, 32, ecrec_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: s, ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 64, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 64, 32, s, ecrec_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: r, s, ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, 32, r, s, ecrec_return, kexit_info - %build_address - MLOAD_32BYTES - // stack: v, r, s, ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 32) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, v, r, s, ecrec_return, kexit_info - %build_address_no_offset - MLOAD_32BYTES - // stack: hash, v, r, s, ecrec_return, kexit_info - %jump(ecrecover) -ecrec_return: - // stack: address, kexit_info - DUP1 %eq_const(@U256_MAX) %jumpi(ecrec_bad_input) // ecrecover returns U256_MAX on bad input. - - // Store the result address to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address) - %build_address_no_offset - MSTORE_32BYTES_32 - %jump(pop_and_return_success) - -// On bad input, return empty return data but still return success. -ecrec_bad_input: - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm deleted file mode 100644 index 6bff54ea4e..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm +++ /dev/null @@ -1,470 +0,0 @@ -// Mod 16 to the range [1, 16]. -%macro mod_16 - // stack: x - %mod_const(16) - DUP1 %jumpi(%%after) - POP PUSH 16 -%%after: -%endmacro - -// Load bytes, packing 16 bytes into each limb, and store limbs on the stack. -// We pass around total_num_limbs and len for conveience, because we can't access them from the stack -// if they're hidden behind the variable number of limbs. -mload_bytes_as_limbs: - // stack: addr, num_bytes, retdest, total_num_limbs, len, ..limbs - DUP2 - // stack: num_bytes, addr, num_bytes, retdest, total_num_limbs, len, ..limbs - %mod_16 - // stack: min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs - DUP2 - // stack: addr, min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs - MLOAD_32BYTES - // stack: new_limb, addr, num_bytes, retdest, total_num_limbs, len, ..limbs - %stack (new, addr, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) - // stack: num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs - DUP1 - %mod_16 - // stack: num_bytes%16, num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs - DUP1 SWAP2 - SUB - // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs - DUP1 - ISZERO - %jumpi(mload_bytes_return) - SWAP1 - // stack: num_bytes%16, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs - DUP3 // addr - ADD // increment offset - // stack: addr_new, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs - SWAP2 POP - // stack: num_bytes_new, addr_new, retdest, total_num_limbs, len, new_limb, ..limbs - SWAP1 - %jump(mload_bytes_as_limbs) -mload_bytes_return: - // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs - %pop3 - // stack: retdest, total_num_limbs, len, ..limbs - JUMP - -%macro mload_bytes_as_limbs - %stack (addr, num_bytes, total_num_limbs) -> (addr, num_bytes, %%after, total_num_limbs) - %jump(mload_bytes_as_limbs) -%%after: -%endmacro - -store_limbs: - // stack: offset, retdest, num_limbs, limb[num_limbs - 1], ..limb[0] - DUP3 - // stack: num_limbs, offset, retdest, num_limbs, limb[num_limbs - 1], ..limb[0] - ISZERO - %jumpi(store_limbs_return) - // stack: offset, retdest, num_limbs, limb[num_limbs - 1], ..limb[0] - %stack (offset, ret, num, limb) -> (offset, limb, offset, ret, num) - // stack: offset, limb[num_limbs - 1], offset, retdest, num_limbs, limb[num_limbs - 2], ..limb[0] - %mstore_current_general - // stack: offset, retdest, num_limbs, limb[num_limbs - 2], ..limb[0] - %increment - SWAP2 - %decrement - SWAP2 - // stack: offset + 1, retdest, num_limbs - 1, limb[num_limbs - 2], ..limb[0] - %jump(store_limbs) -store_limbs_return: - // stack: offset, retdest, num_limbs=0 - POP - SWAP1 - POP - JUMP - -%macro store_limbs - %stack (offset, num_limbs) -> (offset, %%after, num_limbs) - %jump(store_limbs) -%%after: -%endmacro - -%macro expmod_gas_f - // stack: x - // Overflow check - DUP1 %ge_const(0x800000000000000000000000000000007) %jumpi(fault_exception) - // stack: x - %ceil_div_const(8) - // stack: ceil(x/8) - %square - // stack: ceil(x/8)^2 -%endmacro - -calculate_l_E_prime: - // stack: l_E, l_B, retdest - // Throw a fault early if the lengths are too large. - DUP2 %gt_const(0x100000000000000000000000000000000) %jumpi(fault_exception) - DUP1 %gt_const(0x100000000000000000000000000000000) %jumpi(fault_exception) - DUP1 ISZERO %jumpi(case_le_zero) - // stack: l_E, l_B, retdest - DUP1 %le_const(32) - // stack: l_E <= 32, l_E, l_B, retdest - %jumpi(case_le_32) - // stack: l_E, l_B, retdest - PUSH 32 - // stack: 32, l_E, l_B, retdest - DUP3 - // stack: l_B, 32, l_E, l_B, retdest - %add_const(96) - // stack: 96 + l_B, 32, l_E, l_B, retdest - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - %build_address - MLOAD_32BYTES - // stack: i[96 + l_B..128 + l_B], l_E, l_B, retdest - %log2_floor - // stack: log2(i[96 + l_B..128 + l_B]), l_E, l_B, retdest - SWAP1 - // stack: l_E, log2(i[96 + l_B..128 + l_B]), l_B, retdest - %sub_const(32) - // Overflow check - DUP1 %ge_const(0x2000000000000000000000000000000000000000000000000000000000000000) %jumpi(fault_exception) - %mul_const(8) - // stack: 8 * (l_E - 32), log2(i[96 + l_B..128 + l_B]), l_B, retdest - ADD - // stack: 8 * (l_E - 32) + log2(i[96 + l_B..128 + l_B]), l_B, retdest - SWAP1 - POP - // stack: 8 * (l_E - 32) + log2(i[96 + l_B..128 + l_B]), retdest - SWAP1 - // stack: retdest, 8 * (l_E - 32) + log2(i[96 + l_B..128 + l_B]) - JUMP -case_le_zero: - %stack (l_E, l_B, retdest) -> (retdest, 0) - JUMP -case_le_32: - // stack: l_E, l_B, retdest - SWAP1 - // stack: l_B, l_E, retdest - %add_const(96) - // stack: 96 + l_B, l_E, retdest - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - %build_address - MLOAD_32BYTES - // stack: E, retdest - %log2_floor - // stack: log2(E), retdest - SWAP1 - // stack: retdest, log2(E) - JUMP - -global precompile_expmod: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - // Load l_B from i[0..32]. - %stack () -> (@SEGMENT_CALLDATA, 32) - // stack: @SEGMENT_CALLDATA, 32, kexit_info - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 32, kexit_info - %build_address_no_offset - MLOAD_32BYTES - // stack: l_B, kexit_info - - // Load l_E from i[32..64]. - %stack () -> (@SEGMENT_CALLDATA, 32, 32) - GET_CONTEXT - %build_address - MLOAD_32BYTES - // stack: l_E, l_B, kexit_info - - // Load l_M from i[64..96]. - %stack () -> (@SEGMENT_CALLDATA, 64, 32) - GET_CONTEXT - %build_address - MLOAD_32BYTES - // stack: l_M, l_E, l_B, kexit_info - DUP3 ISZERO DUP2 ISZERO - MUL // AND - // stack: l_M==0 && l_B==0, l_M, l_E, l_B, kexit_info - %jumpi(zero_base_zero_mod) - %stack (l: 3) -> (l, l) - // stack: l_M, l_E, l_B, l_M, l_E, l_B, kexit_info - %max_3 - // stack: max_len, l_M, l_E, l_B, kexit_info - - %ceil_div_const(16) - // stack: len=ceil(max_len/16), l_M, l_E, l_B, kexit_info - - // Calculate gas costs. - - PUSH l_E_prime_return - // stack: l_E_prime_return, len, l_M, l_E, l_B, kexit_info - DUP5 - DUP5 - // stack: l_E, l_B, l_E_prime_return, len, l_M, l_E, l_B, kexit_info - %jump(calculate_l_E_prime) -l_E_prime_return: - // stack: l_E_prime, len, l_M, l_E, l_B, kexit_info - DUP5 - // stack: l_B, l_E_prime, len, l_M, l_E, l_B, kexit_info - DUP4 - // stack: l_M, l_B, l_E_prime, len, l_M, l_E, l_B, kexit_info - %max - // stack: max(l_M, l_B), l_E_prime, len, l_M, l_E, l_B, kexit_info - %expmod_gas_f - // stack: f(max(l_M, l_B)), l_E_prime, len, l_M, l_E, l_B, kexit_info - SWAP1 - // stack: l_E_prime, f(max(l_M, l_B)), len, l_M, l_E, l_B, kexit_info - %max_const(1) - // stack: max(1, l_E_prime), f(max(l_M, l_B)), len, l_M, l_E, l_B, kexit_info - MUL - // stack: max(1, l_E_prime) * f(max(l_M, l_B)), len, l_M, l_E, l_B, kexit_info - %div_const(3) // G_quaddivisor - // stack: (max(1, l_E_prime) * f(max(l_M, l_B))) / G_quaddivisor, len, l_M, l_E, l_B, kexit_info - %max_const(200) - // stack: g_r, len, l_M, l_E, l_B, kexit_info - %stack (g_r, l: 4, kexit_info) -> (g_r, kexit_info, l) - // stack: g_r, kexit_info, len, l_M, l_E, l_B - %charge_gas - // stack: kexit_info, len, l_M, l_E, l_B - %stack (kexit_info, l: 4) -> (l, kexit_info) - // stack: len, l_M, l_E, l_B, kexit_info - - // Copy B to memory. - // stack: len, l_M, l_E, l_B, kexit_info - DUP1 - // stack: len, len, l_M, l_E, l_B, kexit_info - DUP5 - // stack: num_bytes=l_B, len, len, l_M, l_E, l_B, kexit_info - DUP1 - %ceil_div_const(16) - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - DUP2 - ISZERO - %jumpi(copy_b_len_zero) - SWAP1 - // stack: num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 96) - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 96, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - %build_address - %mload_bytes_as_limbs - // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - SWAP1 - POP - // stack: num_limbs, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - PUSH 0 - // stack: b_loc=0, num_limbs, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - %store_limbs - // stack: len, l_M, l_E, l_B, kexit_info - %jump(copy_b_end) -copy_b_len_zero: - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - %pop3 -copy_b_end: - - // Copy E to memory. - // stack: len, l_M, l_E, l_B, kexit_info - DUP1 - // stack: len, len, l_M, l_E, l_B, kexit_info - DUP4 - // stack: num_bytes=l_E, len, len, l_M, l_E, l_B, kexit_info - DUP1 - %ceil_div_const(16) - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - DUP2 - ISZERO - %jumpi(copy_e_len_zero) - SWAP1 - // stack: num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - DUP7 - %add_const(96) - // stack: 96 + l_B, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - %build_address - %mload_bytes_as_limbs - // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - SWAP1 - // stack: e_loc=len, num_limbs, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - %store_limbs - // stack: len, l_M, l_E, l_B, kexit_info - %jump(copy_e_end) -copy_e_len_zero: - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - %pop3 -copy_e_end: - - // Copy M to memory. - // stack: len, l_M, l_E, l_B, kexit_info - DUP1 - // stack: len, len, l_M, l_E, l_B, kexit_info - DUP3 - // stack: num_bytes=l_M, len, len, l_M, l_E, l_B, kexit_info - DUP1 - %ceil_div_const(16) - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - DUP2 - ISZERO - %jumpi(copy_m_len_zero) - SWAP1 - // stack: num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - DUP7 - DUP7 - ADD - %add_const(96) - // stack: 96 + l_B + l_E, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B + l_E, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info - %build_address - %mload_bytes_as_limbs - // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - SWAP1 - %mul_const(2) - // stack: m_loc=2*len, num_limbs, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info - %store_limbs - // stack: len, l_M, l_E, l_B, kexit_info - %jump(copy_m_end) -copy_m_len_zero: - // stack: num_limbs, num_bytes, len, len, l_M, l_E, l_B, kexit_info - %pop3 -copy_m_end: - - %stack (len, l_M, ls: 2) -> (len, l_M) - // stack: len, l_M, kexit_info - - PUSH expmod_contd - // stack: expmod_contd, len, l_M, kexit_info - DUP2 - // stack: len, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(11) - // stack: s5=11*len, len, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(9) - // stack: s4=9*len, len, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, s4, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(7) - // stack: s3=7*len, len, s4, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(5) - // stack: s2=5*len, len, s3, s4, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(4) - // stack: s1=4*len, len, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(3) - // stack: out=3*len, len, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, out, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - DUP1 - %mul_const(2) - // stack: m_loc=2*len, len, out, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - SWAP1 - // stack: len, m_loc, out, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - PUSH 0 - // stack: b_loc=0, e_loc=len, m_loc, out, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - DUP2 - // stack: len, b_loc, e_loc, m_loc, out, s1, s2, s3, s4, s5, expmod_contd, len, l_M, kexit_info - - %jump(modexp_bignum) - -expmod_contd: - // stack: len, l_M, kexit_info - - // Copy the result value from memory to the parent's return data. - - // Store return data size: l_M (number of bytes). - SWAP1 - // stack: l_M, len, kexit_info - DUP1 %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) - // stack: l_M, len, kexit_info - DUP1 ISZERO %jumpi(zero_modulus) - // stack: l_M, len, kexit_info - DUP1 %ceil_div_const(16) - // stack: l_M_128, l_M, len, kexit_info - SWAP1 %mod_16 - // stack: l_M%16, l_M_128, len, kexit_info - SWAP2 - // stack: len, l_M_128, l_M%16, kexit_info - %mul_const(3) - // stack: out=3*len, l_M_128, l_M%16, kexit_info - %decrement - DUP2 - DUP2 - ADD - // stack: cur_offset=out+l_M_128-1, end_offset=out-1, l_M_128, l_M%16, kexit_info - DUP1 %mload_current_general - %stack (cur_limb, cur_offset, end_offset, l_M_128, l_M_mod16, kexit_info) -> - (@SEGMENT_RETURNDATA, cur_limb, l_M_mod16, cur_offset, end_offset, l_M_128, kexit_info) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %build_address_no_offset - %mstore_unpacking - // stack: address, cur_offset, end_offset, l_M_128, kexit_info - SWAP1 - %decrement - // stack: cur_offset, address, end_offset, l_M_128, kexit_info - // Store in big-endian format. -expmod_store_loop: - // stack: cur_offset, address, end_offset, l_M_128, kexit_info - DUP3 DUP2 EQ %jumpi(expmod_store_end) - // stack: cur_offset, address, end_offset, l_M_128, kexit_info - DUP1 %mload_current_general - %stack (cur_limb, cur_offset, address, end_offset, l_M_128, kexit_info) -> - (address, cur_limb, cur_offset, end_offset, l_M_128, kexit_info) - %stack (address, cur_limb) -> (address, cur_limb, 16) - %mstore_unpacking - // stack: address', cur_offset, end_offset, l_M_128, kexit_info) - SWAP1 %decrement - // stack: cur_offset-1, address', end_offset, l_M_128, kexit_info) - %jump(expmod_store_loop) -expmod_store_end: - // stack: cur_offset, address, end_offset, l_M_128, kexit_info - %pop4 -the_end: - // stack: kexit_info - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) - -zero_modulus: - // stack: l_M, len, kexit_info - %pop2 - %jump(the_end) - -zero_base_zero_mod: - // stack: l_M, l_E, l_B, kexit_info - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) - // stack: l_E, l_B, kexit_info - %pop2 - // stack: kexit_info - PUSH 200 - %charge_gas - // stack: kexit_info - %jump(the_end) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/id.asm b/evm/src/cpu/kernel/asm/core/precompiles/id.asm deleted file mode 100644 index a606ef4a85..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/id.asm +++ /dev/null @@ -1,47 +0,0 @@ -global precompile_id: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %calldatasize - %num_bytes_to_num_words - // stack: data_words_len, kexit_info - %mul_const(@ID_DYNAMIC_GAS) - PUSH @ID_STATIC_GAS - ADD - // stack: gas, kexit_info - %charge_gas - - // Simply copy the call data to the parent's return data. - %calldatasize - DUP1 %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) - - PUSH id_contd SWAP1 - - PUSH @SEGMENT_CALLDATA - GET_CONTEXT - %build_address_no_offset - // stack: SRC, size, id_contd - - PUSH @SEGMENT_RETURNDATA - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %build_address_no_offset - - // stack: DST, SRC, size, id_contd - %jump(memcpy_bytes) - -id_contd: - // stack: kexit_info - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/main.asm b/evm/src/cpu/kernel/asm/core/precompiles/main.asm deleted file mode 100644 index b7c916e9c4..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/main.asm +++ /dev/null @@ -1,69 +0,0 @@ -%macro handle_precompiles - // stack: address, new_ctx, (old stack) - PUSH %%after - SWAP1 - // stack: address, %%after, new_ctx, (old stack) - %jump(handle_precompiles) -%%after: - // stack: new_ctx, (old stack) -%endmacro - -global handle_precompiles: - // stack: address, retdest, new_ctx, (old stack) - DUP1 %eq_const(@ECREC) %jumpi(precompile_ecrec) - DUP1 %eq_const(@SHA256) %jumpi(precompile_sha256) - DUP1 %eq_const(@RIP160) %jumpi(precompile_rip160) - DUP1 %eq_const(@ID) %jumpi(precompile_id) - DUP1 %eq_const(@EXPMOD) %jumpi(precompile_expmod) - DUP1 %eq_const(@BN_ADD) %jumpi(precompile_bn_add) - DUP1 %eq_const(@BN_MUL) %jumpi(precompile_bn_mul) - DUP1 %eq_const(@SNARKV) %jumpi(precompile_snarkv) - %eq_const(@BLAKE2_F) %jumpi(precompile_blake2_f) - // stack: retdest - JUMP - -global pop_and_return_success: - // stack: _unused, kexit_info - POP - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) - -global after_precompile: - %mload_global_metadata(@GLOBAL_METADATA_IS_PRECOMPILE_FROM_EOA) %jumpi(process_message_txn_after_call) - %stack (success, leftover_gas, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size) - %jump(after_call_instruction) - -%macro handle_precompiles_from_eoa - // stack: retdest - %mload_txn_field(@TXN_FIELD_TO) - // stack: addr, retdest - DUP1 %is_precompile - %jumpi(handle_precompiles_from_eoa) - // stack: addr, retdest - POP -%endmacro - -global handle_precompiles_from_eoa: - PUSH 1 %mstore_global_metadata(@GLOBAL_METADATA_IS_PRECOMPILE_FROM_EOA) - // stack: addr, retdest - %create_context - // stack: new_ctx, addr, retdest - %non_intrinisic_gas %set_new_ctx_gas_limit - // stack: new_ctx, addr, retdest - - // Set calldatasize and copy txn data to calldata. - %mload_txn_field(@TXN_FIELD_DATA_LEN) - %stack (calldata_size, new_ctx) -> (calldata_size, new_ctx, calldata_size) - %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size) -> (@SEGMENT_TXN_DATA, @SEGMENT_CALLDATA, new_ctx, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) - SWAP2 %build_address_no_offset // DST - // stack: DST, SRC, calldata_size, handle_precompiles_from_eoa_finish, new_ctx - %jump(memcpy_bytes) - -handle_precompiles_from_eoa_finish: - %stack (new_ctx, addr, retdest) -> (addr, new_ctx, retdest) - %handle_precompiles - PANIC // We already checked that a precompile is called, so this should be unreachable. diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm deleted file mode 100644 index e57504961b..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm +++ /dev/null @@ -1,50 +0,0 @@ -global precompile_rip160: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %calldatasize - %num_bytes_to_num_words - // stack: data_words_len, kexit_info - %mul_const(@RIP160_DYNAMIC_GAS) - PUSH @RIP160_STATIC_GAS - ADD - // stack: gas, kexit_info - %charge_gas - - // Copy the call data to the kernel general segment (ripemd expects it there) and call ripemd. - %calldatasize - GET_CONTEXT - - %stack (ctx, size) -> - ( - ctx, @SEGMENT_CALLDATA, // SRC - ctx, - size, ripemd, // count, retdest - 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest - ) - %build_address_no_offset - %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 200, addr) - %build_address - // stack: DST, SRC, count, retdest, virt, num_bytes, retdest - - %jump(memcpy_bytes) - -rip160_contd: - // stack: hash, kexit_info - // Store the result hash to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash) - %build_address_no_offset - MSTORE_32BYTES_32 - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm deleted file mode 100644 index 3c926f0bbd..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm +++ /dev/null @@ -1,50 +0,0 @@ -global precompile_sha256: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - %calldatasize - %num_bytes_to_num_words - // stack: data_words_len, kexit_info - %mul_const(@SHA256_DYNAMIC_GAS) - PUSH @SHA256_STATIC_GAS - ADD - // stack: gas, kexit_info - %charge_gas - - // Copy the call data to the kernel general segment (sha2 expects it there) and call sha2. - %calldatasize - GET_CONTEXT - - %stack (ctx, size) -> - ( - ctx, @SEGMENT_CALLDATA, // SRC - ctx, - size, sha2, // count, retdest - 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest - ) - %build_address_no_offset - %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 1, addr) - %build_address - // stack: DST, SRC, count, retdest, virt, num_bytes, retdest - - %jump(memcpy_bytes) - -sha256_contd: - // stack: hash, kexit_info - // Store the result hash to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash) - %build_address_no_offset - MSTORE_32BYTES_32 - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm deleted file mode 100644 index 23ad9eb17d..0000000000 --- a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm +++ /dev/null @@ -1,130 +0,0 @@ -global precompile_snarkv: - // stack: address, retdest, new_ctx, (old stack) - %pop2 - // stack: new_ctx, (old stack) - %set_new_ctx_parent_pc(after_precompile) - // stack: new_ctx, (old stack) - DUP1 - SET_CONTEXT - %checkpoint // Checkpoint - %increment_call_depth - // stack: (empty) - PUSH 0x100000000 // = 2^32 (is_kernel = true) - // stack: kexit_info - - PUSH 192 %calldatasize DUP2 DUP2 - // stack: calldata_size, 192, calldata_size, 192, kexit_info - MOD %jumpi(fault_exception) // calldata_size should be a multiple of 192 - DIV - // stack: k, kexit_info - DUP1 %mul_const(@SNARKV_DYNAMIC_GAS) %add_const(@SNARKV_STATIC_GAS) - %stack (gas, k, kexit_info) -> (gas, kexit_info, k) - %charge_gas - SWAP1 - // stack: k, kexit_info - PUSH 0 -loading_loop: - // stack: i, k, kexit_info - DUP2 DUP2 EQ %jumpi(loading_done) - // stack: i, k, kexit_info - DUP1 %mul_const(192) - // stack: px, i, k, kexit_info - GET_CONTEXT - %stack (ctx, px) -> (ctx, @SEGMENT_CALLDATA, px, 32, px) - %build_address - MLOAD_32BYTES -loading_loop_contd: - // stack: x, px, i, k, kexit_info - SWAP1 %add_const(32) - GET_CONTEXT - %stack (ctx, py) -> (ctx, @SEGMENT_CALLDATA, py, 32, py) - %build_address - MLOAD_32BYTES -loading_loop_contd2: - // stack: y, py, x, i, k, kexit_info - SWAP1 %add_const(32) - GET_CONTEXT - %stack (ctx, px_im) -> (ctx, @SEGMENT_CALLDATA, px_im, 32, px_im) - %build_address - MLOAD_32BYTES -loading_loop_contd3: - // stack: x_im, px_im, y, x, i, k, kexit_info - SWAP1 %add_const(32) - // stack: px_re, x_im, y, x, i, k, kexit_info - GET_CONTEXT - %stack (ctx, px_re) -> (ctx, @SEGMENT_CALLDATA, px_re, 32, px_re) - %build_address - MLOAD_32BYTES -loading_loop_contd4: - // stack: x_re, px_re, x_im, y, x, i, k, kexit_info - SWAP1 %add_const(32) - // stack: py_im, x_re, x_im, y, x, i, k, kexit_info - GET_CONTEXT - %stack (ctx, py_im) -> (ctx, @SEGMENT_CALLDATA, py_im, 32, py_im) - %build_address - MLOAD_32BYTES -loading_loop_contd5: - // stack: y_im, py_im, x_re, x_im, y, x, i, k, kexit_info - SWAP1 %add_const(32) - // stack: py_re, y_im, x_re, x_im, y, x, i, k, kexit_info - GET_CONTEXT - %stack (ctx, py_re) -> (ctx, @SEGMENT_CALLDATA, py_re, 32) - %build_address - MLOAD_32BYTES -loading_loop_contd6: - // stack: y_re, y_im, x_re, x_im, y, x, i, k, kexit_info - SWAP1 // the EVM serializes the imaginary part first - // stack: y_im, y_re, x_re, x_im, y, x, i, k, kexit_info - DUP7 - // stack: i, y_im, y_re, x_re, x_im, y, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %add_const(5) - %mstore_bn254_pairing - // stack: y_re, x_re, x_im, y, x, i, k, kexit_info - DUP6 - // stack: i, y_re, x_re, x_im, y, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %add_const(4) - %mstore_bn254_pairing - SWAP1 // the EVM serializes the imaginary part first - // stack: x_im, x_re, y, x, i, k, kexit_info - DUP5 - // stack: i, x_im, x_re, y, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %add_const(3) - %mstore_bn254_pairing - // stack: x_re, y, x, i, k, kexit_info - DUP4 - // stack: i, x_re, y, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %add_const(2) - %mstore_bn254_pairing - // stack: y, x, i, k, kexit_info - DUP3 - // stack: i, y, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %add_const(1) - %mstore_bn254_pairing - // stack: x, i, k, kexit_info - DUP2 - // stack: i, x, i, k, kexit_info - %mul_const(6) %add_const(@SNARKV_INP) - %mstore_bn254_pairing - // stack: i, k, kexit_info - %increment - %jump(loading_loop) - -loading_done: - %stack (i, k) -> (k, @SNARKV_INP, @SNARKV_OUT, got_result) - %jump(bn254_pairing) -got_result: - // stack: result, kexit_info - DUP1 %eq_const(@U256_MAX) %jumpi(fault_exception) - // stack: result, kexit_info - // Store the result bool (repr. by a U256) to the parent's return data using `mstore_unpacking`. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address) - %build_address_no_offset - MSTORE_32BYTES_32 - %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm deleted file mode 100644 index c70287a6f9..0000000000 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ /dev/null @@ -1,472 +0,0 @@ -// After the transaction data has been parsed into a normalized set of fields -// (see NormalizedTxnField), this routine processes the transaction. - -// TODO: Save checkpoints in @CTX_METADATA_STATE_TRIE_CHECKPOINT_PTR and @SEGMENT_STORAGE_TRIE_CHECKPOINT_PTRS. - -// Pre stack: retdest -// Post stack: success, leftover_gas -global process_normalized_txn: - // stack: retdest - %compute_fees - // stack: retdest - - // Compute this transaction's intrinsic gas and store it. - %intrinsic_gas - DUP1 - %mstore_txn_field(@TXN_FIELD_INTRINSIC_GAS) - // stack: intrinsic_gas, retdest - - // Assert gas_limit >= intrinsic_gas. - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - %assert_ge(invalid_txn) - - // Assert block gas limit >= txn gas limit. - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_LIMIT) - %assert_ge(invalid_txn) - - %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: sender, retdest - - // Check that txn nonce matches account nonce. - DUP1 %nonce - DUP1 %eq_const(@MAX_NONCE) %assert_zero(invalid_txn_2) // EIP-2681 - // stack: sender_nonce, sender, retdest - %mload_txn_field(@TXN_FIELD_NONCE) - // stack: tx_nonce, sender_nonce, sender, retdest - %assert_eq(invalid_txn_1) - // stack: sender, retdest - - // Assert sender has no code. - DUP1 %ext_code_empty %assert_nonzero(invalid_txn_1) - // stack: sender, retdest - - // Assert sender balance >= gas_limit * gas_price + value. - %balance - // stack: sender_balance, retdest - %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - MUL - %mload_txn_field(@TXN_FIELD_VALUE) - ADD - %assert_le(invalid_txn) - // stack: retdest - - // Assert chain ID matches block metadata - %mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) - // stack: chain_id_present, retdest - DUP1 - %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: tx_chain_id, chain_id_present, chain_id_present, retdest - MUL SWAP1 - // stack: chain_id_present, filtered_tx_chain_id, retdest - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_CHAIN_ID) - MUL - // stack: filtered_block_chain_id, filtered_tx_chain_id, retdest - %assert_eq(invalid_txn) - // stack: retdest - -global buy_gas: - %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - MUL - // stack: gas_cost, retdest - %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: sender_addr, gas_cost, retdest - %deduct_eth - // stack: deduct_eth_status, retdest - %jumpi(panic) - // stack: retdest - -global increment_sender_nonce: - %mload_txn_field(@TXN_FIELD_ORIGIN) - DUP1 %increment_nonce - -global warm_origin: - // stack: origin, retdest - %insert_accessed_addresses_no_return - -global warm_precompiles: - // Add precompiles to accessed addresses. - PUSH @ECREC %insert_accessed_addresses_no_return - PUSH @SHA256 %insert_accessed_addresses_no_return - PUSH @RIP160 %insert_accessed_addresses_no_return - PUSH @ID %insert_accessed_addresses_no_return - PUSH @EXPMOD %insert_accessed_addresses_no_return - PUSH @BN_ADD %insert_accessed_addresses_no_return - PUSH @BN_MUL %insert_accessed_addresses_no_return - PUSH @SNARKV %insert_accessed_addresses_no_return - PUSH @BLAKE2_F %insert_accessed_addresses_no_return - -// EIP-3651 -global warm_coinbase: - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY) - %insert_accessed_addresses_no_return - -global process_based_on_type: - %is_contract_creation - %jumpi(process_contract_creation_txn) - %jump(process_message_txn) - -global process_contract_creation_txn: - // stack: retdest - - %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: origin, retdest - DUP1 %nonce - // stack: origin_nonce, origin, retdest - %decrement // Need the non-incremented nonce - SWAP1 - // stack: origin, origin_nonce, retdest - %get_create_address - // stack: address, retdest - DUP1 %insert_accessed_addresses_no_return - - %checkpoint - - // Create the new contract account in the state trie. - DUP1 - // stack: address, address, retdest - %create_contract_account - // stack: status, address, retdest - %jumpi(create_contract_account_fault) - - // stack: address, retdest - // Transfer value to new contract - DUP1 %mload_txn_field(@TXN_FIELD_VALUE) - SWAP1 - %mload_txn_field(@TXN_FIELD_ORIGIN) - DUP3 DUP3 DUP3 - %transfer_eth %jumpi(panic) - %journal_add_balance_transfer - // stack: address, retdest - - %create_context - // stack: new_ctx, address, retdest - - // Store constructor code length - PUSH @CTX_METADATA_CODE_SIZE - // stack: offset, new_ctx, address, retdest - DUP2 // new_ctx - ADD // CTX_METADATA_CODE_SIZE is already scaled by its segment - // stack: addr, new_ctx, address, retdest - %mload_txn_field(@TXN_FIELD_DATA_LEN) - // stack: data_len, addr, new_ctx, address, retdest - MSTORE_GENERAL - // stack: new_ctx, address, retdest - - // Copy the code from txdata to the new context's code segment. - PUSH process_contract_creation_txn_after_code_loaded - %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH @SEGMENT_TXN_DATA // SRC (context == offset == 0) - DUP4 // DST (segment == 0 (i.e. CODE), and offset == 0) - %jump(memcpy_bytes) - -global process_contract_creation_txn_after_code_loaded: - // stack: new_ctx, address, retdest - - // Each line in the block below does not change the stack. - DUP2 %set_new_ctx_addr - %mload_txn_field(@TXN_FIELD_ORIGIN) %set_new_ctx_caller - %mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value - %set_new_ctx_parent_ctx - %set_new_ctx_parent_pc(process_contract_creation_txn_after_constructor) - %non_intrinisic_gas %set_new_ctx_gas_limit - // stack: new_ctx, address, retdest - - %enter_new_ctx - // (Old context) stack: new_ctx, address, retdest - -global process_contract_creation_txn_after_constructor: - // stack: success, leftover_gas, new_ctx, address, retdest - // We eventually return leftover_gas and success. - %stack (success, leftover_gas, new_ctx, address, retdest) -> (success, leftover_gas, new_ctx, address, retdest, success) - - ISZERO %jumpi(contract_creation_fault_3) - - // EIP-3541: Reject new contract code starting with the 0xEF byte - PUSH 0 %mload_current(@SEGMENT_RETURNDATA) %eq_const(0xEF) %jumpi(contract_creation_fault_3_zero_leftover) - - // stack: leftover_gas, new_ctx, address, retdest, success - %returndatasize // Size of the code. - // stack: code_size, leftover_gas, new_ctx, address, retdest, success - DUP1 %gt_const(@MAX_CODE_SIZE) %jumpi(contract_creation_fault_4) - // stack: code_size, leftover_gas, new_ctx, address, retdest, success - %mul_const(@GAS_CODEDEPOSIT) SWAP1 - // stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest, success - DUP2 DUP2 LT %jumpi(contract_creation_fault_4) - // stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest, success - SUB - - // Store the code hash of the new contract. - // stack: leftover_gas, new_ctx, address, retdest, success - %returndatasize - PUSH @SEGMENT_RETURNDATA - GET_CONTEXT - %build_address_no_offset - // stack: addr, len - KECCAK_GENERAL - // stack: codehash, leftover_gas, new_ctx, address, retdest, success - %observe_new_contract - DUP4 - // stack: address, codehash, leftover_gas, new_ctx, address, retdest, success - %set_codehash - - %stack (leftover_gas, new_ctx, address, retdest, success) -> (leftover_gas, new_ctx, address, retdest, success, leftover_gas) - %pay_coinbase_and_refund_sender - // stack: leftover_gas', new_ctx, address, retdest, success, leftover_gas - SWAP5 POP - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - // stack: new_ctx, address, retdest, success, leftover_gas - POP - POP - JUMP - -global process_message_txn: - // stack: retdest - %mload_txn_field(@TXN_FIELD_VALUE) - %mload_txn_field(@TXN_FIELD_TO) - DUP1 %insert_accessed_addresses_no_return - %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: from, to, amount, retdest - %transfer_eth - // stack: transfer_eth_status, retdest - %jumpi(process_message_txn_insufficient_balance) - // stack: retdest - - %handle_precompiles_from_eoa - - // If to's code is empty, return. - %mload_txn_field(@TXN_FIELD_TO) %ext_code_empty - // stack: code_empty, retdest - %jumpi(process_message_txn_return) - - // Otherwise, load to's code and execute it in a new context. - // stack: retdest - %create_context - // stack: new_ctx, retdest - PUSH process_message_txn_code_loaded - DUP2 // new_ctx - %mload_txn_field(@TXN_FIELD_TO) - // stack: address, new_ctx, process_message_txn_code_loaded, new_ctx, retdest - %jump(load_code_padded) - -global process_message_txn_insufficient_balance: - // stack: retdest - PANIC // TODO - -global process_message_txn_return: - // stack: retdest - // Since no code was executed, the leftover gas is the non-intrinsic gas. - %non_intrinisic_gas - DUP1 - // stack: leftover_gas, leftover_gas, retdest - %pay_coinbase_and_refund_sender - // stack: leftover_gas', leftover_gas, retdest - SWAP1 POP - %delete_all_touched_addresses - // stack: leftover_gas', retdest - SWAP1 - PUSH 1 // success - SWAP1 - // stack: retdest, success, leftover_gas - JUMP - -global process_message_txn_code_loaded: - // stack: code_size, new_ctx, retdest - %set_new_ctx_code_size - // stack: new_ctx, retdest - - // Each line in the block below does not change the stack. - %mload_txn_field(@TXN_FIELD_TO) %set_new_ctx_addr - %mload_txn_field(@TXN_FIELD_ORIGIN) %set_new_ctx_caller - %mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value - %set_new_ctx_parent_ctx - %set_new_ctx_parent_pc(process_message_txn_after_call) - %non_intrinisic_gas %set_new_ctx_gas_limit - // stack: new_ctx, retdest - - // Set calldatasize and copy txn data to calldata. - %mload_txn_field(@TXN_FIELD_DATA_LEN) - %stack (calldata_size, new_ctx, retdest) -> (calldata_size, new_ctx, calldata_size, retdest) - %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size, retdest) -> (new_ctx, @SEGMENT_CALLDATA, @SEGMENT_TXN_DATA, calldata_size, process_message_txn_code_loaded_finish, new_ctx, retdest) - %build_address_no_offset // DST - %jump(memcpy_bytes) - -process_message_txn_code_loaded_finish: - %enter_new_ctx - // (Old context) stack: new_ctx, retdest - -global process_message_txn_after_call: - // stack: success, leftover_gas, new_ctx, retdest - // We will return leftover_gas and success. - %stack (success, leftover_gas, new_ctx, retdest) -> (success, leftover_gas, new_ctx, retdest, success, leftover_gas) - ISZERO %jumpi(process_message_txn_fail) -process_message_txn_after_call_contd: - // stack: leftover_gas, new_ctx, retdest, success, leftover_gas - %pay_coinbase_and_refund_sender - // stack: leftover_gas', new_ctx, retdest, success, leftover_gas - SWAP4 POP - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - // stack: new_ctx, retdest, success, leftover_gas - POP - JUMP - -process_message_txn_fail: - // stack: leftover_gas, new_ctx, retdest, success, leftover_gas - // Transfer value back to the caller. - %mload_txn_field(@TXN_FIELD_VALUE) ISZERO %jumpi(process_message_txn_after_call_contd) - %mload_txn_field(@TXN_FIELD_VALUE) - %mload_txn_field(@TXN_FIELD_ORIGIN) - %mload_txn_field(@TXN_FIELD_TO) - %transfer_eth %jumpi(panic) - %jump(process_message_txn_after_call_contd) - -%macro pay_coinbase_and_refund_sender - // stack: leftover_gas - DUP1 - // stack: leftover_gas, leftover_gas - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SUB - // stack: used_gas, leftover_gas - %mload_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) - // stack: refund, used_gas, leftover_gas - DUP2 %div_const(@MAX_REFUND_QUOTIENT) // max_refund = used_gas/5 - // stack: max_refund, refund, used_gas, leftover_gas - %min - %stack (refund, used_gas, leftover_gas) -> (leftover_gas, refund, refund, used_gas) - ADD - // stack: leftover_gas', refund, used_gas - SWAP2 - // stack: used_gas, refund, leftover_gas' - SUB - // stack: used_gas', leftover_gas' - - // Pay the coinbase. - %mload_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS) - MUL - // stack: used_gas_tip, leftover_gas' - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY) - // stack: coinbase, used_gas_tip, leftover_gas' - %add_eth - // stack: leftover_gas' - DUP1 - - // Refund gas to the origin. - %mload_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) - MUL - // stack: leftover_gas_cost, leftover_gas' - %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: origin, leftover_gas_cost, leftover_gas' - %add_eth - // stack: leftover_gas' -%endmacro - -// Sets @TXN_FIELD_MAX_FEE_PER_GAS and @TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS. -%macro compute_fees - // stack: (empty) - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BASE_FEE) - %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: max_fee, max_priority_fee, base_fee - DUP3 DUP2 %assert_ge(invalid_txn_3) // Assert max_fee >= base_fee - // stack: max_fee, max_priority_fee, base_fee - DUP2 DUP2 %assert_ge(invalid_txn_3) // Assert max_fee >= max_priority_fee - %stack (max_fee, max_priority_fee, base_fee) -> (max_fee, base_fee, max_priority_fee, base_fee) - SUB - // stack: max_fee - base_fee, max_priority_fee, base_fee - %min - // stack: computed_priority_fee, base_fee - %stack (computed_priority_fee, base_fee) -> (computed_priority_fee, base_fee, computed_priority_fee) - ADD - // stack: computed_fee, computed_priority_fee - %mstore_txn_field(@TXN_FIELD_COMPUTED_FEE_PER_GAS) - %mstore_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS) - // stack: (empty) -%endmacro - -%macro non_intrinisic_gas - // stack: (empty) - %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SUB - // stack: gas_limit - intrinsic_gas -%endmacro - -create_contract_account_fault: - %revert_checkpoint - // stack: address, retdest - POP - PUSH 0 // leftover_gas - // stack: leftover_gas, retdest - %pay_coinbase_and_refund_sender - // stack: leftover_gas', retdest - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - // stack: leftover_gas', retdest - SWAP1 PUSH 0 // success - // stack: success, retdest, leftover_gas - SWAP1 - JUMP - -contract_creation_fault_3: - %revert_checkpoint - %stack (leftover_gas, new_ctx, address, retdest, success) -> (leftover_gas, retdest, success) - %pay_coinbase_and_refund_sender - // stack: leftover_gas', retdest, success - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - %stack (leftover_gas, retdest, success) -> (retdest, 0, leftover_gas) - JUMP - -contract_creation_fault_3_zero_leftover: - %revert_checkpoint - // stack: leftover_gas, new_ctx, address, retdest, success - %pop3 - PUSH 0 // leftover gas - // stack: leftover_gas, retdest, success - %pay_coinbase_and_refund_sender - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - %stack (leftover_gas, retdest, success) -> (retdest, 0, leftover_gas) - JUMP - -contract_creation_fault_4: - %revert_checkpoint - // stack: code_size/leftover_gas, leftover_gas/codedeposit_cost, new_ctx, address, retdest, success - %pop4 - PUSH 0 // leftover gas - // stack: leftover_gas, retdest, success - %pay_coinbase_and_refund_sender - %delete_all_touched_addresses - %delete_all_selfdestructed_addresses - %stack (leftover_gas, retdest, success) -> (retdest, 0, leftover_gas) - JUMP - - -global invalid_txn: - POP - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - PUSH 0 - %jump(txn_after) - -global invalid_txn_1: - %pop2 - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - PUSH 0 - %jump(txn_after) - -global invalid_txn_2: - %pop3 - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - PUSH 0 - %jump(txn_after) - -global invalid_txn_3: - %pop4 - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - PUSH 0 - %jump(txn_after) diff --git a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm b/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm deleted file mode 100644 index 258f794054..0000000000 --- a/evm/src/cpu/kernel/asm/core/selfdestruct_list.asm +++ /dev/null @@ -1,78 +0,0 @@ -/// Self-destruct list. -/// Implemented as an array, with the length stored in the global metadata. -/// Note: This array allows duplicates. - -%macro insert_selfdestruct_list - // stack: addr - %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) - DUP1 PUSH @SEGMENT_SELFDESTRUCT_LIST %build_kernel_address - %stack (write_addr, len, addr) -> (addr, write_addr, len) - MSTORE_GENERAL // Store new address at the end of the array. - // stack: len - %increment - %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Store new length. -%endmacro - -/// Remove one occurrence of the address from the list. -/// Panics if the address is not in the list. -global remove_selfdestruct_list: - // stack: addr, retdest - %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) - // stack: len, addr, retdest - PUSH @SEGMENT_SELFDESTRUCT_LIST ADD - PUSH @SEGMENT_SELFDESTRUCT_LIST -remove_selfdestruct_list_loop: - // `i` and `len` are both scaled by SEGMENT_SELFDESTRUCT_LIST - %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(panic) - // stack: i, len, addr, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, addr, retdest - DUP4 - // stack: addr, loaded_addr, i, len, addr, retdest - EQ %jumpi(remove_selfdestruct_list_found) - // stack: i, len, addr, retdest - %increment - %jump(remove_selfdestruct_list_loop) -remove_selfdestruct_list_found: - %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB - PUSH @SEGMENT_SELFDESTRUCT_LIST - DUP2 SUB // unscale - %mstore_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) // Decrement the list length. - // stack: len-1, i, retdest - MLOAD_GENERAL // Load the last address in the list. - // stack: last_addr, i, retdest - MSTORE_GENERAL // Store the last address at the position of the removed address. - JUMP - -global delete_all_selfdestructed_addresses: - // stack: retdest - %mload_global_metadata(@GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN) - // stack: len, retdest - PUSH @SEGMENT_SELFDESTRUCT_LIST ADD - PUSH @SEGMENT_SELFDESTRUCT_LIST -delete_all_selfdestructed_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_SELFDESTRUCT_LIST - // stack: i, len, retdest - DUP2 DUP2 EQ %jumpi(delete_all_selfdestructed_addresses_done) - // stack: i, len, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, retdest - DUP1 %is_non_existent ISZERO %jumpi(bingo) - // stack: loaded_addr, i, len, retdest - POP %increment %jump(delete_all_selfdestructed_addresses_loop) -bingo: - // stack: loaded_addr, i, len, retdest - %delete_account - %increment %jump(delete_all_selfdestructed_addresses_loop) -delete_all_selfdestructed_addresses_done: - // stack: i, len, retdest - %pop2 JUMP - -%macro delete_all_selfdestructed_addresses - %stack () -> (%%after) - %jump(delete_all_selfdestructed_addresses) -%%after: - // stack: (empty) -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/syscall.asm b/evm/src/cpu/kernel/asm/core/syscall.asm deleted file mode 100644 index 5d1a6c95c0..0000000000 --- a/evm/src/cpu/kernel/asm/core/syscall.asm +++ /dev/null @@ -1,155 +0,0 @@ -global syscall_jumptable: - // 0x00-0x0f - JUMPTABLE sys_stop - JUMPTABLE panic // add is implemented natively - JUMPTABLE panic // mul is implemented natively - JUMPTABLE panic // sub is implemented natively - JUMPTABLE panic // div is implemented natively - JUMPTABLE sys_sdiv - JUMPTABLE panic // mod is implemented natively - JUMPTABLE sys_smod - JUMPTABLE panic // addmod is implemented natively - JUMPTABLE panic // mulmod is implemented natively - JUMPTABLE sys_exp - JUMPTABLE sys_signextend - JUMPTABLE panic // 0x0c is an invalid opcode - JUMPTABLE panic // 0x0d is an invalid opcode - JUMPTABLE panic // 0x0e is an invalid opcode - JUMPTABLE panic // 0x0f is an invalid opcode - - // 0x10-0x1f - JUMPTABLE panic // lt is implemented natively - JUMPTABLE panic // gt is implemented natively - JUMPTABLE sys_slt - JUMPTABLE sys_sgt - JUMPTABLE panic // eq is implemented natively - JUMPTABLE panic // iszero is implemented natively - JUMPTABLE panic // and is implemented natively - JUMPTABLE panic // or is implemented natively - JUMPTABLE panic // xor is implemented natively - JUMPTABLE panic // not is implemented natively - JUMPTABLE panic // byte is implemented natively - JUMPTABLE panic // shl is implemented natively - JUMPTABLE panic // shr is implemented natively - JUMPTABLE sys_sar - JUMPTABLE panic // 0x1e is an invalid opcode - JUMPTABLE panic // 0x1f is an invalid opcode - - // 0x20-0x2f - JUMPTABLE sys_keccak256 - %rep 15 - JUMPTABLE panic // 0x21-0x2f are invalid opcodes - %endrep - - // 0x30-0x3f - JUMPTABLE sys_address - JUMPTABLE sys_balance - JUMPTABLE sys_origin - JUMPTABLE sys_caller - JUMPTABLE sys_callvalue - JUMPTABLE sys_calldataload - JUMPTABLE sys_calldatasize - JUMPTABLE sys_calldatacopy - JUMPTABLE sys_codesize - JUMPTABLE sys_codecopy - JUMPTABLE sys_gasprice - JUMPTABLE sys_extcodesize - JUMPTABLE sys_extcodecopy - JUMPTABLE sys_returndatasize - JUMPTABLE sys_returndatacopy - JUMPTABLE sys_extcodehash - - // 0x40-0x4f - JUMPTABLE sys_blockhash - JUMPTABLE sys_coinbase - JUMPTABLE sys_timestamp - JUMPTABLE sys_number - JUMPTABLE sys_prevrandao - JUMPTABLE sys_gaslimit - JUMPTABLE sys_chainid - JUMPTABLE sys_selfbalance - JUMPTABLE sys_basefee - %rep 7 - JUMPTABLE panic // 0x49-0x4f are invalid opcodes - %endrep - - // 0x50-0x5f - JUMPTABLE panic // pop is implemented natively - JUMPTABLE sys_mload - JUMPTABLE sys_mstore - JUMPTABLE sys_mstore8 - JUMPTABLE sys_sload - JUMPTABLE sys_sstore - JUMPTABLE panic // jump is implemented natively - JUMPTABLE panic // jumpi is implemented natively - JUMPTABLE panic // pc is implemented natively - JUMPTABLE sys_msize - JUMPTABLE sys_gas - JUMPTABLE panic // jumpdest is implemented natively - JUMPTABLE panic // 0x5c is an invalid opcode - JUMPTABLE panic // 0x5d is an invalid opcode - JUMPTABLE panic // 0x5e is an invalid opcode - JUMPTABLE panic // 0x5f is an invalid opcode - - // 0x60-0x6f - %rep 16 - JUMPTABLE panic // push1-push16 are implemented natively - %endrep - - // 0x70-0x7f - %rep 16 - JUMPTABLE panic // push17-push32 are implemented natively - %endrep - - // 0x80-0x8f - %rep 16 - JUMPTABLE panic // dup1-dup16 are implemented natively - %endrep - - // 0x90-0x9f - %rep 16 - JUMPTABLE panic // swap1-swap16 are implemented natively - %endrep - - // 0xa0-0xaf - JUMPTABLE sys_log0 - JUMPTABLE sys_log1 - JUMPTABLE sys_log2 - JUMPTABLE sys_log3 - JUMPTABLE sys_log4 - %rep 11 - JUMPTABLE panic // 0xa5-0xaf are invalid opcodes - %endrep - - // 0xb0-0xbf - %rep 16 - JUMPTABLE panic // 0xb0-0xbf are invalid opcodes - %endrep - - // 0xc0-0xdf - %rep 32 - JUMPTABLE panic // mstore_32bytes_1-32 are implemented natively - %endrep - - // 0xe0-0xef - %rep 16 - JUMPTABLE panic // 0xe0-0xef are invalid opcodes - %endrep - - // 0xf0-0xff - JUMPTABLE sys_create - JUMPTABLE sys_call - JUMPTABLE sys_callcode - JUMPTABLE sys_return - JUMPTABLE sys_delegatecall - JUMPTABLE sys_create2 - JUMPTABLE panic // 0xf6 is an invalid opcode - JUMPTABLE panic // 0xf7 is an invalid opcode - JUMPTABLE panic // 0xf8 is an invalid opcode - JUMPTABLE panic // 0xf9 is an invalid opcode - JUMPTABLE sys_staticcall - JUMPTABLE panic // 0xfb is an invalid opcode - JUMPTABLE panic // 0xfc is an invalid opcode - JUMPTABLE sys_revert - JUMPTABLE panic // 0xfe is an invalid opcode - JUMPTABLE sys_selfdestruct diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm deleted file mode 100644 index 8572f34f28..0000000000 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ /dev/null @@ -1,225 +0,0 @@ -// Handlers for operations which terminate the current context, namely STOP, -// RETURN, SELFDESTRUCT, REVERT, and exceptions such as stack underflow. - -global sys_stop: - // stack: kexit_info - // Set the parent context's return data size to 0. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) - -global sys_return: - // stack: kexit_info, offset, size - %stack (kexit_info, offset, size) -> (offset, size, kexit_info, offset, size) - %add_or_fault - // stack: offset+size, kexit_info, offset, size - DUP4 ISZERO %jumpi(return_zero_size) - // stack: offset+size, kexit_info, offset, size - DUP1 %ensure_reasonable_offset - %update_mem_bytes - %jump(return_after_gas) -return_zero_size: - POP -return_after_gas: - // Load the parent's context. - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - - // Store the return data size in the parent context's metadata. - %stack (parent_ctx, kexit_info, offset, size) -> - (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) - ADD // addr (CTX offsets are already scaled by their segment) - SWAP1 - // stack: size, addr, offset, size, parent_ctx, kexit_info - MSTORE_GENERAL - // stack: offset, size, parent_ctx, kexit_info - - // Store the return data in the parent context's returndata segment. - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - - %stack (addr, size, parent_ctx, kexit_info) -> - ( - parent_ctx, @SEGMENT_RETURNDATA, // DST - addr, // SRC - size, sys_return_finish, kexit_info // count, retdest, ... - ) - %build_address_no_offset - // stack: DST, SRC, size, sys_return_finish, kexit_info - %jump(memcpy_bytes) - -sys_return_finish: - // stack: kexit_info - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) - -global sys_selfdestruct: - %check_static - // stack: kexit_info, recipient - SWAP1 %u256_to_addr - %address DUP1 %balance - - // Insert recipient into the accessed addresses list. - // stack: balance, address, recipient, kexit_info - DUP3 %insert_accessed_addresses - - // Set the parent context's return data size to 0. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - - // Compute gas. - // stack: cold_access, balance, address, recipient, kexit_info - %mul_const(@GAS_COLDACCOUNTACCESS) - DUP2 - // stack: balance, gas_coldaccess, balance, address, recipient, kexit_info - ISZERO %not_bit - // stack: balance!=0, gas_coldaccess, balance, address, recipient, kexit_info - DUP5 %is_dead MUL %mul_const(@GAS_NEWACCOUNT) - // stack: gas_newaccount, gas_coldaccess, balance, address, recipient, kexit_info - ADD %add_const(@GAS_SELFDESTRUCT) - %stack (gas, balance, address, recipient, kexit_info) -> (gas, kexit_info, balance, address, recipient) - %charge_gas - %stack (kexit_info, balance, address, recipient) -> (balance, address, recipient, kexit_info) - - // Insert address into the selfdestruct set. - // stack: balance, address, recipient, kexit_info - DUP2 %insert_selfdestruct_list - - // Set the balance of the address to 0. - // stack: balance, address, recipient, kexit_info - PUSH 0 - // stack: 0, balance, address, recipient, kexit_info - DUP3 %mpt_read_state_trie - // stack: account_ptr, 0, balance, address, recipient, kexit_info - %add_const(1) - // stack: balance_ptr, 0, balance, address, recipient, kexit_info - %mstore_trie_data - - %stack (balance, address, recipient, kexit_info) -> - (address, recipient, address, recipient, balance, kexit_info) - - // If the recipient is the same as the address, then we're done. - // Otherwise, send the balance to the recipient. - // stack: address, recipient, address, recipient, balance, kexit_info - EQ %jumpi(sys_selfdestruct_journal_add) - %stack (address, recipient, balance, kexit_info) -> (recipient, balance, address, recipient, balance, kexit_info) - %add_eth - -sys_selfdestruct_journal_add: - // stack: address, recipient, balance, kexit_info - %journal_add_account_destroyed - - // stack: kexit_info - %leftover_gas - // stack: leftover_gas - PUSH 1 // success - %jump(terminate_common) - -global sys_revert: - // stack: kexit_info, offset, size - %stack (kexit_info, offset, size) -> (offset, size, kexit_info, offset, size) - %add_or_fault - // stack: offset+size, kexit_info, offset, size - DUP4 ISZERO %jumpi(revert_zero_size) - // stack: offset+size, kexit_info, offset, size - DUP1 %ensure_reasonable_offset - %update_mem_bytes - %jump(revert_after_gas) -revert_zero_size: - POP -revert_after_gas: - // Load the parent's context. - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - - // Store the return data size in the parent context's metadata. - %stack (parent_ctx, kexit_info, offset, size) -> - (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) - ADD // addr (CTX offsets are already scaled by their segment) - SWAP1 - // stack: size, addr, offset, size, parent_ctx, kexit_info - MSTORE_GENERAL - // stack: offset, size, parent_ctx, kexit_info - - // Store the return data in the parent context's returndata segment. - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - - %stack (addr, size, parent_ctx, kexit_info) -> - ( - parent_ctx, @SEGMENT_RETURNDATA, // DST - addr, // SRC - size, sys_revert_finish, kexit_info // count, retdest, ... - ) - %build_address_no_offset - // stack: DST, SRC, size, sys_revert_finish, kexit_info - %jump(memcpy_bytes) - -sys_revert_finish: - %leftover_gas - // stack: leftover_gas - %revert_checkpoint - PUSH 0 // success - %jump(terminate_common) - -// The execution is in an exceptional halting state if -// - there is insufficient gas -// - the instruction is invalid -// - there are insufficient stack items -// - a JUMP/JUMPI destination is invalid -// - the new stack size would be larger than 1024, or -// - state modification is attempted during a static call -global fault_exception: - // stack: (empty) - %revert_checkpoint - PUSH 0 // leftover_gas - // Set the parent context's return data size to 0. - %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) - PUSH 0 // success - %jump(terminate_common) - -global terminate_common: - // stack: success, leftover_gas - // TODO: Panic if we exceeded our gas limit? - - // We want to move the success flag from our (child) context's stack to the - // parent context's stack. We will write it to memory, specifically - // SEGMENT_KERNEL_GENERAL[0], then load it after the context switch. - PUSH 0 - // stack: 0, success, leftover_gas - %mstore_kernel_general - // stack: leftover_gas - - // Similarly, we write leftover_gas to SEGMENT_KERNEL_GENERAL[1] so that - // we can later read it after switching to the parent context. - PUSH 1 - // stack: 1, leftover_gas - %mstore_kernel_general - // stack: (empty) - - // Similarly, we write the parent PC to SEGMENT_KERNEL_GENERAL[2] so that - // we can later read it after switching to the parent context. - PUSH 2 - PUSH @SEGMENT_KERNEL_GENERAL - %build_kernel_address - %mload_context_metadata(@CTX_METADATA_PARENT_PC) - MSTORE_GENERAL - // stack: (empty) - - // Go back to the parent context. - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - SET_CONTEXT - %decrement_call_depth - // stack: (empty) - - // Load the fields that we stored in SEGMENT_KERNEL_GENERAL. - PUSH 1 %mload_kernel_general // leftover_gas - PUSH 0 %mload_kernel_general // success - PUSH 2 %mload_kernel_general // parent_pc - - // stack: parent_pc, success, leftover_gas - JUMP diff --git a/evm/src/cpu/kernel/asm/core/touched_addresses.asm b/evm/src/cpu/kernel/asm/core/touched_addresses.asm deleted file mode 100644 index d9c70f47ac..0000000000 --- a/evm/src/cpu/kernel/asm/core/touched_addresses.asm +++ /dev/null @@ -1,112 +0,0 @@ -%macro insert_touched_addresses - %stack (addr) -> (addr, %%after) - %jump(insert_touched_addresses) -%%after: - // stack: (empty) -%endmacro - -%macro insert_touched_addresses_no_return - %insert_touched_addresses - POP -%endmacro - -/// Inserts the address into the list if it is not already present. -global insert_touched_addresses: - // stack: addr, retdest - %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) - // stack: len, addr, retdest - PUSH @SEGMENT_TOUCHED_ADDRESSES ADD - PUSH @SEGMENT_TOUCHED_ADDRESSES -insert_touched_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES - %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(insert_address) - // stack: i, len, addr, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, addr, retdest - DUP4 - // stack: addr, loaded_addr, i, len, addr, retdest - EQ %jumpi(insert_touched_addresses_found) - // stack: i, len, addr, retdest - %increment - %jump(insert_touched_addresses_loop) - -insert_address: - %stack (i, len, addr, retdest) -> (i, addr, len, @SEGMENT_TOUCHED_ADDRESSES, retdest) - DUP2 %journal_add_account_touched // Add a journal entry for the touched account. - %swap_mstore // Store new address at the end of the array. - // stack: len, segment, retdest - SUB // unscale - %increment - %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Store new length. - JUMP - -insert_touched_addresses_found: - %stack (i, len, addr, retdest) -> (retdest) - JUMP - -/// Remove the address from the list. -/// Panics if the address is not in the list. -/// TODO: Unused? -global remove_touched_addresses: - // stack: addr, retdest - %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) - // stack: len, addr, retdest - PUSH @SEGMENT_TOUCHED_ADDRESSES ADD - PUSH @SEGMENT_TOUCHED_ADDRESSES -remove_touched_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES - %stack (i, len, addr, retdest) -> (i, len, i, len, addr, retdest) - EQ %jumpi(panic) - // stack: i, len, addr, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, addr, retdest - DUP4 - // stack: addr, loaded_addr, i, len, addr, retdest - EQ %jumpi(remove_touched_addresses_found) - // stack: i, len, addr, retdest - %increment - %jump(remove_touched_addresses_loop) -remove_touched_addresses_found: - %stack (i, len, addr, retdest) -> (len, 1, i, retdest) - SUB - PUSH @SEGMENT_TOUCHED_ADDRESSES DUP2 - SUB // unscale - %mstore_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) // Decrement the list length. - // stack: len-1, i, retdest - MLOAD_GENERAL // Load the last address in the list. - // stack: last_addr, i, retdest - MSTORE_GENERAL // Store the last address at the position of the removed address. - JUMP - - -global delete_all_touched_addresses: - // stack: retdest - %mload_global_metadata(@GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN) - // stack: len, retdest - PUSH @SEGMENT_TOUCHED_ADDRESSES ADD - PUSH @SEGMENT_TOUCHED_ADDRESSES -delete_all_touched_addresses_loop: - // `i` and `len` are both scaled by SEGMENT_TOUCHED_ADDRESSES - // stack: i, len, retdest - DUP2 DUP2 EQ %jumpi(delete_all_touched_addresses_done) - // stack: i, len, retdest - DUP1 MLOAD_GENERAL - // stack: loaded_addr, i, len, retdest - DUP1 %is_empty %jumpi(bingo) - // stack: loaded_addr, i, len, retdest - POP %increment %jump(delete_all_touched_addresses_loop) -bingo: - // stack: loaded_addr, i, len, retdest - %delete_account - %increment %jump(delete_all_touched_addresses_loop) -delete_all_touched_addresses_done: - // stack: i, len, retdest - %pop2 JUMP - -%macro delete_all_touched_addresses - %stack () -> (%%after) - %jump(delete_all_touched_addresses) -%%after: - // stack: (empty) -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm deleted file mode 100644 index 0517cf3a8f..0000000000 --- a/evm/src/cpu/kernel/asm/core/transfer.asm +++ /dev/null @@ -1,112 +0,0 @@ -// Transfers some ETH from one address to another. The amount is given in wei. -// Pre stack: from, to, amount, retdest -// Post stack: status (0 indicates success) -global transfer_eth: - // stack: from, to, amount, retdest - %stack (from, to, amount, retdest) - -> (from, amount, to, amount, retdest) - %deduct_eth - // stack: deduct_eth_status, to, amount, retdest - %jumpi(transfer_eth_failure) - // stack: to, amount, retdest - %add_eth - %stack (retdest) -> (retdest, 0) - JUMP -global transfer_eth_failure: - %stack (to, amount, retdest) -> (retdest, 1) - JUMP - -// Convenience macro to call transfer_eth and return where we left off. -%macro transfer_eth - %stack (from, to, amount) -> (from, to, amount, %%after) - %jump(transfer_eth) -%%after: -%endmacro - -// Returns 0 on success, or 1 if addr has insufficient balance. Panics if addr isn't found in the trie. -// Pre stack: addr, amount, retdest -// Post stack: status (0 indicates success) -global deduct_eth: - // stack: addr, amount, retdest - DUP1 %insert_touched_addresses - %mpt_read_state_trie - // stack: account_ptr, amount, retdest - DUP1 ISZERO %jumpi(deduct_eth_no_such_account) // If the account pointer is null, return 1. - %add_const(1) - // stack: balance_ptr, amount, retdest - DUP1 %mload_trie_data - // stack: balance, balance_ptr, amount, retdest - DUP1 DUP4 GT - // stack: amount > balance, balance, balance_ptr, amount, retdest - %jumpi(deduct_eth_insufficient_balance) - %stack (balance, balance_ptr, amount, retdest) -> (balance, amount, balance_ptr, retdest, 0) - SUB - SWAP1 - // stack: balance_ptr, balance - amount, retdest, 0 - %mstore_trie_data - // stack: retdest, 0 - JUMP -global deduct_eth_no_such_account: - %stack (account_ptr, amount, retdest) -> (retdest, 1) - JUMP -global deduct_eth_insufficient_balance: - %stack (balance, balance_ptr, amount, retdest) -> (retdest, 1) - JUMP - -// Convenience macro to call deduct_eth and return where we left off. -%macro deduct_eth - %stack (addr, amount) -> (addr, amount, %%after) - %jump(deduct_eth) -%%after: -%endmacro - -// Pre stack: addr, amount, redest -// Post stack: (empty) -global add_eth: - // stack: addr, amount, retdest - DUP1 %insert_touched_addresses - DUP1 %mpt_read_state_trie - // stack: account_ptr, addr, amount, retdest - DUP1 ISZERO %jumpi(add_eth_new_account) // If the account pointer is null, we need to create the account. - %add_const(1) - // stack: balance_ptr, addr, amount, retdest - DUP1 %mload_trie_data - // stack: balance, balance_ptr, addr, amount, retdest - %stack (balance, balance_ptr, addr, amount) -> (amount, balance, balance_ptr) - ADD - // stack: new_balance, balance_ptr, retdest - SWAP1 - // stack: balance_ptr, new_balance, retdest - %mstore_trie_data - // stack: retdest - JUMP -global add_eth_new_account: - // stack: null_account_ptr, addr, amount, retdest - POP - // stack: addr, amount, retdest - DUP2 ISZERO %jumpi(add_eth_new_account_zero) - DUP1 %journal_add_account_created - %get_trie_data_size // pointer to new account we're about to create - // stack: new_account_ptr, addr, amount, retdest - SWAP2 - // stack: amount, addr, new_account_ptr, retdest - PUSH 0 %append_to_trie_data // nonce - %append_to_trie_data // balance - // stack: addr, new_account_ptr, retdest - PUSH 0 %append_to_trie_data // storage root pointer - PUSH @EMPTY_STRING_HASH %append_to_trie_data // code hash - // stack: addr, new_account_ptr, retdest - %addr_to_state_key - // stack: key, new_account_ptr, retdest - %jump(mpt_insert_state_trie) - -add_eth_new_account_zero: - // stack: addr, amount, retdest - %pop2 JUMP - -// Convenience macro to call add_eth and return where we left off. -%macro add_eth - %stack (addr, amount) -> (addr, amount, %%after) - %jump(add_eth) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm deleted file mode 100644 index a77329bd8c..0000000000 --- a/evm/src/cpu/kernel/asm/core/util.asm +++ /dev/null @@ -1,88 +0,0 @@ -// Return the next context ID, and record the old context ID in the new one's -// @CTX_METADATA_PARENT_CONTEXT field. Does not actually enter the new context. -%macro create_context - // stack: (empty) - %next_context_id - %set_new_ctx_parent_ctx - // stack: new_ctx -%endmacro - -// Get and increment @GLOBAL_METADATA_LARGEST_CONTEXT to determine the next context ID. -%macro next_context_id - // stack: (empty) - %mload_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) - %add_const(0x10000000000000000) // scale each context by 2^64 - // stack: new_ctx - DUP1 - %mstore_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) - // stack: new_ctx -%endmacro - -// Returns whether the current transaction is a contract creation transaction. -%macro is_contract_creation - // stack: (empty) - %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) -%endmacro - -%macro is_precompile - // stack: addr - DUP1 %ge_const(@ECREC) SWAP1 %le_const(@BLAKE2_F) - // stack: addr>=1, addr<=9 - MUL // Cheaper than AND -%endmacro - -// Returns 1 if the account is non-existent, 0 otherwise. -%macro is_non_existent - // stack: addr - %mpt_read_state_trie ISZERO -%endmacro - -// Returns 1 if the account is empty, 0 otherwise. -%macro is_empty - // stack: addr - %mpt_read_state_trie - // stack: account_ptr - DUP1 ISZERO %jumpi(%%false) - // stack: account_ptr - DUP1 %mload_trie_data - // stack: nonce, account_ptr - ISZERO %not_bit %jumpi(%%false) - %increment DUP1 %mload_trie_data - // stack: balance, balance_ptr - ISZERO %not_bit %jumpi(%%false) - %add_const(2) %mload_trie_data - // stack: code_hash - PUSH @EMPTY_STRING_HASH - EQ - %jump(%%after) -%%false: - // stack: account_ptr - POP - PUSH 0 -%%after: -%endmacro - -// Returns 1 if the account is dead (i.e., empty or non-existent), 0 otherwise. -%macro is_dead - // stack: addr - DUP1 %is_non_existent - SWAP1 %is_empty - OR -%endmacro - -// Gets the size of the stack _before_ the macro is run -// WARNING: this macro is side-effecting. It writes the current stack length to offset -// `CTX_METADATA_STACK_SIZE`, segment `SEGMENT_CONTEXT_METADATA` in the current context. But I can't -// imagine it being an issue unless someone's doing something dumb. -%macro stack_length - // stack: (empty) - GET_CONTEXT - // stack: current_ctx - // It seems odd to switch to the context that we are already in. We do this because SET_CONTEXT - // saves the stack length of the context we are leaving in its metadata segment. - SET_CONTEXT - // stack: (empty) - // We can now read this stack length from memory. - %mload_context_metadata(@CTX_METADATA_STACK_SIZE) - // stack: stack_length -%endmacro diff --git a/evm/src/cpu/kernel/asm/core/withdrawals.asm b/evm/src/cpu/kernel/asm/core/withdrawals.asm deleted file mode 100644 index 3be05d880c..0000000000 --- a/evm/src/cpu/kernel/asm/core/withdrawals.asm +++ /dev/null @@ -1,25 +0,0 @@ -%macro withdrawals - // stack: (empty) - PUSH %%after - %jump(withdrawals) -%%after: - // stack: (empty) -%endmacro - -global withdrawals: - // stack: retdest - PROVER_INPUT(withdrawal) - // stack: address, retdest - PROVER_INPUT(withdrawal) - // stack: amount, address, retdest - DUP2 %eq_const(@U256_MAX) %jumpi(withdrawals_end) - SWAP1 - // stack: address, amount, retdest - %add_eth - // stack: retdest - %jump(withdrawals) - -withdrawals_end: - // stack: amount, address, retdest - %pop2 - JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bls381/util.asm b/evm/src/cpu/kernel/asm/curve/bls381/util.asm deleted file mode 100644 index 13943be7d9..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bls381/util.asm +++ /dev/null @@ -1,101 +0,0 @@ -%macro add_fp381 - // stack: x0, x1, y0, y1 - PROVER_INPUT(sf::bls381_base::add_hi) - // stack: z1, x0, x1, y0, y1 - SWAP4 - // stack: y1, x0, x1, y0, z1 - PROVER_INPUT(sf::bls381_base::add_lo) - // stack: z0, y1, x0, x1, y0, z1 - SWAP4 - // stack: y0, y1, x0, x1, z0, z1 - %pop4 - // stack: z0, z1 -%endmacro - -%macro sub_fp381 - // stack: x0, x1, y0, y1 - PROVER_INPUT(sf::bls381_base::sub_hi) - // stack: z1, x0, x1, y0, y1 - SWAP4 - // stack: y1, x0, x1, y0, z1 - PROVER_INPUT(sf::bls381_base::sub_lo) - // stack: z0, y1, x0, x1, y0, z1 - SWAP4 - // stack: y0, y1, x0, x1, z0, z1 - %pop4 - // stack: z0, z1 -%endmacro - -%macro mul_fp381 - // stack: x0, x1, y0, y1 - PROVER_INPUT(sf::bls381_base::mul_hi) - // stack: z1, x0, x1, y0, y1 - SWAP4 - // stack: y1, x0, x1, y0, z1 - PROVER_INPUT(sf::bls381_base::mul_lo) - // stack: z0, y1, x0, x1, y0, z1 - SWAP4 - // stack: y0, y1, x0, x1, z0, z1 - %pop4 - // stack: z0, z1 -%endmacro - -%macro add_fp381_2 - // stack: x_re, x_im, y_re, y_im - %stack (x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (y_im, x_im, y_re, x_re) - // stack: y_im, x_im, y_re, x_re - %add_fp381 - // stack: z_im, y_re, x_re - %stack (z_im: 2, y_re: 2, x_re: 2) -> (x_re, y_re, z_im) - // stack: x_re, y_re, z_im - %add_fp381 - // stack: z_re, z_im -%endmacro - -%macro sub_fp381_2 - // stack: x_re, x_im, y_re, y_im - %stack (x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (x_im, y_im, y_re, x_re) - // stack: x_im, y_im, y_re, x_re - %sub_fp381 - // stack: z_im, y_re, x_re - %stack (z_im: 2, y_re: 2, x_re: 2) -> (x_re, y_re, z_im) - // stack: x_re, y_re, z_im - %sub_fp381 - // stack: z_re, z_im -%endmacro - -// note that {x,y}_{re,im} all take up two stack terms -global mul_fp381_2: - // stack: x_re, x_im, y_re, y_im, jumpdest - DUP4 - DUP4 - // stack: x_im, x_re, x_im, y_re, y_im, jumpdest - DUP8 - DUP8 - // stack: y_re, x_im, x_re, x_im, y_re, y_im, jumpdest - DUP12 - DUP12 - // stack: y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest - DUP8 - DUP8 - // stack: x_re , y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest - %mul_fp381 - // stack: x_re * y_im, y_re, x_im, x_re, x_im, y_re, y_im, jumpdest - %stack (v: 2, y_re: 2, x_im: 2) -> (x_im, y_re, v) - // stack: x_im , y_re, x_re*y_im, x_re, x_im, y_re, y_im, jumpdest - %mul_fp381 - // stack: x_im * y_re, x_re*y_im, x_re, x_im, y_re, y_im, jumpdest - %add_fp381 - // stack: z_im, x_re, x_im, y_re, y_im, jumpdest - %stack (z_im: 2, x_re: 2, x_im: 2, y_re: 2, y_im: 2) -> (x_im, y_im, y_re, x_re, z_im) - // stack: x_im , y_im, y_re, x_re, z_im, jumpdest - %mul_fp381 - // stack: x_im * y_im, y_re, x_re, z_im, jumpdest - %stack (v: 2, y_re: 2, x_re: 2) -> (x_re, y_re, v) - // stack: x_re , y_re, x_im*y_im, z_im, jumpdest - %mul_fp381 - // stack: x_re * y_re, x_im*y_im, z_im, jumpdest - %sub_fp381 - // stack: z_re, z_im, jumpdest - %stack (z_re: 2, z_im: 2, jumpdest) -> (jumpdest, z_re, z_im) - JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/constants.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/constants.asm deleted file mode 100644 index 20882c0530..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/constants.asm +++ /dev/null @@ -1,88 +0,0 @@ -/// miller_data is defined by -/// (1) taking the binary expansion of N254, the order of the elliptic curve group -/// (2) popping the first and last elements, then appending a 0: -/// exp = bin(N254)[1:-1] + [0] -/// (3) counting the lengths of runs of 1s then 0s in exp, e.g. -/// if exp = 1100010011110, then EXP = [(2,3), (1,2), (4,1)] -/// (4) byte encoding each pair (n,m) as follows: -/// miller_data = [(0x20)n + m for (n,m) in EXP] - -global miller_data: - BYTES 0xdc, 0x22, 0x42, 0x21 - BYTES 0xa1, 0xa4, 0x24, 0x21 - BYTES 0x23, 0x22, 0x64, 0x21 - BYTES 0x62, 0x41, 0x82, 0x24 - BYTES 0x22, 0x24, 0xa1, 0x42 - BYTES 0x25, 0x21, 0x22, 0x61 - BYTES 0x21, 0x44, 0x21, 0x21 - BYTES 0x46, 0x26, 0x41, 0x41 - BYTES 0x41, 0x21, 0x23, 0x25 - BYTES 0x21, 0x64, 0x41, 0x22 - BYTES 0x21, 0x27, 0x41, 0x43 - BYTES 0x22, 0x64, 0x21, 0x62 - BYTES 0x62, 0x22, 0x23, 0x42 - BYTES 0x25 - - -/// final_exp first computes y^a4, y^a2, y^a0 -/// representing a4, a2, a0 in *little endian* binary, define -/// EXPS4 = [(a4[i], a2[i], a0[i]) for i in 0..len(a4)] -/// EXPS2 = [ (a2[i], a0[i]) for i in len(a4)..len(a2)] -/// EXPS0 = [ a0[i] for i in len(a2)..len(a0)] -/// power_data_n is simply a reverse-order byte encoding of EXPSn -/// where (i,j,k) is sent to (100)i + (10)j + k - -global power_data_4: - BYTES 111, 010, 011, 111 - BYTES 110, 101, 001, 100 - BYTES 001, 100, 110, 110 - BYTES 110, 011, 011, 101 - BYTES 011, 101, 101, 111 - BYTES 000, 011, 011, 001 - BYTES 011, 001, 101, 100 - BYTES 100, 000, 010, 100 - BYTES 110, 010, 110, 100 - BYTES 110, 101, 101, 001 - BYTES 001, 110, 110, 110 - BYTES 010, 110, 101, 001 - BYTES 010, 010, 110, 110 - BYTES 110, 010, 101, 110 - BYTES 101, 010, 101, 001 - BYTES 000, 111, 111, 110 - -global power_data_2: - BYTES 11, 01, 11, 10 - BYTES 11, 10, 01, 10 - BYTES 00, 01, 10, 11 - BYTES 01, 11, 10, 01 - BYTES 00, 00, 00, 01 - BYTES 10, 01, 01, 10 - BYTES 00, 01, 11, 00 - BYTES 01, 00, 10, 11 - BYTES 11, 00, 11, 10 - BYTES 11, 00, 11, 01 - BYTES 11, 11, 11, 01 - BYTES 01, 00, 00, 11 - BYTES 00, 11, 11, 01 - BYTES 01, 10, 11, 10 - BYTES 11, 10, 10, 00 - BYTES 11, 10 - -global power_data_0: - BYTES 0, 1, 1, 0 - BYTES 0, 1, 1, 1 - BYTES 1, 0, 0, 0 - BYTES 1, 0, 0, 1 - BYTES 1, 0, 1, 0 - BYTES 1, 1, 1, 1 - BYTES 0, 0, 1, 1 - BYTES 1, 0, 1, 0 - BYTES 1, 0, 0, 0 - BYTES 0, 0, 1, 1 - BYTES 0, 1, 0, 1 - BYTES 0, 0, 1, 0 - BYTES 0, 0, 1, 0 - BYTES 1, 1, 1, 0 - BYTES 1, 0, 1, 1 - BYTES 0, 0, 1, 0 - BYTES 0 diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm deleted file mode 100644 index a43c4047d3..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm +++ /dev/null @@ -1,268 +0,0 @@ -// BN254 elliptic curve addition. -// Uses the standard affine addition formula. -global bn_add: - // stack: x0, y0, x1, y1, retdest - // Check if points are valid BN254 points. - DUP2 - // stack: y0, x0, y0, x1, y1, retdest - DUP2 - // stack: x0, y0, x0, y0, x1, y1, retdest - %bn_check - // stack: isValid(x0, y0), x0, y0, x1, y1, retdest - DUP5 - // stack: x1, isValid(x0, y0), x0, y0, x1, y1, retdest - DUP5 - // stack: x1, y1, isValid(x0, y0), x0, y0, x1, y1, retdest - %bn_check - // stack: isValid(x1, y1), isValid(x0, y0), x0, y0, x1, y1, retdest - AND - // stack: isValid(x1, y1) & isValid(x0, y0), x0, y0, x1, y1, retdest - %jumpi(bn_add_valid_points) - // stack: x0, y0, x1, y1, retdest - - // Otherwise return - %pop4 - // stack: retdest - %bn_invalid_input - -// BN254 elliptic curve addition. -// Assumption: (x0,y0) and (x1,y1) are valid points. -global bn_add_valid_points: - // stack: x0, y0, x1, y1, retdest - - // Check if the first point is the identity. - DUP2 - // stack: y0, x0, y0, x1, y1, retdest - DUP2 - // stack: x0, y0, x0, y0, x1, y1, retdest - %ec_isidentity - // stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest - %jumpi(bn_add_fst_zero) - // stack: x0, y0, x1, y1, retdest - - // Check if the second point is the identity. - DUP4 - // stack: y1, x0, y0, x1, y1, retdest - DUP4 - // stack: x1, y1, x0, y0, x1, y1, retdest - %ec_isidentity - // stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest - %jumpi(bn_add_snd_zero) - // stack: x0, y0, x1, y1, retdest - - // Check if both points have the same x-coordinate. - DUP3 - // stack: x1, x0, y0, x1, y1, retdest - DUP2 - // stack: x0, x1, x0, y0, x1, y1, retdest - EQ - // stack: x0 == x1, x0, y0, x1, y1, retdest - %jumpi(bn_add_equal_first_coord) - // stack: x0, y0, x1, y1, retdest - - // Otherwise, we can use the standard formula. - // Compute lambda = (y0 - y1)/(x0 - x1) - DUP4 - // stack: y1, x0, y0, x1, y1, retdest - DUP3 - // stack: y0, y1, x0, y0, x1, y1, retdest - SUBFP254 - // stack: y0 - y1, x0, y0, x1, y1, retdest - DUP4 - // stack: x1, y0 - y1, x0, y0, x1, y1, retdest - DUP3 - // stack: x0, x1, y0 - y1, x0, y0, x1, y1, retdest - SUBFP254 - // stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest - %divr_fp254 - // stack: lambda, x0, y0, x1, y1, retdest - %jump(bn_add_valid_points_with_lambda) - -// BN254 elliptic curve addition. -// Assumption: (x0,y0) == (0,0) -bn_add_fst_zero: - // stack: x0, y0, x1, y1, retdest - // Just return (x1,y1) - %stack (x0, y0, x1, y1, retdest) -> (retdest, x1, y1) - JUMP - -// BN254 elliptic curve addition. -// Assumption: (x1,y1) == (0,0) -bn_add_snd_zero: - // stack: x0, y0, x1, y1, retdest - - // Just return (x0,y0) - %stack (x0, y0, x1, y1, retdest) -> (retdest, x0, y0) - JUMP - -// BN254 elliptic curve addition. -// Assumption: lambda = (y0 - y1)/(x0 - x1) -bn_add_valid_points_with_lambda: - // stack: lambda, x0, y0, x1, y1, retdest - - // Compute x2 = lambda^2 - x1 - x0 - DUP2 - // stack: x0, lambda, x0, y0, x1, y1, retdest - DUP5 - // stack: x1, x0, lambda, x0, y0, x1, y1, retdest - DUP3 - // stack: lambda, x1, x0, lambda, x0, y0, x1, y1, retdest - DUP1 - // stack: lambda, lambda, x1, x0, lambda, x0, y0, x1, y1, retdest - MULFP254 - // stack: lambda^2, x1, x0, lambda, x0, y0, x1, y1, retdest - SUBFP254 - // stack: lambda^2 - x1, x0, lambda, x0, y0, x1, y1, retdest - SUBFP254 - // stack: x2, lambda, x0, y0, x1, y1, retdest - - // Compute y2 = lambda*(x1 - x2) - y1 - DUP1 - // stack: x2, x2, lambda, x0, y0, x1, y1, retdest - DUP6 - // stack: x1, x2, x2, lambda, x0, y0, x1, y1, retdest - SUBFP254 - // stack: x1 - x2, x2, lambda, x0, y0, x1, y1, retdest - DUP3 - // stack: lambda, x1 - x2, x2, lambda, x0, y0, x1, y1, retdest - MULFP254 - // stack: lambda * (x1 - x2), x2, lambda, x0, y0, x1, y1, retdest - DUP7 - // stack: y1, lambda * (x1 - x2), x2, lambda, x0, y0, x1, y1, retdest - SWAP1 - // stack: lambda * (x1 - x2), y1, x2, lambda, x0, y0, x1, y1, retdest - SUBFP254 - // stack: y2, x2, lambda, x0, y0, x1, y1, retdest - - // Return x2,y2 - %stack (y2, x2, lambda, x0, y0, x1, y1, retdest) -> (retdest, x2, y2) - JUMP - -// BN254 elliptic curve addition. -// Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1 -bn_add_equal_first_coord: - // stack: x0, y0, x1, y1, retdest with x0 == x1 - - // Check if the points are equal - DUP2 - // stack: y0, x0, y0, x1, y1, retdest - DUP5 - // stack: y1, y0, x0, y0, x1, y1, retdest - EQ - // stack: y1 == y0, x0, y0, x1, y1, retdest - %jumpi(bn_add_equal_points) - // stack: x0, y0, x1, y1, retdest - - // Otherwise, one is the negation of the other so we can return (0,0). - %pop4 - // stack: retdest - PUSH 0 - // stack: 0, retdest - PUSH 0 - // stack: 0, 0, retdest - SWAP2 - // stack: retdest, 0, 0 - JUMP - - -// BN254 elliptic curve addition. -// Assumption: x0 == x1 and y0 == y1 -// Standard doubling formula. -bn_add_equal_points: - // stack: x0, y0, x1, y1, retdest - - // Compute lambda = 3/2 * x0^2 / y0 - DUP1 - // stack: x0, x0, y0, x1, y1, retdest - DUP1 - // stack: x0, x0, x0, y0, x1, y1, retdest - MULFP254 - // stack: x0^2, x0, y0, x1, y1, retdest with - PUSH 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea5 // 3/2 in the base field - // stack: 3/2, x0^2, x0, y0, x1, y1, retdest - MULFP254 - // stack: 3/2 * x0^2, x0, y0, x1, y1, retdest - DUP3 - // stack: y0, 3/2 * x0^2, x0, y0, x1, y1, retdest - %divr_fp254 - // stack: lambda, x0, y0, x1, y1, retdest - %jump(bn_add_valid_points_with_lambda) - -// BN254 elliptic curve doubling. -// Assumption: (x0,y0) is a valid point. -// Standard doubling formula. -global bn_double: - // stack: x, y, retdest - DUP2 DUP2 %ec_isidentity - // stack: (x,y)==(0,0), x, y, retdest - %jumpi(ec_double_retself) - DUP2 DUP2 - // stack: x, y, x, y, retdest - %jump(bn_add_equal_points) - -// Check if (x,y) is a valid curve point. -// Returns (range & curve) || ident -// where -// range = (x < N) & (y < N) -// curve = y^2 == (x^3 + 3) -// ident = (x,y) == (0,0) -%macro bn_check - // stack: x, y - DUP1 - // stack: x, x, y - PUSH @BN_BASE - // stack: N , x, x, y - DUP1 - // stack: N, N , x, x, y - DUP5 - // stack: y , N, N , x, x, y - LT - // stack: y < N, N , x, x, y - SWAP2 - // stack: x , N, y < N, x, y - LT - // stack: x < N, y < N, x, y - AND - // stack: range, x, y - SWAP2 - // stack: y, x, range - DUP2 - // stack: x , y, x, range - DUP1 - DUP1 - MULFP254 - MULFP254 - // stack: x^3, y, x, range - PUSH 3 - ADDFP254 - // stack: 3 + x^3, y, x, range - DUP2 - // stack: y , 3 + x^3, y, x, range - DUP1 - MULFP254 - // stack: y^2, 3 + x^3, y, x, range - EQ - // stack: curve, y, x, range - SWAP2 - // stack: x, y, curve, range - %ec_isidentity - // stack: ident , curve, range - SWAP2 - // stack: range , curve, ident - AND - // stack: range & curve, ident - OR - // stack: is_valid -%endmacro - -// Return (u256::MAX, u256::MAX) which is used to indicate the input was invalid. -%macro bn_invalid_input - // stack: retdest - PUSH @U256_MAX - // stack: u256::MAX, retdest - DUP1 - // stack: u256::MAX, u256::MAX, retdest - SWAP2 - // stack: retdest, u256::MAX, u256::MAX - JUMP -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm deleted file mode 100644 index 93864c5519..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_mul.asm +++ /dev/null @@ -1,41 +0,0 @@ -// BN254 elliptic curve scalar multiplication. -// Uses GLV, wNAF with w=5, and a MSM algorithm. -global bn_mul: - // stack: x, y, s, retdest - DUP2 - // stack: y, x, y, s, retdest - DUP2 - // stack: x, y, x, y, s, retdest - %ec_isidentity - // stack: (x,y)==(0,0), x, y, s, retdest - %jumpi(ret_zero_ec_mul) - // stack: x, y, s, retdest - DUP2 - // stack: y, x, y, s, retdest - DUP2 - // stack: x, y, x, y, s, retdest - %bn_check - // stack: isValid(x, y), x, y, s, retdest - %jumpi(bn_mul_valid_point) - // stack: x, y, s, retdest - %pop3 - %bn_invalid_input - -bn_mul_valid_point: - %stack (x, y, s, retdest) -> (s, bn_mul_after_glv, x, y, bn_msm, bn_mul_end, retdest) - %jump(bn_glv_decompose) -bn_mul_after_glv: - // stack: bneg, a, b, x, y, bn_msm, bn_mul_end, retdest - // Store bneg at this (otherwise unused) location. Will be used later in the MSM. - %mstore_current(@SEGMENT_BN_TABLE_Q, @BN_BNEG_LOC) - // stack: a, b, x, y, bn_msm, bn_mul_end, retdest - PUSH bn_mul_after_a SWAP1 PUSH @SEGMENT_BN_WNAF_A PUSH @BN_SCALAR %jump(wnaf) -bn_mul_after_a: - // stack: b, x, y, bn_msm, bn_mul_end, retdest - PUSH bn_mul_after_b SWAP1 PUSH @SEGMENT_BN_WNAF_B PUSH @BN_SCALAR %jump(wnaf) -bn_mul_after_b: - // stack: x, y, bn_msm, bn_mul_end, retdest - %jump(bn_precompute_table) -bn_mul_end: - %stack (Ax, Ay, retdest) -> (retdest, Ax, Ay) - JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm deleted file mode 100644 index 035cb43830..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/final_exponent.asm +++ /dev/null @@ -1,326 +0,0 @@ -/// To make the Tate pairing an invariant, the final step is to exponentiate by -/// (p^12 - 1)/N = (p^6 - 1) * (p^2 + 1) * (p^4 - p^2 + 1)/N -/// and thus we can exponentiate by each factor sequentially. -/// -/// def bn254_final_exponent(y: Fp12): -/// y = first_exp(y) -/// y = second_exp(y) -/// return final_exp(y) - -global bn254_final_exponent: - -/// first, exponentiate by (p^6 - 1) via -/// def first_exp(y): -/// return y.frob(6) / y - // stack: k, inp, out, retdest {out: y} - %stack (k, inp, out) -> (out, 0, first_exp, out) - // stack: out, 0, first_exp, out, retdest {out: y} - %jump(inv_fp254_12) -first_exp: - // stack: out, retdest {out: y , 0: y^-1} - %frob_fp254_12_6 - // stack: out, retdest {out: y_6, 0: y^-1} - %stack (out) -> (out, 0, out, second_exp, out) - // stack: out, 0, out, second_exp, out, retdest {out: y_6, 0: y^-1} - %jump(mul_fp254_12) - -/// second, exponentiate by (p^2 + 1) via -/// def second_exp(y): -/// return y.frob(2) * y -second_exp: - // stack: out, retdest {out: y} - %stack (out) -> (out, 0, out, out, final_exp, out) - // stack: out, 0, out, out, final_exp, out, retdest {out: y} - %frob_fp254_12_2_ - // stack: 0, out, out, final_exp, out, retdest {out: y, 0: y_2} - %jump(mul_fp254_12) - -/// Finally, we must exponentiate by (p^4 - p^2 + 1)/N -/// To do so efficiently, we can express this power as -/// (p^4 - p^2 + 1)/N = p^3 + (a2)p^2 - (a1)p - a0 -/// and simultaneously compute y^a4, y^a2, y^a0 where -/// a1 = a4 + 2a2 - a0 -/// We first initialize these powers as 1 and then use -/// binary algorithms for exponentiation. -/// -/// def final_exp(y): -/// y4, y2, y0 = 1, 1, 1 -/// power_loop_4() -/// power_loop_2() -/// power_loop_0() -/// custom_powers() -/// final_power() - -final_exp: - // stack: val, retdest - %stack (val) -> (val, 0, val) - // stack: val, 0, val, retdest - %move_fp254_12 - // dest addr returned by %move_fp254_12 is already scaled - // stack: addr, val, retdest {0: sqr} - - // Write 1s at offset 12, 24 and 36 - PUSH 12 - ADD - DUP1 %add_const(12) - DUP1 %add_const(12) - // stack: addr_1, addr_2, addr_3 - %rep 3 - PUSH 1 MSTORE_GENERAL - %endrep - - // stack: val, retdest {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (64, 62, 65, 0) - // stack: 64, 62, 65, 0, val, retdest {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(power_loop_4) - -/// After computing the powers -/// y^a4, y^a2, y^a0 -/// we would like to transform them to -/// y^a2, y^-a1, y^-a0 -/// -/// def custom_powers() -/// y0 = y0^{-1} -/// y1 = y4 * y2^2 * y0 -/// return y2, y1, y0 -/// -/// And finally, upon doing so, compute the final power -/// y^(p^3) * (y^a2)^(p^2) * (y^-a1)^p * (y^-a0) -/// -/// def final_power() -/// y = y.frob(3) -/// y2 = y2.frob(2) -/// y1 = y1.frob(1) -/// return y * y2 * y1 * y0 - -custom_powers: - // stack: val, retdest {12: y0, 24: y2, 36: y4} - %stack () -> (12, 48, make_term_1) - // stack: 12, 48, make_term_1, val, retdest {12: y0, 24: y2, 36: y4} - %jump(inv_fp254_12) -make_term_1: - // stack: val, retdest {24: y2, 36: y4, 48: y0^-1} - %stack () -> (24, 36, 36, make_term_2) - // stack: 24, 36, 36, make_term_2, val, retdest {24: y2, 36: y4, 48: y0^-1} - %jump(mul_fp254_12) -make_term_2: - // stack: val, retdest {24: y2, 36: y4 * y2, 48: y0^-1} - %stack () -> (24, 36, 36, make_term_3) - // stack: 24, 36, 36, make_term_3, val, retdest {24: y2, 36: y4 * y2, 48: y0^-1} - %jump(mul_fp254_12) -make_term_3: - // stack: val, retdest {24: y2, 36: y4 * y2^2, 48: y0^-1} - %stack () -> (48, 36, 36, final_power) - // stack: 48, 36, 36, final_power, val, retdest {24: y2, 36: y4 * y2^2, 48: y0^-1} - %jump(mul_fp254_12) -final_power: - // stack: val, retdest {val: y , 24: y^a2 , 36: y^a1 , 48: y^a0} - %frob_fp254_12_3 - // stack: val, retdest {val: y_3, 24: y^a2 , 36: y^a1 , 48: y^a0} - %stack () -> (24, 24) - %frob_fp254_12_2_ - POP - // stack: val, retdest {val: y_3, 24: (y^a2)_2, 36: y^a1 , 48: y^a0} - PUSH 36 - %frob_fp254_12_1 - POP - // stack: val, retdest {val: y_3, 24: (y^a2)_2, 36: (y^a1)_1, 48: y^a0} - %stack (val) -> (24, val, val, penult_mul, val) - // stack: 24, val, val, penult_mul, val, retdest {val: y_3, 24: (y^a2)_2, 36: (y^a1)_1, 48: y^a0} - %jump(mul_fp254_12) -penult_mul: - // stack: val, retdest {val: y_3 * (y^a2)_2, 36: (y^a1)_1, 48: y^a0} - %stack (val) -> (36, val, val, final_mul, val) - // stack: 36, val, val, final_mul, val, retdest {val: y_3 * (y^a2)_2, 36: (y^a1)_1, 48: y^a0} - %jump(mul_fp254_12) -final_mul: - // stack: val, retdest {val: y_3 * (y^a2)_2 * (y^a1)_1, 48: y^a0} - %stack (val) -> (48, val, val) - // stack: 48, val, val, retdest {val: y_3 * (y^a2)_2 * (y^a1)_1, 48: y^a0} - %jump(mul_fp254_12) - - -/// def power_loop_4(): -/// for i in range(64): -/// abc = load(i, power_data_4) -/// if a: -/// y4 *= acc -/// if b: -/// y2 *= acc -/// if c: -/// y0 *= acc -/// acc = square_fp254_12(acc) -/// y4 *= acc -/// -/// def power_loop_2(): -/// for i in range(62): -/// ab = load(i, power_data_2) -/// if a: -/// y2 *= acc -/// if b: -/// y0 *= acc -/// acc = square_fp254_12(acc) -/// y2 *= acc -/// -/// def power_loop_0(): -/// for i in range(65): -/// a = load(i, power_data_0) -/// if a: -/// y0 *= acc -/// acc = square_fp254_12(acc) -/// y0 *= acc - -power_loop_4: - // stack: i , j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - ISZERO - // stack: break?, i , j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_4_end) - // stack: i , j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(1) - // stack: i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %mload_kernel_code(power_data_4) - // stack: abc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %lt_const(100) - // stack: skip?, abc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_4_b) - // stack: abc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(100) - // stack: bc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (36, 36, power_loop_4_b) - // stack: 36, 36, power_loop_4_b, bc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP8 - // stack: sqr, 36, 36, power_loop_4_b, bc, i-1, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_4_b: - // stack: bc, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %lt_const(10) - // stack: skip?, bc, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_4_c) - // stack: bc, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(10) - // stack: c, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (24, 24, power_loop_4_c) - // stack: 24, 24, power_loop_4_c, c, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP8 - // stack: sqr, 24, 24, power_loop_4_c, c, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_4_c: - // stack: c, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - ISZERO - // stack: skip?, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_4_sq) - // stack: i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (12, 12, power_loop_4_sq) - // stack: 12, 12, power_loop_4_sq, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP7 - // stack: sqr, 12, 12, power_loop_4_sq, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_4_sq: - // stack: i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - PUSH power_loop_4 - // stack: power_loop_4, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP5 - DUP1 - // stack: sqr, sqr, power_loop_4, i, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(square_fp254_12) -power_loop_4_end: - // stack: 0, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - POP - // stack: j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (36, 36, power_loop_2) - // stack: 36, 36, power_loop_2, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP6 - // stack: sqr, 36, 36, power_loop_2, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) - -power_loop_2: - // stack: j , k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - ISZERO - // stack: break?, j , k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_2_end) - // stack: j , k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(1) - // stack: j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %mload_kernel_code(power_data_2) - // stack: ab, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %lt_const(10) - // stack: skip?, ab, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_2_b) - // stack: ab, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(10) - // stack: b, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (24, 24, power_loop_2_b) - // stack: 24, 24, power_loop_2_b, b, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP7 - // stack: sqr, 24, 24, power_loop_2_b, b, j-1, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_2_b: - // stack: b, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - ISZERO - // stack: skip?, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_2_sq) - // stack: j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (12, 12, power_loop_2_sq) - // stack: 12, 12, power_loop_2_sq, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP6 - // stack: sqr, 12, 12, power_loop_2_sq, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_2_sq: - // stack: j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - PUSH power_loop_2 - // stack: power_loop_2, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP4 - DUP1 - // stack: sqr, sqr, power_loop_2, j, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(square_fp254_12) -power_loop_2_end: - // stack: 0, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - POP - // stack: k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (24, 24, power_loop_0) - // stack: 24, 24, power_loop_0, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP5 - // stack: sqr, 24, 24, power_loop_0, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) - -power_loop_0: - // stack: k , sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - ISZERO - // stack: break?, k , sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_0_end) - // stack: k , sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %sub_const(1) - // stack: k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP1 - %mload_kernel_code(power_data_0) - // stack: a, k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - ISZERO - // stack: skip?, k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jumpi(power_loop_0_sq) - // stack: k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack () -> (12, 12, power_loop_0_sq) - // stack: 12, 12, power_loop_0_sq, k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP5 - // stack: sqr, 12, 12, power_loop_0_sq, k-1, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) -power_loop_0_sq: - // stack: k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - PUSH power_loop_0 - // stack: power_loop_0, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - DUP3 - DUP1 - // stack: sqr, sqr, power_loop_0, k, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(square_fp254_12) -power_loop_0_end: - // stack: 0, sqr {0: sqr, 12: y0, 24: y2, 36: y4} - %stack (i, sqr) -> (12, sqr, 12, custom_powers) - // stack: 12, sqr, 12, custom_powers {0: sqr, 12: y0, 24: y2, 36: y4} - %jump(mul_fp254_12) diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/glv.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/glv.asm deleted file mode 100644 index 32eb5b6c13..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/glv.asm +++ /dev/null @@ -1,116 +0,0 @@ -// Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff -// See also Sage code in evm/src/cpu/kernel/tests/ecc/bn_glv_test_data -// Given scalar `k ∈ Bn254::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^127` and such that -// `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism. -// In the comments below, N means @BN_SCALAR -// -// Z3 proof that the resulting `k1, k2` satisfy `k1>0`, `k1 < 2^127` and `|k2| < 2^127`. -// ```python -// from z3 import Solver, Int, Or, unsat -// q = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001 -// glv_s = 0xB3C4D79D41A917585BFC41088D8DAAA78B17EA66B99C90DD -// -// b2 = 0x89D3256894D213E3 -// b1 = -0x6F4D8248EEB859FC8211BBEB7D4F1128 -// -// g1 = 0x24CCEF014A773D2CF7A7BD9D4391EB18D -// g2 = 0x2D91D232EC7E0B3D7 -// k = Int("k") -// c1 = Int("c1") -// c2 = Int("c2") -// s = Solver() -// -// c2p = -c2 -// s.add(k < q) -// s.add(0 < k) -// s.add(c1 * (2**256) <= g2 * k) -// s.add((c1 + 1) * (2**256) > g2 * k) -// s.add(c2p * (2**256) <= g1 * k) -// s.add((c2p + 1) * (2**256) > g1 * k) -// -// q1 = c1 * b1 -// q2 = c2 * b2 -// -// k2 = q2 - q1 -// k2L = (glv_s * k2) % q -// k1 = k - k2L -// k2 = -k2 -// -// s.add(Or((k2 >= 2**127), (-k2 >= 2**127), (k1 >= 2**127), (k1 < 0))) -// -// assert s.check() == unsat -// ``` -global bn_glv_decompose: - // stack: k, retdest - %mod_const(@BN_SCALAR) - PUSH @BN_SCALAR DUP1 DUP1 - // Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d. - PUSH @U256_MAX - // stack: -1, N, N, N, k, retdest - PUSH @BN_GLV_MINUS_G1 DUP6 - // stack: k, g1, -1, N, N, N, k, retdest - MULMOD - // stack: (k * g1 % -1), N, N, N, k, retdest - PUSH @BN_GLV_MINUS_G1 DUP6 - // stack: k, g1, (k * g1 % -1), N, N, N, k, retdest - MUL - // stack: bottom = (k * g1), (k * g1 % -1), N, N, N, k, retdest - DUP1 DUP3 - // stack: (k * g1 % -1), bottom, bottom, (k * g1 % -1), N, N, N, k, retdest - LT SWAP2 SUB SUB - // stack: c2, N, N, N, k, retdest - PUSH @BN_GLV_B2 MULMOD - // stack: q2=c2*b2, N, N, k, retdest - - // Use the same trick to compute c1 = top 256 bits of g2*k. - PUSH @BN_SCALAR PUSH @U256_MAX - PUSH @BN_GLV_G2 DUP7 MULMOD - PUSH @BN_GLV_G2 DUP7 MUL - DUP1 DUP3 LT - SWAP2 SUB SUB - // stack: c1, N, q2, N, N, k, retdest - PUSH @BN_GLV_B1 MULMOD - // stack: q1, q2, N, N, k, retdest - - // We compute k2 = q1 + q2 - N, but we check for underflow and return N-q1-q2 instead if there is one, - // along with a flag `underflow` set to 1 if there is an underflow, 0 otherwise. - ADD %bn_sub_check_underflow - // stack: k2, underflow, N, k, retdest - DUP1 %ge_const(0x80000000000000000000000000000000) %jumpi(negate) - %jump(contd) -negate: - // stack: k2, underflow, N, k, retdest - SWAP1 PUSH 1 SUB SWAP1 - PUSH @BN_SCALAR SUB -contd: - // stack: k2, underflow, N, k, retdest - SWAP3 PUSH @BN_SCALAR DUP5 PUSH @BN_GLV_S - // stack: s, k2, N, k, underflow, N, k2, retdest - MULMOD - // stack: s*k2, k, underflow, N, k2, retdest - // Need to return `k + s*k2` if no underflow occur, otherwise return `k - s*k2` which is done in the `underflowed` fn. - SWAP2 DUP1 %jumpi(underflowed) - %stack (underflow, k, x, N, k2) -> (k, x, N, k2, underflow) - ADDMOD - %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) - JUMP - -underflowed: - // stack: underflow, k, s*k2, N, k2 - // Compute (k-s*k2)%N. - %stack (u, k, x, N, k2) -> (k, x, N, k2, u) - SUBMOD - %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) - JUMP - -%macro bn_sub_check_underflow - // stack: x, y - DUP2 DUP2 LT - // stack: x=y, x (x, y, b, a, c) - SUB MUL ADD - %stack (res, bool) -> (res, @BN_SCALAR, bool) - MOD -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm deleted file mode 100644 index 99cf24e71d..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/miller_loop.asm +++ /dev/null @@ -1,325 +0,0 @@ -/// def miller(P, Q): -/// miller_init() -/// miller_loop() -/// -/// def miller_init(): -/// out = 1 -/// O = P -/// times = 61 -/// -/// def miller_loop(): -/// while times: -/// 0xnm = load(miller_data) -/// while 0xnm > 0x20: -/// miller_one() -/// while 0xnm: -/// miller_zero() -/// times -= 1 -/// -/// def miller_one(): -/// 0xnm -= 0x20 -/// mul_tangent() -/// mul_cord() -/// -/// def miller_zero(): -/// 0xnm -= 1 -/// mul_tangent() - -global bn254_miller: - // stack: ptr, out, retdest - %stack (ptr, out) -> (out, ptr, out) - // stack: out, ptr, out, retdest - %write_fp254_12_unit - // stack: ptr, out, retdest - %load_fp254_6 - // stack: P, Q, out, retdest - %stack (P: 2) -> (0, 53, P, P) - // stack: 0, 53, O, P, Q, out, retdest - // the head 0 lets miller_loop start with POP -miller_loop: - POP - // stack: times , O, P, Q, out, retdest - DUP1 - ISZERO - // stack: break?, times , O, P, Q, out, retdest - %jumpi(miller_return) - // stack: times , O, P, Q, out, retdest - %sub_const(1) - // stack: times-1, O, P, Q, out, retdest - DUP1 - // stack: times-1, times-1, O, P, Q, out, retdest - %mload_kernel_code(miller_data) - // stack: 0xnm, times-1, O, P, Q, out, retdest - %jump(miller_one) -miller_return: - // stack: times, O, P, Q, out, retdest - %stack (times, O: 2, P: 2, Q: 4, out, retdest) -> (retdest) - // stack: retdest - %clear_line - JUMP - -miller_one: - // stack: 0xnm, times, O, P, Q, out, retdest - DUP1 - %lt_const(0x20) - // stack: skip?, 0xnm, times, O, P, Q, out, retdest - %jumpi(miller_zero) - // stack: 0xnm, times, O, P, Q, out, retdest - %sub_const(0x20) - // stack: 0x{n-1}m, times, O, P, Q, out, retdest - PUSH mul_cord - // stack: mul_cord, 0x{n-1}m, times, O, P, Q, out, retdest - %jump(mul_tangent) - -miller_zero: - // stack: m , times, O, P, Q, out, retdest - DUP1 - ISZERO - // stack: skip?, m , times, O, P, Q, out, retdest - %jumpi(miller_loop) - // stack: m , times, O, P, Q, out, retdest - %sub_const(1) - // stack: m-1, times, O, P, Q, out, retdest - PUSH miller_zero - // stack: miller_zero, m-1, times, O, P, Q, out, retdest - %jump(mul_tangent) - - -/// def mul_tangent() -/// out = square_fp254_12(out) -/// line = tangent(O, Q) -/// out = mul_fp254_12_sparse(out, line) -/// O += O - -mul_tangent: - // stack: retdest, 0xnm, times, O, P, Q, out - PUSH mul_tangent_2 - DUP13 - PUSH mul_tangent_1 - // stack: mul_tangent_1, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - %stack (mul_tangent_1, out) -> (out, out, mul_tangent_1, out) - // stack: out, out, mul_tangent_1, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - %jump(square_fp254_12) -mul_tangent_1: - // stack: out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - DUP13 - DUP13 - DUP13 - DUP13 - // stack: Q, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - DUP11 - DUP11 - // stack: O, Q, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out - %tangent - // stack: out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out {12: line} - %stack (out) -> (out, 12, out) - // stack: out, 12, out, mul_tangent_2, retdest, 0xnm, times, O, P, Q, out {12: line} - %jump(mul_fp254_12_sparse) -mul_tangent_2: - // stack: retdest, 0xnm, times, O, P, Q, out {12: line} - PUSH after_double - // stack: after_double, retdest, 0xnm, times, O, P, Q, out {12: line} - DUP6 - DUP6 - // stack: O, after_double, retdest, 0xnm, times, O, P, Q, out {12: line} - %jump(bn_double) -after_double: - // stack: 2*O, retdest, 0xnm, times, O, P, Q, out {12: line} - SWAP5 - POP - SWAP5 - POP - // stack: retdest, 0xnm, times, 2*O, P, Q, out {12: line} - JUMP - -/// def mul_cord() -/// line = cord(P, O, Q) -/// out = mul_fp254_12_sparse(out, line) -/// O += P - -mul_cord: - // stack: 0xnm, times, O, P, Q, out - PUSH mul_cord_1 - // stack: mul_cord_1, 0xnm, times, O, P, Q, out - DUP11 - DUP11 - DUP11 - DUP11 - // stack: Q, mul_cord_1, 0xnm, times, O, P, Q, out - DUP9 - DUP9 - // stack: O, Q, mul_cord_1, 0xnm, times, O, P, Q, out - DUP13 - DUP13 - // stack: P, O, Q, mul_cord_1, 0xnm, times, O, P, Q, out - %cord - // stack: mul_cord_1, 0xnm, times, O, P, Q, out {12: line} - DUP12 - // stack: out, mul_cord_1, 0xnm, times, O, P, Q, out {12: line} - %stack (out) -> (out, 12, out) - // stack: out, 12, out, mul_cord_1, 0xnm, times, O, P, Q, out {12: line} - %jump(mul_fp254_12_sparse) -mul_cord_1: - // stack: 0xnm, times, O , P, Q, out - PUSH after_add - // stack: after_add, 0xnm, times, O , P, Q, out - DUP7 - DUP7 - DUP7 - DUP7 - // stack: O , P, after_add, 0xnm, times, O , P, Q, out - %jump(bn_add_valid_points) -after_add: - // stack: O + P, 0xnm, times, O , P, Q, out - SWAP4 - POP - SWAP4 - POP - // stack: 0xnm, times, O+P, P, Q, out - %jump(miller_one) - - -/// def tangent(px, py, qx, qy): -/// return sparse_store( -/// py**2 - 9, -/// (-3px**2) * qx, -/// (2py) * qy, -/// ) - -%macro tangent - // stack: px, py, qx, qx_, qy, qy_ - PUSH 12 - %create_bn254_pairing_address - %stack (addr12, px, py) -> (py, py, 9, addr12, addr12, px, py) - // stack: py, py, 9, addr12, addr12, px, py, qx, qx_, qy, qy_ - MULFP254 - // stack: py^2, 9, addr12, addr12, px, py, qx, qx_, qy, qy_ - SUBFP254 - // stack: py^2 - 9, addr12, addr12, px, py, qx, qx_, qy, qy_ - MSTORE_GENERAL - // stack: addr12, px, py, qx, qx_, qy, qy_ - %add_const(2) DUP1 - SWAP2 - DUP1 - MULFP254 - // stack: px^2, addr14, addr14, py, qx, qx_, qy, qy_ - PUSH 3 - MULFP254 - // stack: 3*px^2, addr14, addr14, py, qx, qx_, qy, qy_ - PUSH 0 - SUBFP254 - // stack: -3*px^2, addr14, addr14, py, qx, qx_, qy, qy_ - SWAP4 - // stack: qx, addr14, addr14, py, -3px^2, qx_, qy, qy_ - DUP5 - MULFP254 - // stack: (-3*px^2)qx, addr14, addr14, py, -3px^2, qx_, qy, qy_ - MSTORE_GENERAL - // stack: addr14, py, -3px^2, qx_, qy, qy_ - DUP1 %add_const(6) - // stack: addr20, addr14, py, -3px^2, qx_, qy, qy_ - %stack (addr20, addr14, py) -> (2, py, addr20, addr14) - MULFP254 - // stack: 2py, addr20, addr14, -3px^2, qx_, qy, qy_ - SWAP5 - // stack: qy, addr20, addr14, -3px^2, qx_, 2py, qy_ - DUP6 - MULFP254 - // stack: (2py)qy, addr20, addr14, -3px^2, qx_, 2py, qy_ - MSTORE_GENERAL - // stack: addr14, -3px^2, qx_, 2py, qy_ - %add_const(1) SWAP2 - // stack: qx_, -3px^2, addr15, 2py, qy_ - MULFP254 - // stack: (-3px^2)*qx_, addr15, 2py, qy_ - MSTORE_GENERAL - // stack: 2py, qy_ - MULFP254 - // stack: (2py)*qy_ - %mstore_bn254_pairing(21) -%endmacro - -/// def cord(p1x, p1y, p2x, p2y, qx, qy): -/// return sparse_store( -/// p1y*p2x - p2y*p1x, -/// (p2y - p1y) * qx, -/// (p1x - p2x) * qy, -/// ) - -%macro cord - // stack: p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - DUP1 - DUP5 - MULFP254 - // stack: p2y*p1x, p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - DUP3 - DUP5 - MULFP254 - // stack: p1y*p2x , p2y*p1x, p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - SUBFP254 - // stack: p1y*p2x - p2y*p1x, p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - %mstore_bn254_pairing(12) - // stack: p1x , p1y, p2x , p2y, qx, qx_, qy, qy_ - SWAP3 - // stack: p2y , p1y, p2x , p1x, qx, qx_, qy, qy_ - SUBFP254 - // stack: p2y - p1y, p2x , p1x, qx, qx_, qy, qy_ - SWAP2 - // stack: p1x , p2x, p2y - p1y, qx, qx_, qy, qy_ - SUBFP254 - // stack: p1x - p2x, p2y - p1y, qx, qx_, qy, qy_ - SWAP4 - // stack: qy, p2y - p1y, qx, qx_, p1x - p2x, qy_ - DUP5 - MULFP254 - // stack: (p1x - p2x)qy, p2y - p1y, qx, qx_, p1x - p2x, qy_ - %mstore_bn254_pairing(20) - // stack: p2y - p1y, qx, qx_, p1x - p2x, qy_ - SWAP1 - // stack: qx, p2y - p1y, qx_, p1x - p2x, qy_ - DUP2 - MULFP254 - // stack: (p2y - p1y)qx, p2y - p1y, qx_, p1x - p2x, qy_ - %mstore_bn254_pairing(14) - // stack: p2y - p1y, qx_, p1x - p2x, qy_ - MULFP254 - // stack: (p2y - p1y)qx_, p1x - p2x, qy_ - %mstore_bn254_pairing(15) - // stack: p1x - p2x, qy_ - MULFP254 - // stack: (p1x - p2x)*qy_ - %mstore_bn254_pairing(21) -%endmacro - -%macro clear_line - PUSH 12 - %create_bn254_pairing_address - // stack: addr12 - DUP1 %add_const(2) - // stack: addr14, addr12 - DUP1 %add_const(1) - // stack: addr15, addr14, addr12 - DUP1 %add_const(5) - // stack: addr20, addr15, addr14, addr12 - DUP1 %add_const(1) - // stack: addr21, addr20, addr15, addr14, addr12 - %rep 5 - PUSH 0 MSTORE_GENERAL - %endrep -%endmacro - - -%macro write_fp254_12_unit - // Write 0x10000000000000000000000 with MSTORE_32BYTES_12, - // effectively storing 1 at the initial offset, and 11 0s afterwards. - - // stack: out - %create_bn254_pairing_address - // stack: addr - PUSH 0x10000000000000000000000 - SWAP1 - // stack: addr, 0x10000000000000000000000 - MSTORE_32BYTES_12 - POP - // stack: -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm deleted file mode 100644 index d5b97312ba..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/msm.asm +++ /dev/null @@ -1,73 +0,0 @@ -// Computes the multiplication `a*G` using a standard MSM with the GLV decomposition of `a`. -// see there for a detailed description. -global bn_msm: - // stack: retdest - PUSH 0 PUSH 0 PUSH 0 -global bn_msm_loop: - // stack: accx, accy, i, retdest - DUP3 %bn_mload_wnaf_a - // stack: w, accx, accy, i, retdest - DUP1 %jumpi(bn_msm_loop_add_a_nonzero) - POP -msm_loop_add_b: - //stack: accx, accy, i, retdest - DUP3 %bn_mload_wnaf_b - // stack: w, accx, accy, i, retdest - DUP1 %jumpi(bn_msm_loop_add_b_nonzero) - POP -msm_loop_contd: - %stack (accx, accy, i, retdest) -> (i, i, accx, accy, retdest) - // TODO: the GLV scalars for the BN curve are 127-bit, so could use 127 here. But this would require modifying `wnaf.asm`. Not sure it's worth it... - %eq_const(129) %jumpi(msm_end) - %increment - //stack: i+1, accx, accy, retdest - %stack (i, accx, accy, retdest) -> (accx, accy, bn_msm_loop, i, retdest) - %jump(bn_double) - -msm_end: - %stack (i, accx, accy, retdest) -> (retdest, accx, accy) - JUMP - -bn_msm_loop_add_a_nonzero: - %stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_add_b, i, retdest) - %bn_mload_point_a - // stack: px, py, accx, accy, msm_loop_add_b, i, retdest - %jump(bn_add_valid_points) - -bn_msm_loop_add_b_nonzero: - %stack (w, accx, accy, i, retdest) -> (w, accx, accy, msm_loop_contd, i, retdest) - %bn_mload_point_b - // stack: px, py, accx, accy, msm_loop_contd, i, retdest - %jump(bn_add_valid_points) - -%macro bn_mload_wnaf_a - // stack: i - %mload_current(@SEGMENT_BN_WNAF_A) -%endmacro - -%macro bn_mload_wnaf_b - // stack: i - %mload_current(@SEGMENT_BN_WNAF_B) -%endmacro - -%macro bn_mload_point_a - // stack: w - DUP1 - %mload_current(@SEGMENT_BN_TABLE_Q) - //stack: Gy, w - SWAP1 %decrement %mload_current(@SEGMENT_BN_TABLE_Q) - //stack: Gx, Gy -%endmacro - -%macro bn_mload_point_b - // stack: w - DUP1 - %mload_current(@SEGMENT_BN_TABLE_Q) - PUSH @BN_BNEG_LOC %mload_current(@SEGMENT_BN_TABLE_Q) - %stack (bneg, Gy, w) -> (@BN_BASE, Gy, bneg, bneg, Gy, w) - SUB SWAP1 ISZERO MUL SWAP2 MUL ADD - SWAP1 %decrement %mload_current(@SEGMENT_BN_TABLE_Q) - //stack: Gx, Gy - PUSH @BN_GLV_BETA - MULFP254 -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm deleted file mode 100644 index 735d001aae..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/pairing.asm +++ /dev/null @@ -1,194 +0,0 @@ -/// The input to the pairing script is a list of points -/// P_i = n_i*G: Curve, Q_i = m_i*H: TwistedCurve -/// where G, H are the respective generators, such that -/// sum_i n_i*m_i = 0 -/// and therefore, due to bilinearity of the pairing: -/// prod_i e(P_i, Q_i) -/// = prod_i e(n_i G, m_i H) -/// = prod_i e(G,H)^{n_i * m_i} -/// = e(G,H)^{sum_i n_i * m_i} -/// = e(G,H)^0 -/// = 1: Fp12 - -/// def bn254_pairing(pairs: List((Curve, TwistedCurve))) -> Bool: -/// -/// for P, Q in pairs: -/// if not (P.is_valid and Q.is_valid): -/// return @U256_MAX -/// -/// out = 1 -/// for P, Q in pairs: -/// if P != 0 and Q != 0: -/// out *= miller_loop(P, Q) -/// -/// result = bn254_final_exponent(out) -/// return result == unit_fp12 - -/// The following is a key to this API -/// -/// - k is the number of inputs -/// - each input given by a pair of points, one on the curve and one on the twisted curve -/// - each input consists of 6 stack terms---2 for the curve point and 4 for the twisted curve point -/// - the inputs are presumed to be placed on the kernel contiguously -/// - the output (as defined above) is an Fp12 element -/// - out and inp are the BnPairing segment offsets for the output element and input -/// - the assembly code currently uses offsets 0-78 for scratch space - -global bn254_pairing: - // stack: k, inp, out, retdest - DUP1 - -bn254_input_check: - // stack: j , k, inp - DUP1 - ISZERO - // stack: end?, j , k, inp - %jumpi(bn254_pairing_start) - // stack: j , k, inp - %sub_const(1) - // stack: j=j-1, k, inp - - %stack (j, k, inp) -> (j, inp, j, k, inp) - // stack: j, inp, j, k, inp - %mul_const(6) - ADD - // stack: inp_j=inp+6j, j, k, inp - DUP1 - // stack: inp_j, inp_j, j, k, inp - %load_fp254_2 - // stack: P_j, inp_j, j, k, inp - %bn_check - // stack: valid?, inp_j, j, k, inp - ISZERO - %jumpi(bn_pairing_invalid_input) - // stack: inp_j, j, k, inp - DUP1 - // stack: inp_j , inp_j, j, k, inp - %add_const(2) - // stack: inp_j', inp_j, j, k, inp - %load_fp254_4 - // stack: Q_j, inp_j, j, k, inp - %bn_check_twisted - // stack: valid?, inp_j, j, k, inp - ISZERO - %jumpi(bn_pairing_invalid_input) - // stack: inp_j, j, k, inp - POP - %jump(bn254_input_check) - -bn_pairing_invalid_input: - // stack: inp_j, j, k, inp, out, retdest - %stack (inp_j, j, k, inp, out, retdest) -> (retdest, @U256_MAX) - JUMP - -bn254_pairing_start: - // stack: 0, k, inp, out, retdest - %stack (j, k, inp, out) -> (out, k, inp, out, bn254_pairing_output_validation, out) - // stack: out, k, inp, out, bn254_pairing_output_validation, out, retdest - %mstore_bn254_pairing_value(1) - // stack: k, inp, out, bn254_pairing_output_validation, out, retdest - -bn254_pairing_loop: - // stack: k, inp, out, bn254_pairing_output_validation, out, retdest - DUP1 - ISZERO - // stack: end?, k, inp, out, bn254_pairing_output_validation, out, retdest - %jumpi(bn254_final_exponent) - // stack: k, inp, out, bn254_pairing_output_validation, out, retdest - %sub_const(1) - // stack: k=k-1, inp, out, bn254_pairing_output_validation, out, retdest - %stack (k, inp) -> (k, inp, k, inp) - // stack: k, inp, k, inp, out, bn254_pairing_output_validation, out, retdest - %mul_const(6) - ADD - // stack: inp_k, k, inp, out, bn254_pairing_output_validation, out, retdest - DUP1 - %load_fp254_6 - // stack: P, Q, inp_k, k, inp, out, bn254_pairing_output_validation, out, retdest - %neutral_input - // stack: skip?, inp_k, k, inp, out, bn254_pairing_output_validation, out, retdest - %jumpi(bn_skip_input) - // stack: inp_k, k, inp, out, bn254_pairing_output_validation, out, retdest - %stack (inp_k, k, inp, out) -> (bn254_miller, inp_k, 0, mul_fp254_12, 0, out, out, bn254_pairing_loop, k, inp, out) - // stack: bn254_miller, inp_k, 0, - // mul_fp254_12, 0, out, out, - // bn254_pairing_loop, k, inp, out, - // bn254_pairing_output_validation, out, retdest - JUMP - -bn_skip_input: - // stack: inp_k, k, inp, out, bn254_pairing_output_validation, out, retdest - POP - // stack: k, inp, out, bn254_pairing_output_validation, out, retdest - %jump(bn254_pairing_loop) - - -bn254_pairing_output_validation: - // stack: out, retdest - %create_bn254_pairing_address - PUSH 1 - // stack: check, out_addr, retdest - %check_output_term - %check_output_term(1) - %check_output_term(2) - %check_output_term(3) - %check_output_term(4) - %check_output_term(5) - %check_output_term(6) - %check_output_term(7) - %check_output_term(8) - %check_output_term(9) - %check_output_term(10) - %check_output_term(11) - // stack: check, out_addr, retdest - %stack (check, out_addr, retdest) -> (retdest, check) - JUMP - -%macro check_output_term - // stack: check, out - DUP2 - // stack: out0, check, out - MLOAD_GENERAL - // stack: f0, check, out - %eq_const(1) - // stack: check0, check, out - MUL - // stack: check, out -%endmacro - -%macro check_output_term(j) - // stack: check, out - DUP2 - %add_const($j) - // stack: outj, check, out - MLOAD_GENERAL - // stack: fj, check, out - ISZERO - // stack: checkj, check, out - MUL - // stack: check, out -%endmacro - -%macro neutral_input - // stack: P , Q - ISZERO - SWAP1 - ISZERO - MUL - // stack: P==0, Q - SWAP4 - // stack: Q , P==0 - ISZERO - SWAP1 - ISZERO - MUL - SWAP1 - ISZERO - MUL - SWAP1 - ISZERO - MUL - // stack: Q==0, P==0 - OR - // stack: Q==0||P==0 -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm deleted file mode 100644 index 5ee6685fe6..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/precomputation.asm +++ /dev/null @@ -1,35 +0,0 @@ -// Precompute a table of multiples of the BN254 point `Q = (Qx, Qy)`. -// Let `(Qxi, Qyi) = i * Q`, then store in the `SEGMENT_BN_TABLE_Q` segment of memory the values -// `i-1 => Qxi`, `i => Qyi if i < 16 else -Qy(32-i)` for `i in range(1, 32, 2)`. -global bn_precompute_table: - // stack: Qx, Qy, retdest - PUSH precompute_table_contd DUP3 DUP3 - %jump(bn_double) -precompute_table_contd: - // stack: Qx2, Qy2, Qx, Qy, retdest - PUSH 1 -bn_precompute_table_loop: - // stack i, Qx2, Qy2, Qx, Qy, retdest - PUSH 1 DUP2 SUB - %stack (im, i, Qx2, Qy2, Qx, Qy, retdest) -> (i, Qy, im, Qx, i, Qx2, Qy2, Qx, Qy, retdest) - %mstore_current(@SEGMENT_BN_TABLE_Q) %mstore_current(@SEGMENT_BN_TABLE_Q) - // stack: i, Qx2, Qy2, Qx, Qy, retdest - DUP1 PUSH 32 SUB PUSH 1 DUP2 SUB - // stack: 31-i, 32-i, i, Qx2, Qy2, Qx, Qy, retdest - DUP7 PUSH @BN_BASE SUB - // TODO: Could maybe avoid storing Qx a second time here, not sure if it would be more efficient. - %stack (Qyy, iii, ii, i, Qx2, Qy2, Qx, Qy, retdest) -> (iii, Qx, ii, Qyy, i, Qx2, Qy2, Qx, Qy, retdest) - %mstore_current(@SEGMENT_BN_TABLE_Q) %mstore_current(@SEGMENT_BN_TABLE_Q) - // stack: i, Qx2, Qy2, Qx, Qy, retdest - PUSH 2 ADD - // stack: i+2, Qx2, Qy2, Qx, Qy, retdest - DUP1 PUSH 16 LT %jumpi(precompute_table_end) - %stack (i, Qx2, Qy2, Qx, Qy, retdest) -> (Qx, Qy, Qx2, Qy2, precompute_table_loop_contd, i, Qx2, Qy2, retdest) - %jump(bn_add_valid_points) -precompute_table_loop_contd: - %stack (Qx, Qy, i, Qx2, Qy2, retdest) -> (i, Qx2, Qy2, Qx, Qy, retdest) - %jump(bn_precompute_table_loop) - -precompute_table_end: - // stack: i, Qx2, Qy2, Qx, Qy, retdest - %pop5 JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm deleted file mode 100644 index 859c45fe3b..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/twisted_curve.asm +++ /dev/null @@ -1,94 +0,0 @@ -// Check if (X,Y) is a valid curve point. -// Returns (range & curve) || ident -// where -// range = (x < N) & (x_ < N) & (y < N) & (y_ < N) -// curve = Y^2 == X^3 + 3/(9+i) -// ident = (X,Y) == (0,0) - -%macro bn_check_twisted - // stack: x, x_, y, y_ - %bn_check_twisted_range - // stack: range, x, x_, y, y_ - %bn_check_twisted_curve - // stack: curve , range, x, x_, y, y_ - MUL // Cheaper than AND - // stack: curve & range, x, x_, y, y_ - SWAP4 - // stack: y_, x, x_, y, curve & range - %bn_check_twisted_ident - // stack: ident , curve & range - OR - // stack: ident || (curve & range) -%endmacro - -%macro bn_check_twisted_range - // stack: x, x_, y, y_ - PUSH @BN_BASE - // stack: N, x, x_, y, y_ - %stack (N) -> (N, N, N, N) - // stack: N, N, N, N, x, x_, y, y_ - DUP8 - // stack: y_ , N, N, N, N, x, x_, y, y_ - LT - // stack: y_ < N, N, N, N, x, x_, y, y_ - SWAP3 - // stack: N, N, N, y_ < N, x, x_, y, y_ - DUP7 - // stack: y , N, N, N, y_ < N, x, x_, y, y_ - LT - // stack: y < N, N, N, y_ < N, x, x_, y, y_ - SWAP2 - // stack: N, N, y < N, y_ < N, x, x_, y, y_ - DUP6 - // stack: x_ , N, N, y < N, y_ < N, x, x_, y, y_ - LT - // stack: x_ < N, N, y < N, y_ < N, x, x_, y, y_ - SWAP1 - // stack: N, x_ < N, y < N, y_ < N, x, x_, y, y_ - DUP5 - // stack: x , N, x_ < N, y < N, y_ < N, x, x_, y, y_ - LT - // stack: x < N, x_ < N, y < N, y_ < N, x, x_, y, y_ - MUL // Cheaper than AND - MUL // Cheaper than AND - MUL // Cheaper than AND - // stack: range, x, x_, y, y_ -%endmacro - -%macro bn_check_twisted_curve - // stack: range, X, Y - %stack (range, X: 2, Y: 2) -> (Y, Y, range, X, Y) - // stack: Y, Y, range, X, Y - %mul_fp254_2 - // stack: Y^2, range, X, Y - %stack () -> (@BN_TWISTED_RE, @BN_TWISTED_IM) - // stack: A, Y^2, range, X, Y - %stack (A: 2, Y2: 2, range, X: 2) -> (X, X, X, A, Y2, range, X) - // stack: X, X, X, A, Y^2, range, X, Y - %mul_fp254_2 - %mul_fp254_2 - // stack: X^3 , A, Y^2, range, X, Y - %add_fp254_2 - // stack: X^3 + A, Y^2, range, X, Y - %eq_fp254_2 - // stack: curve, range, X, Y -%endmacro - -%macro bn_check_twisted_ident - SWAP2 - // stack: a , b , c , d - ISZERO - SWAP3 - // stack: d , b , c , a==0 - ISZERO - SWAP2 - // stack: c , b , d==0, a==0 - ISZERO - SWAP1 - // stack: b , c==0, d==0, a==0 - ISZERO - // stack: b==0, c==0, d==0, a==0 - MUL // Cheaper than AND - MUL // Cheaper than AND - MUL // Cheaper than AND -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_12_mul.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_12_mul.asm deleted file mode 100644 index 45016ed155..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_12_mul.asm +++ /dev/null @@ -1,303 +0,0 @@ -/////////////////////////////////////// -///// GENERAL FP12 MULTIPLICATION ///// -/////////////////////////////////////// - -/// inputs: -/// F = f + f'z -/// G = g + g'z -/// -/// output: -/// H = h + h'z = FG -/// -/// h = fg + sh(f'g') -/// h' = (f+f')(g+g') - fg - f'g' -/// -/// memory pointers [ind' = ind+6] -/// {inA: f, inA: f', inB: g, inB':g', out: h, out': h'} -/// -/// f, f', g, g' consist of six elements on the stack - -global mul_fp254_12: - // stack: inA, inB, out - DUP1 - %add_const(6) - // stack: inA', inA, inB, out - %load_fp254_6 - // stack: f', inA, inB, out - DUP8 - %add_const(6) - // stack: inB', f', inA, inB, out - %load_fp254_6 - // stack: g', f', inA, inB, out - PUSH mul_fp254_12_1 - // stack: mul_fp254_12_1, g', f', inA, inB, out - %dup_fp254_6_7 - // stack: f', mul_fp254_12_1, g', f', inA, inB, out - %dup_fp254_6_7 - // stack: g', f', mul_fp254_12_1, g', f', inA, inB, out - %jump(mul_fp254_6) -mul_fp254_12_1: - // stack: f'g', g' , f', inA, inB, out - %dup_fp254_6_0 - // stack: f'g', f'g', g' , f', inA, inB, out - %store_fp254_6_sh(60) - // stack: f'g', g' , f', inA, inB, out {60: sh(f'g')} - %store_fp254_6(66) - // stack: g' , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - DUP13 - // stack: inA, g' , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - DUP15 - // stack: inB, inA, g' , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %load_fp254_6 - // stack: g , inA, g' , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: g', inA, g , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %dup_fp254_6_7 - // stack: g,g', inA, g , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %add_fp254_6 - // stack: g+g', inA, g , f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: g, inA, g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g'} - PUSH mul_fp254_12_2 - // stack: mul_fp254_12_2, g, inA, g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g'} - SWAP7 - // stack: inA, g, mul_fp254_12_2, g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %load_fp254_6 - // stack: f, g, mul_fp254_12_2, g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %jump(mul_fp254_6) -mul_fp254_12_2: - // stack: fg, g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g'} - %store_fp254_6(72) - // stack: g+g', f', inA, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %stack (x: 6, y: 6) -> (y, x) - // stack: f', g+g', inA, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - PUSH mul_fp254_12_3 - // stack: mul_fp254_12_3, f', g+g', inA, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - SWAP13 - // stack: inA, f', g+g', mul_fp254_12_3, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %load_fp254_6 - // stack: f,f', g+g', mul_fp254_12_3, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %add_fp254_6 - // stack: f+f', g+g', mul_fp254_12_3, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %jump(mul_fp254_6) -mul_fp254_12_3: - // stack: (f+f')(g+g'), inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %load_fp254_6(72) - // stack: fg, (f+f')(g+g'), inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %stack (x: 6, y: 6) -> (y, x) - // stack: (f+f')(g+g'), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %dup_fp254_6_6 - // stack: fg, (f+f')(g+g'), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %load_fp254_6(66) - // stack: f'g',fg, (f+f')(g+g'), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %add_fp254_6 - // stack: f'g'+fg, (f+f')(g+g'), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %subr_fp254_6 - // stack: (f+f')(g+g') - (f'g'+fg), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - DUP14 - %add_const(6) - // stack: out', (f+f')(g+g') - (f'g'+fg), fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %store_fp254_6 - // stack: fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %load_fp254_6(60) - // stack: sh(f'g') , fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %add_fp254_6 - // stack: sh(f'g') + fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - DUP8 - // stack: out, sh(f'g') + fg, inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %store_fp254_6 - // stack: inB, out {60: sh(f'g'), 66: f'g', 72: fg} - %pop2 - JUMP - - -////////////////////////////////////// -///// SPARSE FP12 MULTIPLICATION ///// -////////////////////////////////////// - -/// input: -/// F = f + f'z -/// G = g0 + (G1)t + (G2)tz -/// -/// output: -/// H = h + h'z = FG -/// = g0 * [f + f'z] + G1 * [sh(f) + sh(f')z] + G2 * [sh2(f') + sh(f)z] -/// -/// h = g0 * f + G1 * sh(f ) + G2 * sh2(f') -/// h' = g0 * f' + G1 * sh(f') + G2 * sh (f ) -/// -/// memory pointers [ind' = ind+6, inB2 = inB1 + 2 = inB + 3] -/// { inA: f, inA': f', inB: g0, inB1: G1, inB2: G2, out: h, out': h'} -/// -/// f, f' consist of six elements; G1, G1' consist of two elements; and g0 of one element - -global mul_fp254_12_sparse: - // stack: inA, inB, out - DUP1 - %add_const(6) - // stack: inA', inA, inB, out - %load_fp254_6 - // stack: f', inA, inB, out - DUP8 - // stack: inB, f', inA, inB, out - DUP8 - // stack: inA, inB, f', inA, inB, out - %load_fp254_6 - // stack: f, inB, f', inA, inB, out - DUP16 - // stack: out, f, inB, f', inA, inB, out - %dup_fp254_6_8 - // stack: f', out, f, inB, f', inA, inB, out - DUP14 - // stack: inB, f', out, f, inB, f', inA, inB, out - %dup_fp254_6_8 - // stack: f, inB, f', out, f, inB, f', inA, inB, out - DUP7 - // stack: inB, f, inB, f', out, f, inB, f', inA, inB, out - %dup_fp254_6_8 - // stack: f', inB, f, inB, f', out, f, inB, f', inA, inB, out - %dup_fp254_6_7 - // stack: f, f', inB, f, inB, f', out, f, inB, f', inA, inB, out - DUP13 - // stack: inB, f, f', inB, f, inB, f', out, f, inB, f', inA, inB, out - %mload_bn254_pairing - // stack: g0 , f, f', inB, f, inB, f', out, f, inB, f', inA, inB, out - %scale_re_fp254_6 - // stack: g0 * f, f', inB, f, inB, f', out, f, inB, f', inA, inB, out - %stack (x: 6, y: 6) -> (y, x) - // stack: f' , g0 * f, inB, f, inB, f', out, f, inB, f', inA, inB, out - DUP13 - %add_const(8) - // stack: inB2, f' , g0 * f, inB, f, inB, f', out, f, inB, f', inA, inB, out - %load_fp254_2 - // stack: G2 , f' , g0 * f, inB, f, inB, f', out, f, inB, f', inA, inB, out - %scale_fp254_6_sh2 - // stack: G2 * sh2(f') , g0 * f, inB, f, inB, f', out, f, inB, f', inA, inB, out - %add_fp254_6 - // stack: G2 * sh2(f') + g0 * f, inB, f, inB, f', out, f, inB, f', inA, inB, out - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: f , inB, G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - DUP7 %add_const(2) - // stack: inB1, f , inB, G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - %load_fp254_2 - // stack: G1 , f , inB, G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - %scale_fp254_6_sh - // stack: G1 * sh(f), inB, G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - %add_fp254_6_hole - // stack: G1 * sh(f) + G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - DUP14 - // stack: out, G1 * sh(f) + G2 * sh2(f') + g0 * f, inB, f', out, f, inB, f', inA, inB, out - %store_fp254_6 - // stack: inB, f', out, f, inB, f', inA, inB, out - %mload_bn254_pairing - // stack: g0 , f', out, f, inB, f', inA, inB, out - %scale_re_fp254_6 - // stack: g0 * f', out, f, inB, f', inA, inB, out - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: f , out, g0 * f', inB, f', inA, inB, out - DUP14 - %add_const(8) - // stack: inB2, f , out, g0 * f', inB, f', inA, inB, out - %load_fp254_2 - // stack: G2 , f , out, g0 * f', inB, f', inA, inB, out - %scale_fp254_6_sh - // stack: G2 * sh(f) , out, g0 * f', inB, f', inA, inB, out - %add_fp254_6_hole - // stack: G2 * sh(f) + g0 * f', inB, f', inA, inB, out - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: f' , inB, G2 * sh(f) + g0 * f', inA, inB, out - DUP7 - %add_const(2) - // stack: inB1, f' , inB, G2 * sh(f) + g0 * f', inA, inB, out - %load_fp254_2 - // stack: G1 , f' , inB, G2 * sh(f) + g0 * f', inA, inB, out - %scale_fp254_6_sh - // stack: G1 * sh(f'), inB, G2 * sh(f) + g0 * f', inA, inB, out - %add_fp254_6_hole - // stack: G1 * sh(f') + G2 * sh(f) + g0 * f', inA, inB, out - DUP9 - %add_const(6) - // stack: out', G1 * sh(f') + G2 * sh(f) + g0 * f', inA, inB, out - %store_fp254_6 - // stack: inA, inB, out - %pop3 - JUMP - - -///////////////////////// -///// FP12 SQUARING ///// -///////////////////////// - -/// input: -/// F = f + f'z -/// -/// output: -/// H = h + h'z = FF -/// -/// h = ff + sh(f'f') -/// h' = 2ff' -/// -/// memory pointers [ind' = ind+6] -/// {inp: f, inp: f', out: h, out': h'} -/// -/// f, f' consist of six elements on the stack - -global square_fp254_12: - // stack: inp, out - DUP1 - // stack: inp, inp, out - %load_fp254_6 - // stack: f, inp, out - PUSH square_fp254_12_3 - // stack: square_fp254_12_3, f, inp, out - SWAP7 - // stack: inp, f, square_fp254_12_3, out - PUSH square_fp254_12_2 - // stack: square_fp254_12_2, inp, f, square_fp254_12_3, out - %dup_fp254_6_2 - // stack: f , square_fp254_12_2, inp, f, square_fp254_12_3, out - DUP16 - %add_const(6) - // stack: out', f , square_fp254_12_2, inp, f, square_fp254_12_3, out - PUSH square_fp254_12_1 - // stack: square_fp254_12_1, out', f , square_fp254_12_2, inp, f, square_fp254_12_3, out - DUP10 - %add_const(6) - // stack: inp', square_fp254_12_1, out', f , square_fp254_12_2, inp, f, square_fp254_12_3, out - %load_fp254_6 - // stack: f', square_fp254_12_1, out', f , square_fp254_12_2, inp, f, square_fp254_12_3, out - %stack (f: 6, x: 2, g: 6) -> (g, x, f) - // stack: f , square_fp254_12_1, out', f', square_fp254_12_2, inp, f, square_fp254_12_3, out - %dup_fp254_6_8 - // stack: f', f , square_fp254_12_1, out', f', square_fp254_12_2, inp, f, square_fp254_12_3, out - %jump(mul_fp254_6) -square_fp254_12_1: - // stack: f'f, out', f', square_fp254_12_2, inp, f, square_fp254_12_3, out - DUP7 - // stack: out', f'f, out', f', square_fp254_12_2, inp, f, square_fp254_12_3, out - %store_fp254_6_double - // stack: out', f', square_fp254_12_2, inp, f, square_fp254_12_3, out - POP - // stack: f', square_fp254_12_2, inp, f, square_fp254_12_3, out - %jump(square_fp254_6) -square_fp254_12_2: - // stack: f'f', inp, f, square_fp254_12_3, out - %sh_fp254_6 - // stack: sh(f'f'), inp, f, square_fp254_12_3, out - %stack (f: 6, x, g: 6) -> (g, x, f) - // stack: f, inp, sh(f'f'), square_fp254_12_3, out - SWAP6 - SWAP13 - SWAP6 - // stack: f, square_fp254_12_3, sh(f'f'), inp, out - %jump(square_fp254_6) -square_fp254_12_3: - // stack: ff , sh(f'f'), inp, out - %add_fp254_6 - // stack: ff + sh(f'f'), inp, out - DUP8 - // stack: out, ff + sh(f'f'), inp, out - %store_fp254_6 - // stack: inp, out - %pop2 - JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_6_mul.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_6_mul.asm deleted file mode 100644 index db8b09e0c3..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/degree_6_mul.asm +++ /dev/null @@ -1,435 +0,0 @@ -////////////////////////////////////// -///// GENERAL FP6 MULTIPLICATION ///// -////////////////////////////////////// - -/// inputs: -/// C = C0 + C1t + C2t^2 -/// = (c0 + c0_i) + (c1 + c1_i)t + (c2 + c2_i)t^2 -/// -/// D = D0 + D1t + D2t^2 -/// = (d0 + d0_i) + (d1 + d1_i)t + (d2 + d2_i)t^2 -/// -/// output: -/// E = E0 + E1t + E2t^2 = CD -/// = (e0 + e0_i) + (e1 + e1_i)t + (e2 + e2_i)t^2 -/// -/// initial stack: c0, c0_, c1, c1_, c2, c2_, d0, d0_, d1, d1_, d2, d2_, retdest -/// final stack: e0, e0_, e1, e1_, e2, e2_ - -/// computations: -/// -/// E0 = C0D0 + i9(C1D2 + C2D1) -/// -/// C0D0 = (c0d0 - c0_d0_) + (c0d0_ + c0_d0)i -/// -/// C1D2 = (c1d2 - c1_d2_) + (c1d2_ + c1_d2)i -/// C2D1 = (c2d1 - c2_d1_) + (c2d1_ + c2_d1)i -/// -/// CD12 = C1D2 + C2D1 -/// = (c1d2 + c2d1 - c1_d2_ - c2_d1_) + (c1d2_ + c1_d2 + c2d1_ + c2_d1)i -/// -/// i9(CD12) = (9CD12 - CD12_) + (CD12 + 9CD12_)i -/// -/// e0 = 9CD12 - CD12_ + C0D0 -/// e0_ = 9CD12_ + CD12 + C0D0_ -/// -/// -/// E1 = C0D1 + C1D0 + i9(C2D2) -/// -/// C0D1 = (c0d1 - c0_d1_) + (c0d1_ + c0_d1)i -/// C1D0 = (c1d0 - c1_d0_) + (c1d0_ + c1_d0)i -/// -/// CD01 = c0d1 + c1d0 - (c0_d1_ + c1_d0_) -/// CD01_ = c0d1_ + c0_d1 + c1d0_ + c1_d0 -/// -/// C2D2 = (c2d2 - c2_d2_) + (c2d2_ + c2_d2)i -/// i9(C2D2) = (9C2D2 - C2D2_) + (C2D2 + 9C2D2_)i -/// -/// e1 = 9C2D2 - C2D2_ + CD01 -/// e1_ = C2D2 + 9C2D2_ + CD01_ -/// -/// -/// E2 = C0D2 + C1D1 + C2D0 -/// -/// C0D2 = (c0d2 - c0_d2_) + (c0d2_ + c0_d2)i -/// C1D1 = (c1d1 - c1_d1_) + (c1d1_ + c1_d1)i -/// C2D0 = (c2d0 - c2_d0_) + (c2d0_ + c2_d0)i -/// -/// e2 = c0d2 + c1d1 + c2d0 - (c0_d2_ + c1_d1_ + c2_d0_) -/// e2_ = c0d2_ + c0_d2 + c1d1_ + c1_d1 + c2d0_ + c2_d0 - -// cost: 157 -global mul_fp254_6: - // e2 - // make c0_d2_ + c1_d1_ + c2_d0_ - DUP8 - DUP7 - MULFP254 - DUP11 - DUP6 - MULFP254 - ADDFP254 - DUP13 - DUP4 - MULFP254 - ADDFP254 - // make c0d2 + c1d1 + c2d0 - DUP12 - DUP3 - MULFP254 - DUP11 - DUP6 - MULFP254 - ADDFP254 - DUP9 - DUP8 - MULFP254 - ADDFP254 - // stack: c0d2 + c1d1 + c2d0 , c0_d2_ + c1_d1_ + c2_d0_ - SUBFP254 - // stack: e2 = c0d2 + c1d1 + c2d0 - (c0_d2_ + c1_d1_ + c2_d0_) - SWAP12 - - // e0, e0_ - // make CD12_ = c1d2_ + c1_d2 + c2d1_ + c2_d1 - DUP1 - DUP5 - MULFP254 - DUP13 - DUP7 - MULFP254 - ADDFP254 - DUP12 - DUP8 - MULFP254 - ADDFP254 - DUP11 - DUP9 - MULFP254 - ADDFP254 - // make C0D0_ = c0d0_ + c0_d0 - DUP10 - DUP4 - MULFP254 - DUP10 - DUP6 - MULFP254 - ADDFP254 - // make CD12 = c1d2 + c2d1 - c1_d2_ - c2_d1_ - DUP13 - DUP10 - MULFP254 - DUP4 - DUP9 - MULFP254 - ADDFP254 - DUP15 - DUP8 - MULFP254 - DUP14 - DUP11 - MULFP254 - ADDFP254 - SUBFP254 - // make C0D0 = c0d0 - c0_d0_ - DUP12 - DUP7 - MULFP254 - DUP12 - DUP7 - MULFP254 - SUBFP254 - // stack: C0D0 , CD12 , C0D0_, CD12_ - DUP4 - DUP3 - // stack: CD12 , CD12_ , C0D0 , CD12 , C0D0_, CD12_ - PUSH 9 - MULFP254 - SUBFP254 - ADDFP254 - // stack: e0 = 9CD12 - CD12_ + C0D0 , CD12 , C0D0_, CD12_ - SWAP12 - SWAP3 - // stack: CD12_ , CD12 , C0D0_ - PUSH 9 - MULFP254 - ADDFP254 - ADDFP254 - // stack: e0_ = 9CD12_ + CD12 + C0D0_ - SWAP11 - - // e1, e1_ - // make C2D2_ = c2d2_ + c2_d2 - DUP14 - DUP10 - MULFP254 - DUP4 - DUP10 - MULFP254 - ADDFP254 - // make C2D2 = c2d2 - c2_d2_ - DUP4 - DUP11 - MULFP254 - DUP16 - DUP11 - MULFP254 - SUBFP254 - // make CD01 = c0d1 + c1d0 - (c0_d1_ + c1_d0_) - DUP4 - DUP10 - MULFP254 - DUP16 - DUP9 - MULFP254 - ADDFP254 - DUP13 - DUP10 - MULFP254 - DUP5 - DUP9 - MULFP254 - ADDFP254 - SUBFP254 - // stack: CD01, C2D2, C2D2_ - DUP3 - DUP3 - // stack: C2D2 , C2D2_ , CD01, C2D2, C2D2_ - PUSH 9 - MULFP254 - SUBFP254 - ADDFP254 - // stack: e1 = 9C2D2 - C2D2_ + CD01, C2D2, C2D2_ - SWAP15 - SWAP2 - // stack: C2D2_ , C2D2 - PUSH 9 - MULFP254 - ADDFP254 - // stack: 9C2D2_ + C2D2 - // make CD01_ = c0d1_ + c0_d1 + c1d0_ + c1_d0 - DUP12 - DUP10 - MULFP254 - DUP5 - DUP10 - MULFP254 - ADDFP254 - DUP4 - DUP9 - MULFP254 - ADDFP254 - DUP3 - DUP8 - MULFP254 - ADDFP254 - // stack: CD01_ , 9C2D2_ + C2D2 - ADDFP254 - // stack: e1_ = CD01_ + 9C2D2_ + C2D2 - SWAP15 - - // e2_ - // stack: d2, d1_, d1, d0_, d2_, c0, c0_, c1, c1_, c2, c2_, d0 - SWAP7 - MULFP254 - // stack: c1d1_, d1, d0_, d2_, c0, c0_, d2, c1_, c2, c2_, d0 - SWAP7 - MULFP254 - // stack: c1_d1, d0_, d2_, c0, c0_, d2, c1d1_, c2, c2_, d0 - SWAP7 - MULFP254 - // stack: c2d0_, d2_, c0, c0_, d2, c1d1_, c1_d1 , c2_, d0 - SWAP2 - MULFP254 - // stack: c0d2_ , c2d0_, c0_, d2, c1d1_, c1_d1 , c2_, d0 - ADDFP254 - // stack: c0d2_ + c2d0_, c0_, d2, c1d1_, c1_d1 , c2_, d0 - SWAP2 - MULFP254 - // stack: c0_d2 , c0d2_ + c2d0_ , c1d1_ , c1_d1 , c2_, d0 - ADDFP254 - ADDFP254 - ADDFP254 - // stack: c0_d2 + c0d2_ + c2d0_ + c1d1_ + c1_d1 , c2_, d0 - SWAP2 - MULFP254 - ADDFP254 - // stack: e2_ = c2_d0 + c0_d2 + c0d2_ + c2d0_ + c1d1_ + c1_d1 - SWAP6 - - // stack: retdest, e0, e0_, e1, e1_, e2, e2_ - JUMP - - -//////////////////////// -///// FP6 SQUARING ///// -//////////////////////// - -/// inputs: -/// C = C0 + C1t + C2t^2 -/// = (c0 + c0_i) + (c1 + c1_i)t + (c2 + c2_i)t^2 -/// -/// output: -/// E = E0 + E1t + E2t^2 = C^2 -/// = (e0 + e0_i) + (e1 + e1_i)t + (e2 + e2_i)t^2 -/// -/// initial stack: c0, c0_, c1, c1_, c2, c2_, retdest -/// final stack: e0, e0_, e1, e1_, e2, e2_ - -/// computations: -/// -/// E0 = C0C0 + i9(2C1C2) = (c0+c0_i)^2 + i9(2(c1+c1_i)(c2+c2_i)) -/// = (c0^2 - c0_^2) + (2c0c0_)i + i9[2(c1c2 - c1_c2_) + 2(c1_c2 + c1c2_)i] -/// -/// E1 = 2*C0C1 + i9(C2C2) = 2(c0+c0_i)(c1+c1_i) + i9((c2+c2_i)(c2+c2_i)) -/// = 2(c0c1 - c0_c1_) + 2(c0c1_ + c0_c1)i + i9[(c2^2 - c2_^2) + (2c2c2_)i] -/// -/// E2 = 2*C0C2 + C1C1 -/// = 2(c0c2 - c0_c2_) + 2(c0_c2 + c2c0_)i + (c1^2 - c1_^2) + (2c1c1_)i -/// -/// e0 = (c0^2 - c0_^2) + x0 -/// e0_ = 2c0c0_ + x0_ -/// where x0_, x0 = %i9 c1c2 - c1_c2_, c1_c2 + c1c2_ -/// -/// e1 = 2(c0c1 - c0_c1_) + x1 -/// e1_ = 2(c0c1_ + c0_c1) + x1_ -/// where x1_, x1 = %i9 c2^2 - c2_^2, 2c2c2_ -/// -/// e2 = 2(c0c2 - c0_c2_) + (c1^2 - c1_^2) -/// e2_ = 2(c0_c2 + c2c0_) + 2c1c1_ - -// cost: 101 -global square_fp254_6: - /// e0 = (c0^2 - c0_^2) + x0 - /// e0_ = 2c0c0_ + x0_ - /// where x0_, x0 = %i9 2(c1c2 - c1_c2_), 2(c1_c2 + c1c2_) - DUP6 - DUP4 - MULFP254 - DUP6 - DUP6 - MULFP254 - ADDFP254 - PUSH 2 - MULFP254 - DUP7 - DUP6 - MULFP254 - DUP7 - DUP6 - MULFP254 - SUBFP254 - PUSH 2 - MULFP254 - %i9 - // stack: x0_, x0 - DUP3 - DUP5 - MULFP254 - PUSH 2 - MULFP254 - // stack: 2c0c0_, x0_, x0 - ADDFP254 - // stack: e0_, x0 - SWAP4 - SWAP1 - // stack: x0 - DUP4 - DUP1 - MULFP254 - DUP4 - DUP1 - MULFP254 - SUBFP254 - // stack: c0^2 - c0_^2, x0 - ADDFP254 - // stack: e0 - SWAP3 - - /// e1 = 2(c0c1 - c0_c1_) + x1 - /// e1_ = 2(c0c1_ + c0_c1 ) + x1_ - /// where x1_, x1 = %i9 c2^2 - c2_^2, 2c2c2_ - DUP7 - DUP9 - MULFP254 - PUSH 2 - MULFP254 - DUP9 - DUP1 - MULFP254 - DUP9 - DUP1 - MULFP254 - SUBFP254 - %i9 - // stack: x1_, x1 - DUP4 - DUP4 - MULFP254 - DUP9 - DUP7 - MULFP254 - ADDFP254 - PUSH 2 - MULFP254 - // stack: 2(c0c1_ + c0_c1), x1_, x1 - ADDFP254 - // stack: e1_, x1 - SWAP8 - SWAP1 - // stack: x1 - DUP8 - DUP4 - MULFP254 - DUP5 - DUP7 - MULFP254 - SUBFP254 - PUSH 2 - MULFP254 - // stack: 2(c0c1 - c0_c1_), x1 - ADDFP254 - SWAP7 - - /// e2 = 2(c0c2 - c0_c2_) + (c1^2 - c1_^2) - /// e2_ = 2(c0_c2 + c2c0_ + c1c1_) - DUP1 - DUP1 - MULFP254 - DUP5 - DUP1 - MULFP254 - SUBFP254 - DUP11 - DUP5 - MULFP254 - DUP4 - DUP8 - MULFP254 - SUBFP254 - PUSH 2 - MULFP254 - ADDFP254 - // stack: e2 - SWAP10 - // stack: c2_, c1_, c2, c0_, c1, c0 - SWAP4 - MULFP254 - // stack: c1c1_, c2, c0_, c2_, c0 - SWAP2 - MULFP254 - // stack: c0_c2 , c1c1_, c2_, c0 - ADDFP254 - // stack: c0_c2 + c1c1_, c2_, c0 - SWAP2 - MULFP254 - // stack: c0c2_ , c0_c2 + c1c1_ - ADDFP254 - // stack: c0c2_ + c0_c2 + c1c1_ - PUSH 2 - MULFP254 - // stack: e2_ - SWAP6 - - // stack: retdest, e0, e0_, e1, e1_, e2, e2_ - JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/frobenius.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/frobenius.asm deleted file mode 100644 index ee1e467917..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/frobenius.asm +++ /dev/null @@ -1,272 +0,0 @@ -// frob_fp12 tests - -global test_frob_fp254_12_1: - // stack: ptr - %frob_fp254_12_1 - // stack: ptr - %jump(0xdeadbeef) - -global test_frob_fp254_12_2: - // stack: ptr - DUP1 - // stack: ptr, ptr - %frob_fp254_12_2_ - // stack: ptr - %jump(0xdeadbeef) - -global test_frob_fp254_12_3: - // stack: ptr - %frob_fp254_12_3 - // stack: ptr - %jump(0xdeadbeef) - -global test_frob_fp254_12_6: - // stack: ptr - %frob_fp254_12_6 - // stack: ptr - %jump(0xdeadbeef) - - -/// def frob_fp254_12_n(f, f'): -/// g = frob_fp254_6(n, f ) -/// g' = FROB_z[n] * frob_fp254_6(n, f') -/// return g, g' - -%macro frob_fp254_12_1 - // stack: ptr - DUP1 - // stack: ptr, ptr - %load_fp254_6 - // stack: f, ptr - %frob_fp254_6_1 - // stack: g, ptr - DUP7 - // stack: ptr, g, ptr - %store_fp254_6 - // stack: ptr - DUP1 %add_const(6) - // stack: ptr', ptr - %load_fp254_6 - // stack: f', ptr - %frobz_1 - // stack: g', ptr - DUP7 %add_const(6) - // stack: ptr', g', ptr - %store_fp254_6 - // stack: ptr -%endmacro - -// Note: this is the only one with distinct input and output pointers -%macro frob_fp254_12_2_ - // stack: ptr , out - DUP1 - // stack: ptr, ptr , out - %load_fp254_6 - // stack: f, ptr , out - %frob_fp254_6_2 - // stack: g, ptr , out - DUP8 - // stack: out, g, ptr , out - %store_fp254_6 - // stack: ptr , out - %add_const(6) - // stack: ptr', out - %load_fp254_6 - // stack: f', out - %frobz_2 - // stack: g', out - DUP7 %add_const(6) - // stack: out', g', out - %store_fp254_6 - // stack: out -%endmacro - -%macro frob_fp254_12_3 - // stack: ptr - DUP1 - // stack: ptr, ptr - %load_fp254_6 - // stack: f, ptr - %frob_fp254_6_3 - // stack: g, ptr - DUP7 - // stack: ptr, g, ptr - %store_fp254_6 - // stack: ptr - DUP1 %add_const(6) - // stack: ptr', ptr - %load_fp254_6 - // stack: f', ptr - %frobz_3 - // stack: g', ptr - DUP7 %add_const(6) - // stack: ptr', g', ptr - %store_fp254_6 - // stack: ptr -%endmacro - -%macro frob_fp254_12_6 - // stack: ptr - DUP1 %add_const(6) - // stack: ptr', ptr - %load_fp254_6 - // stack: f', ptr - %frobz_6 - // stack: g', ptr - DUP7 %add_const(6) - // stack: ptr', g', ptr - %store_fp254_6 - // stack: ptr -%endmacro - -// frob_fp12 tests - -global test_frob_fp254_6_1: - // stack: ptr - %frob_fp254_6_1 - // stack: ptr - %jump(0xdeadbeef) - -global test_frob_fp254_6_2: - // stack: ptr - %frob_fp254_6_2 - // stack: ptr - %jump(0xdeadbeef) - -global test_frob_fp254_6_3: - // stack: ptr - %frob_fp254_6_3 - // stack: ptr - %jump(0xdeadbeef) - - -/// let Z` denote the complex conjugate of Z - -/// def frob_fp254_6_n(C0, C1, C2): -/// if n%2: -/// D0, D1, D2 = C0`, FROB_T1[n] * C1`, FROB_T2[n] * C2` -/// else: -/// D0, D1, D2 = C0 , FROB_T1[n] * C1 , FROB_T2[n] * C2 -/// return D0, D1, D2 - -%macro frob_fp254_6_1 - // stack: C0 , C1 , C2 - %conj_fp254_2 - // stack: D0 , C1 , C2 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: C2 , C1 , D0 - %conj_fp254_2 - // stack: C2`, C1 , D0 - %frobt2_1 - // stack: D2 , C1 , D0 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: D0 , C1 , D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: C1 , D0 , D2 - %conj_fp254_2 - // stack: C1`, D0 , D2 - %frobt1_1 - // stack: D1 , D0 , D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: D0 , D1 , D2 -%endmacro - -%macro frob_fp254_6_2 - // stack: C0, C1, C2 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: C2, C1, C0 - %frobt2_2 - // stack: D2, C1, C0 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: C0, C1, D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: C1, C0, D2 - %frobt1_2 - // stack: D1, C0, D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: D0, D1, D2 -%endmacro - -%macro frob_fp254_6_3 - // stack: C0 , C1 , C2 - %conj_fp254_2 - // stack: D0 , C1 , C2 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: C2 , C1 , D0 - %conj_fp254_2 - // stack: C2`, C1 , D0 - %frobt2_3 - // stack: D2 , C1 , D0 - %stack (x: 2, a: 2, y:2) -> (y, a, x) - // stack: D0 , C1 , D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: C1 , D0 , D2 - %conj_fp254_2 - // stack: C1`, D0 , D2 - %frobt1_3 - // stack: D1 , D0 , D2 - %stack (x: 2, y: 2) -> (y, x) - // stack: D0 , D1 , D2 -%endmacro - - -%macro frobz_1 - %frob_fp254_6_1 - PUSH 0x246996f3b4fae7e6a6327cfe12150b8e747992778eeec7e5ca5cf05f80f362ac - PUSH 0x1284b71c2865a7dfe8b99fdd76e68b605c521e08292f2176d60b35dadcc9e470 - %scale_fp254_6 -%endmacro - -%macro frobz_2 - %frob_fp254_6_2 - PUSH 0x30644e72e131a0295e6dd9e7e0acccb0c28f069fbb966e3de4bd44e5607cfd49 - %scale_re_fp254_6 -%endmacro - -%macro frobz_3 - %frob_fp254_6_3 - PUSH 0xabf8b60be77d7306cbeee33576139d7f03a5e397d439ec7694aa2bf4c0c101 - PUSH 0x19dc81cfcc82e4bbefe9608cd0acaa90894cb38dbe55d24ae86f7d391ed4a67f - %scale_fp254_6 -%endmacro - -%macro frobz_6 - PUSH 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46 - %scale_re_fp254_6 -%endmacro - - -%macro frobt1_1 - PUSH 0x16c9e55061ebae204ba4cc8bd75a079432ae2a1d0b7c9dce1665d51c640fcba2 - PUSH 0x2fb347984f7911f74c0bec3cf559b143b78cc310c2c3330c99e39557176f553d - %mul_fp254_2 -%endmacro - -%macro frobt2_1 - PUSH 0x2c145edbe7fd8aee9f3a80b03b0b1c923685d2ea1bdec763c13b4711cd2b8126 - PUSH 0x5b54f5e64eea80180f3c0b75a181e84d33365f7be94ec72848a1f55921ea762 - %mul_fp254_2 -%endmacro - -%macro frobt1_2 - PUSH 0x30644e72e131a0295e6dd9e7e0acccb0c28f069fbb966e3de4bd44e5607cfd48 - %scale_fp254_2 -%endmacro - -%macro frobt2_2 - PUSH 0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe - %scale_fp254_2 -%endmacro - - -%macro frobt1_3 - PUSH 0x4f1de41b3d1766fa9f30e6dec26094f0fdf31bf98ff2631380cab2baaa586de - PUSH 0x856e078b755ef0abaff1c77959f25ac805ffd3d5d6942d37b746ee87bdcfb6d - %mul_fp254_2 -%endmacro - -%macro frobt2_3 - PUSH 0x23d5e999e1910a12feb0f6ef0cd21d04a44a9e08737f96e55fe3ed9d730c239f - PUSH 0xbc58c6611c08dab19bee0f7b5b2444ee633094575b06bcb0e1a92bc3ccbf066 - %mul_fp254_2 -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm deleted file mode 100644 index 7c7729057c..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm +++ /dev/null @@ -1,66 +0,0 @@ -// Returns reverse order division y/x, modulo N -%macro divr_fp254 - // stack: x , y - %inv_fp254 - // stack: x^-1, y - MULFP254 -%endmacro - -// Non-deterministically provide the inverse x^-1 of x modulo N. -// If x === 0 mod N, this function panics. -// Although the official prover provides the unique inverse (inp, out, 60, check_inv_fp254_12) - // stack: inp, out, 60, check_inv_fp254_12, retdest - %jump(mul_fp254_12) -check_inv_fp254_12: - // stack: retdest - PUSH 60 - %load_fp254_12 - // stack: unit?, retdest - %assert_eq_unit_fp254_12 - // stack: retdest - PUSH 60 - %create_bn254_pairing_address - PUSH 0 - // stack: 0, addr, retdest - MSTORE_GENERAL - // stack: retdest - JUMP - -%macro prover_inv_fp254_12 - PROVER_INPUT(ffe::bn254_base::component_11) - PROVER_INPUT(ffe::bn254_base::component_10) - PROVER_INPUT(ffe::bn254_base::component_9) - PROVER_INPUT(ffe::bn254_base::component_8) - PROVER_INPUT(ffe::bn254_base::component_7) - PROVER_INPUT(ffe::bn254_base::component_6) - PROVER_INPUT(ffe::bn254_base::component_5) - PROVER_INPUT(ffe::bn254_base::component_4) - PROVER_INPUT(ffe::bn254_base::component_3) - PROVER_INPUT(ffe::bn254_base::component_2) - PROVER_INPUT(ffe::bn254_base::component_1) - PROVER_INPUT(ffe::bn254_base::component_0) -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm deleted file mode 100644 index 897404dbf2..0000000000 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/util.asm +++ /dev/null @@ -1,1100 +0,0 @@ -// Load a single value from bn254 pairings memory. -%macro mload_bn254_pairing - // stack: offset - %mload_current(@SEGMENT_BN_PAIRING) - // stack: value -%endmacro - -%macro mload_bn254_pairing(offset) - // stack: - PUSH $offset - // stack: offset - %mload_current(@SEGMENT_BN_PAIRING) - // stack: value -%endmacro - -// Store a single value to bn254 pairings memory. -%macro mstore_bn254_pairing - // stack: offset, value - %mstore_current(@SEGMENT_BN_PAIRING) - // stack: -%endmacro - -// Build an address on the current context within SEGMENT_BN_PAIRING. -%macro create_bn254_pairing_address - // stack: offset - PUSH @SEGMENT_BN_PAIRING - GET_CONTEXT - %build_address - // stack: addr -%endmacro - -// Store a single value to bn254 pairings memory. -%macro mstore_bn254_pairing_value(value) - // stack: offset - %create_bn254_pairing_address - PUSH $value - MSTORE_GENERAL - // stack: -%endmacro - -%macro mstore_bn254_pairing(offset) - // stack: value - PUSH $offset - // stack: offset, value - %mstore_current(@SEGMENT_BN_PAIRING) - // stack: -%endmacro - -// fp254_2 macros - -%macro load_fp254_2 - // stack: ptr - %create_bn254_pairing_address - DUP1 - %add_const(1) - // stack: addr1, addr - MLOAD_GENERAL - // stack: x1, addr - SWAP1 - // stack: addr0, x1 - MLOAD_GENERAL - // stack: x0, x1 -%endmacro - -/// complex conjugate -%macro conj_fp254_2 - // stack: a, b - SWAP1 - PUSH 0 - SUBFP254 - SWAP1 - // stack: a, -b -%endmacro - -%macro scale_fp254_2 - // stack: c, x, y - SWAP2 - // stack: y, x, c - DUP3 - // stack: c, y, x, c - MULFP254 - // stack: cy, x, c - SWAP2 - // stack: c, x, cy - MULFP254 - // stack: cx, cy -%endmacro - -%macro eq_fp254_2 - // stack: x, x_, y, y_ - SWAP3 - // stack: y_, x_, y, x - EQ - // stack: y_==x_, y, x - SWAP2 - // stack: x, y, y_==x_ - EQ - // stack: x==y, y_==x_ - AND -%endmacro - -%macro add_fp254_2 - // stack: x, x_, y, y_ - SWAP3 - // stack: y_, x_, y, x - ADDFP254 - // stack: z_, y, x - SWAP2 - // stack: x, y, z_ - ADDFP254 - // stack: z, z_ -%endmacro - -/// Given z = x + iy: Fp254_2, return complex conjugate z': Fp254_2 -/// where input is represented z.re, z.im and output as z'.im, z'.re -/// cost: 9; note this returns y, x for the output x + yi -%macro i9 - // stack: a , b - DUP2 - // stack: b, a , b - DUP2 - // stack: a , b, a , b - PUSH 9 - MULFP254 - // stack: 9a , b, a , b - SUBFP254 - // stack: 9a - b, a , b - SWAP2 - // stack: b , a, 9a - b - PUSH 9 - MULFP254 - // stack 9b , a, 9a - b - ADDFP254 - // stack: 9b + a, 9a - b -%endmacro - -%macro mul_fp254_2 - // stack: a, b, c, d - DUP4 - DUP3 - MULFP254 - // stack: bd, a, b, c, d - DUP4 - DUP3 - MULFP254 - // stack: ac , bd, a, b, c, d - SUBFP254 - // stack: ac - bd, a, b, c, d - SWAP4 - // stack: d, a, b, c, ac - bd - MULFP254 - // stack: ad, b, c, ac - bd - SWAP2 - // stack: c, b, ad, ac - bd - MULFP254 - // stack: bc , ad, ac - bd - ADDFP254 - // stack: bc + ad, ac - bd - SWAP1 - // stack: ac - bd, bc + ad -%endmacro - -// load twisted curve - -%macro load_fp254_4 - // stack: ptr - %create_bn254_pairing_address - DUP1 - %add_const(2) - // stack: addr2, addr - MLOAD_GENERAL - // stack: x2, addr - DUP2 - %add_const(1) - // stack: addr1, x2, addr - MLOAD_GENERAL - // stack: x1, x2, addr - DUP3 - %add_const(3) - // stack: addr3, x1, x2, addr - MLOAD_GENERAL - // stack: x3, x1, x2, addr - SWAP3 - // stack: addr0, x1, x2, x3 - MLOAD_GENERAL - // stack: x0, x1, x2, x3 -%endmacro - -// fp254_6 macros - -%macro load_fp254_6 - // stack: ptr - %create_bn254_pairing_address - DUP1 - %add_const(4) - // stack: addr4, addr - MLOAD_GENERAL - // stack: x4, addr - DUP2 - %add_const(3) - // stack: addr3, x4, addr - MLOAD_GENERAL - // stack: x3, x4, addr - DUP3 - %add_const(2) - // stack: addr2, x3, x4, addr - MLOAD_GENERAL - // stack: x2, x3, x4, addr - DUP4 - %add_const(1) - // stack: addr1, x2, x3, x4, addr - MLOAD_GENERAL - // stack: x1, x2, x3, x4, addr - DUP5 - %add_const(5) - // stack: addr5, x1, x2, x3, x4, addr - MLOAD_GENERAL - // stack: x5, x1, x2, x3, x4, addr - SWAP5 - // stack: addr0, x1, x2, x3, x4, x5 - MLOAD_GENERAL - // stack: x0, x1, x2, x3, x4, x5 -%endmacro - -%macro load_fp254_6(ptr) - // stack: - PUSH $ptr - %load_fp254_6 - // stack: x0, x1, x2, x3, x4, x5 -%endmacro - -%macro store_fp254_6 - // stack: ptr, x0, x1, x2, x3, x4 , x5 - %create_bn254_pairing_address - SWAP5 - // stack: x4, x0, x1, x2, x3, addr, x5 - DUP6 - %add_const(4) - // stack: addr4, x4, x0, x1, x2, x3, addr, x5 - %swap_mstore - // stack: x0, x1, x2, x3, addr, x5 - DUP5 - // stack: addr0, x0, x1, x2, x3, addr, x5 - %swap_mstore - // stack: x1, x2, x3, addr, x5 - DUP4 - %add_const(1) - // stack: addr1, x1, x2, x3, addr, x5 - %swap_mstore - // stack: x2, x3, addr, x5 - DUP3 - %add_const(2) - // stack: addr2, x2, x3, addr, x5 - %swap_mstore - // stack: x3, addr, x5 - DUP2 - %add_const(3) - // stack: addr3, x3, addr, x5 - %swap_mstore - // stack: addr, x5 - %add_const(5) - // stack: addr5, x5 - %swap_mstore - // stack: -%endmacro - -%macro store_fp254_6_double - // stack: ptr, x0, x1, x2, x3, x4, x5 - %create_bn254_pairing_address - SWAP6 - // stack: x5, x0, x1, x2, x3, x4, addr - PUSH 2 - MULFP254 - // stack: 2*x5, x0, x1, x2, x3, x4, addr - DUP7 - %add_const(5) - // stack: addr5, 2*x5, x0, x1, x2, x3, x4, addr - %swap_mstore - // stack: x0, x1, x2, x3, x4, addr - PUSH 2 - MULFP254 - // stack: 2*x0, x1, x2, x3, x4, addr - DUP6 - // stack: addr0, 2*x0, x1, x2, x3, x4, addr - %swap_mstore - // stack: x1, x2, x3, x4, addr - PUSH 2 - MULFP254 - // stack: 2*x1, x2, x3, x4, addr - DUP5 - %add_const(1) - // stack: addr1, 2*x1, x2, x3, x4, addr - %swap_mstore - // stack: x2, x3, x4, addr - PUSH 2 - MULFP254 - // stack: 2*x2, x3, x4, addr - DUP4 - %add_const(2) - // stack: addr2, 2*x2, x3, x4, addr - %swap_mstore - // stack: x3, x4, addr - PUSH 2 - MULFP254 - // stack: 2*x3, x4, addr - DUP3 - %add_const(3) - // stack: addr3, 2*x3, x4, addr - %swap_mstore - // stack: x4, addr - PUSH 2 - MULFP254 - // stack: 2*x4, addr - SWAP1 - // stack: addr, 2*x4 - %add_const(4) - // stack: addr4, 2*x4 - %swap_mstore - // stack: -%endmacro - -%macro store_fp254_6(ptr) - // stack: x0, x1, x2, x3, x4, x5 - PUSH $ptr - %store_fp254_6 - // stack: -%endmacro - -%macro store_fp254_6_sh(ptr) - // stack: x0, x1, x2, x3, x4, x5 - PUSH $ptr - %create_bn254_pairing_address - // stack: addr, x0, x1, x2, x3, x4, x5 - %add_const(2) - DUP1 - // stack: addr2, addr2, x0, x1, x2, x3, x4, x5 - SWAP2 MSTORE_GENERAL - // stack: addr2, x1, x2, x3, x4, x5 - %add_const(1) - DUP1 - // stack: addr3, addr3, x1, x2, x3, x4, x5 - SWAP2 MSTORE_GENERAL - // stack: addr3, x2, x3, x4, x5 - %add_const(1) - DUP1 - // stack: addr4, addr4, x2, x3, x4, x5 - SWAP2 MSTORE_GENERAL - // stack: addr4, x3, x4, x5 - %add_const(1) - // stack: addr5, x3, x4, x5 - %swap_mstore - // stack: x4, x5 - %i9 - // stack: y5, y4 - PUSH $ptr - %create_bn254_pairing_address - DUP1 - %add_const(1) - // stack: addr1, addr, y5, y4 - SWAP3 - MSTORE_GENERAL - // stack: y5, addr1 - MSTORE_GENERAL - // stack: -%endmacro - -// cost: 6 -%macro dup_fp254_6_0 - // stack: f: 6 - DUP6 - DUP6 - DUP6 - DUP6 - DUP6 - DUP6 - // stack: f: 6, f: 6 -%endmacro - -// cost: 6 -%macro dup_fp254_6_2 - // stack: X: 2, f: 6 - DUP8 - DUP8 - DUP8 - DUP8 - DUP8 - DUP8 - // stack: f: 6, X: 2, f: 6 -%endmacro - -// cost: 6 -%macro dup_fp254_6_6 - // stack: X: 6, f: 6 - DUP12 - DUP12 - DUP12 - DUP12 - DUP12 - DUP12 - // stack: f: 6, X: 6, f: 6 -%endmacro - -// cost: 6 -%macro dup_fp254_6_7 - // stack: X: 7, f: 6 - DUP13 - DUP13 - DUP13 - DUP13 - DUP13 - DUP13 - // stack: f: 6, X: 7, f: 6 -%endmacro - -// cost: 6 -%macro dup_fp254_6_8 - // stack: X: 8, f: 6 - DUP14 - DUP14 - DUP14 - DUP14 - DUP14 - DUP14 - // stack: f: 6, X: 8, f: 6 -%endmacro - -/// multiply (a + bt + ct^2) by t: -/// t(a + bt + ct^2) = at + bt^2 + ct^3 = (9+i)c + at + bt^2 -%macro sh_fp254_6 - // stack: a, b, c - %stack (a: 2, b: 2, c: 2) -> (c, a, b) - // stack: c, a, b - %i9 - SWAP1 - // stack: (9+i)c, a, b -%endmacro - -// cost: 16 -%macro add_fp254_6 - // stack: f0, f1, f2, f3, f4, f5, g0, g1, g2, g3, g4, g5 - SWAP7 - ADDFP254 - SWAP6 - // stack: f0, f2, f3, f4, f5, g0, h1, g2, g3, g4, g5 - SWAP7 - ADDFP254 - SWAP6 - // stack: f0, f3, f4, f5, g0, h1, h2, g3, g4, g5 - SWAP7 - ADDFP254 - SWAP6 - // stack: f0, f4, f5, g0, h1, h2, h3, g4, g5 - SWAP7 - ADDFP254 - SWAP6 - // stack: f0, f5, g0, h1, h2, h3, h4, g5 - SWAP7 - ADDFP254 - SWAP6 - // stack: f0, g0, h1, h2, h3, h4, h5 - ADDFP254 - // stack: h0, h1, h2, h3, h4, h5 -%endmacro - -// cost: 18 -// add two fp254_6 elements with a to-be-popped stack term separating them -// (f: 6, X, g: 6) -> (f + g) -%macro add_fp254_6_hole - // stack: f0, f1, f2, f3, f4, f5, X, g0, g1, g2, g3, g4, g5 - SWAP8 - ADDFP254 - SWAP7 - // stack: f0, f2, f3, f4, f5, X, g0, h1, g2, g3, g4, g5 - SWAP8 - ADDFP254 - SWAP7 - // stack: f0, f3, f4, f5, X, g0, h1, h2, g3, g4, g5 - SWAP8 - ADDFP254 - SWAP7 - // stack: f0, f4, f5, X, g0, h1, h2, h3, g4, g5 - SWAP8 - ADDFP254 - SWAP7 - // stack: f0, f5, X, g0, h1, h2, h3, h4, g5 - SWAP8 - ADDFP254 - SWAP7 - // stack: f0, X, g0, h1, h2, h3, h4, h5 - SWAP1 - POP - ADDFP254 - // stack: h0, h1, h2, h3, h4, h5 -%endmacro - -// *reversed argument subtraction* cost: 17 -%macro subr_fp254_6 - // stack: f0, f1, f2, f3, f4, f5, g0, g1, g2, g3, g4, g5 - SWAP7 - SUBFP254 - SWAP6 - // stack: f0, f2, f3, f4, f5, g0, h1, g2, g3, g4, g5 - SWAP7 - SUBFP254 - SWAP6 - // stack: f0, f3, f4, f5, g0, h1, h2, g3, g4, g5 - SWAP7 - SUBFP254 - SWAP6 - // stack: f0, f4, f5, g0, h1, h2, h3, g4, g5 - SWAP7 - SUBFP254 - SWAP6 - // stack: f0, f5, g0, h1, h2, h3, h4, g5 - SWAP7 - SUBFP254 - SWAP6 - // stack: f0, g0, h1, h2, h3, h4, h5 - SWAP1 - SUBFP254 - // stack: h0, h1, h2, h3, h4, h5 -%endmacro - -// cost: 21 -%macro scale_re_fp254_6 - // stack: c , f0, f1, f2, f3, f4, f5 - SWAP6 - DUP7 - MULFP254 - SWAP6 - // stack: c , f0, f1, f2, f3, f4, c * f5 - SWAP5 - DUP6 - MULFP254 - SWAP5 - // stack: c , f0, f1, f2, f3, c * f4, c * f5 - SWAP4 - DUP5 - MULFP254 - SWAP4 - // stack: c , f0, f1, f2, c * f3, c * f4, c * f5 - SWAP3 - DUP4 - MULFP254 - SWAP3 - // stack: c , f0, f1, c * f2, c * f3, c *f 4, c * f5 - SWAP2 - DUP3 - MULFP254 - SWAP2 - // stack: c , f0, c * f1, c * f2, c * f3, c * f4, c * f5 - MULFP254 - // stack: c * f0, c * f1, c * f2, c * f3, c * f4, c * f5 -%endmacro - -/// cost: -/// -/// G0 + G1t + G2t^2 = (a+bi) * (F0 + F1t + F2t^2) -/// = (a+bi)F0 + (a+bi)F1t + (a+bi)F2t^2 -/// -/// G0 = (a+bi)(f0+f0_i) = (af0 - bf0_) + (bf0 + af0_)i -/// G1 = (a+bi)(f1+f1_i) = (af1 - bf1_) + (bf1 + af1_)i -/// G2 = (a+bi)(f2+f2_i) = (af2 - bf2_) + (bf2 + af2_)i - -%macro scale_fp254_6 - // stack: a, b, f0, f0_, f1, f1_, f2, f2_ - DUP2 - DUP5 - MULFP254 - // stack: bf0_, a, b, f0, f0_, f1, f1_, f2, f2_ - DUP2 - DUP5 - MULFP254 - // stack: af0, bf0_, a, b, f0, f0_, f1, f1_, f2, f2_ - SUBFP254 - // stack: g0, a, b, f0, f0_, f1, f1_, f2, f2_ - SWAP3 - // stack: f0, a, b, g0, f0_, f1, f1_, f2, f2_ - DUP3 - MULFP254 - // stack: bf0, a, b, g0, f0_, f1, f1_, f2, f2_ - SWAP1 - SWAP4 - // stack: f0_, bf0, b, g0, a, f1, f1_, f2, f2_ - DUP5 - MULFP254 - // stack: af0_, bf0, b, g0, a, f1, f1_, f2, f2_ - ADDFP254 - // stack: g0_, b, g0, a, f1, f1_, f2, f2_ - SWAP3 - // stack: a, b, g0, g0_, f1, f1_, f2, f2_ - DUP2 - DUP7 - MULFP254 - // stack: bf1_, a, b, g0, g0_, f1, f1_, f2, f2_ - DUP2 - DUP7 - MULFP254 - // stack: af1, bf1_, a, b, g0, g0_, f1, f1_, f2, f2_ - SUBFP254 - // stack: g1, a, b, g0, g0_, f1, f1_, f2, f2_ - SWAP5 - // stack: f1, a, b, g0, g0_, g1, f1_, f2, f2_ - DUP3 - MULFP254 - // stack: bf1, a, b, g0, g0_, g1, f1_, f2, f2_ - SWAP1 - SWAP6 - // stack: f1_, bf1, b, g0, g0_, g1, a, f2, f2_ - DUP7 - MULFP254 - // stack: af1_, bf1, b, g0, g0_, g1, a, f2, f2_ - ADDFP254 - // stack: g1_, b, g0, g0_, g1, a, f2, f2_ - SWAP5 - // stack: a, b, g0, g0_, g1, g1_, f2, f2_ - DUP2 - DUP9 - MULFP254 - // stack: bf2_, a, b, g0, g0_, g1, g1_, f2, f2_ - DUP2 - DUP9 - MULFP254 - // stack: af2, bf2_, a, b, g0, g0_, g1, g1_, f2, f2_ - SUBFP254 - // stack: g2, a, b, g0, g0_, g1, g1_, f2, f2_ - SWAP7 - // stack: f2, a, b, g0, g0_, g1, g1_, g2, f2_ - SWAP8 - // stack: f2_, a, b, g0, g0_, g1, g1_, g2, f2 - MULFP254 - // stack: af2_, b, g0, g0_, g1, g1_, g2, f2 - SWAP7 - // stack: f2, b, g0, g0_, g1, g1_, g2, af2_ - MULFP254 - // stack: bf2, g0, g0_, g1, g1_, g2, af2_ - SWAP1 - SWAP6 - // stack: af2_, bf2, g0_, g1, g1_, g2, g0 - ADDFP254 - // stack: g2_, g0_, g1, g1_, g2, g0 - SWAP5 - // stack: g0, g0_, g1, g1_, g2, g2_ -%endmacro - -/// cost: 1 i9 (9) + 16 dups + 15 swaps + 12 muls + 6 adds/subs = 58 -/// -/// G0 + G1t + G2t^2 = (a+bi)t * (F0 + F1t + F2t^2) -/// = (c+di)F2 + (a+bi)F0t + (a+bi)F1t^2 -/// where c+di = (a+bi)(9+i) = (9a-b) + (a+9b)i -/// -/// G0 = (c+di)(f2+f2_i) = (cf2 - df2_) + (df2 + cf2_)i -/// G1 = (a+bi)(f0+f0_i) = (af0 - bf0_) + (bf0 + af0_)i -/// G2 = (a+bi)(f1+f1_i) = (af1 - bf1_) + (bf1 + af1_)i - -%macro scale_fp254_6_sh - // stack: a, b, f0, f0_, f1, f1_, f2, f2_ - DUP6 - DUP3 - MULFP254 - // stack: bf1_, a, b, f0, f0_, f1, f1_, f2, f2_ - DUP6 - DUP3 - MULFP254 - // stack: af1 , bf1_, a, b, f0, f0_, f1, f1_, f2, f2_ - SUBFP254 - // stack: g2, a, b, f0, f0_, f1, f1_, f2, f2_ - SWAP7 - // stack: f2, a, b, f0, f0_, f1, f1_, g2, f2_ - SWAP5 - // stack: f1, a, b, f0, f0_, f2, f1_, g2, f2_ - DUP3 - MULFP254 - // stack: bf1, a, b, f0, f0_, f2, f1_, g2, f2_ - SWAP1 - SWAP6 - // stack: f1_, bf1, b, f0, f0_, f2, a, g2, f2_ - DUP7 - MULFP254 - // stack: af1_, bf1, b, f0, f0_, f2, a, g2, f2_ - ADDFP254 - // stack: g2_, b, f0, f0_, f2, a, g2, f2_ - SWAP7 - // stack: f2_, b, f0, f0_, f2, a, g2, g2_ - DUP4 - DUP3 - MULFP254 - // stack: bf0_, f2_, b, f0, f0_, f2, a, g2, g2_ - DUP4 - DUP8 - MULFP254 - // stack: af0, bf0_, f2_, b, f0, f0_, f2, a, g2, g2_ - SUBFP254 - // stack: g1, f2_, b, f0, f0_, f2, a, g2, g2_ - SWAP5 - // stack: f2, f2_, b, f0, f0_, g1, a, g2, g2_ - SWAP3 - // stack: f0, f2_, b, f2, f0_, g1, a, g2, g2_ - DUP3 - MULFP254 - // stack: bf0, f2_, b, f2, f0_, g1, a, g2, g2_ - SWAP1 - SWAP4 - // stack: f0_, bf0, b, f2, f2_, g1, a, g2, g2_ - DUP7 - MULFP254 - // stack: af0_, bf0, b, f2, f2_, g1, a, g2, g2_ - ADDFP254 - // stack: g1_, b, f2, f2_, g1, a, g2, g2_ - SWAP5 - // stack: a, b, f2, f2_, g1, g1_, g2, g2_ - %i9 - // stack: d, c, f2, f2_, g1, g1_, g2, g2_ - DUP4 - DUP2 - MULFP254 - // stack: df2_, d, c, f2, f2_, g1, g1_, g2, g2_ - DUP4 - DUP4 - MULFP254 - // stack: cf2, df2_, d, c, f2, f2_, g1, g1_, g2, g2_ - SUBFP254 - // stack: g0, d, c, f2, f2_, g1, g1_, g2, g2_ - SWAP3 - // stack: f2, d, c, g0, f2_, g1, g1_, g2, g2_ - MULFP254 - // stack: df2, c, g0, f2_, g1, g1_, g2, g2_ - SWAP3 - MULFP254 - // stack: cf2_, g0, df2, g1, g1_, g2, g2_ - SWAP1 - SWAP2 - // stack: df2, cf2_, g0, g1, g1_, g2, g2_ - ADDFP254 - // stack: g0_, g0, g1, g1_, g2, g2_ - SWAP1 - // stack: g0, g0_, g1, g1_, g2, g2_ -%endmacro - -/// cost: 1 i9 (9) + 16 dups + 17 swaps + 12 muls + 6 adds/subs = 60 -/// -/// G0 + G1t + G2t^2 = (a+bi)t^2 * (F0 + F1t + F2t^2) -/// = (c+di)F1 + (c+di)F2t + (a+bi)F0t^2 -/// where c+di = (a+bi)(9+i) = (9a-b) + (a+9b)i -/// -/// G0 = (c+di)(f1+f1_i) = (cf1 - df1_) + (df1 + cf1_)i -/// G1 = (a+bi)(f2+f2_i) = (cf2 - df2_) + (df2 + cf2_)i -/// G2 = (a+bi)(f0+f0_i) = (af0 - bf0_) + (bf0 + af0_)i - -%macro scale_fp254_6_sh2 - // stack: a, b, f0, f0_, f1, f1_, f2, f2_ - DUP4 - DUP3 - MULFP254 - // stack: bf0_, a, b, f0, f0_, f1, f1_, f2, f2_ - DUP4 - DUP3 - MULFP254 - // stack: af0, bf0_, a, b, f0, f0_, f1, f1_, f2, f2_ - SUBFP254 - // stack: g2, a, b, f0, f0_, f1, f1_, f2, f2_ - SWAP7 - SWAP3 - // stack: f0, a, b, f2, f0_, f1, f1_, g2, f2_ - DUP3 - MULFP254 - // stack: bf0, a, b, f2, f0_, f1, f1_, g2, f2_ - SWAP1 - SWAP4 - // stack: f0_, bf0, b, f2, a, f1, f1_, g2, f2_ - DUP5 - MULFP254 - // stack: af0_, bf0, b, f2, a, f1, f1_, g2, f2_ - ADDFP254 - // stack: g2_, b, f2, a, f1, f1_, g2, f2_ - SWAP7 - SWAP3 - // stack: a, b, f2, f2_, f1, f1_, g2, g2_ - %i9 - // stack: d, c, f2, f2_, f1, f1_, g2, g2_ - DUP4 - DUP2 - MULFP254 - // stack: df2_, d, c, f2, f2_, f1, f1_, g2, g2_ - DUP4 - DUP4 - MULFP254 - // stack: cf2, df2_, d, c, f2, f2_, f1, f1_, g2, g2_ - SUBFP254 - // stack: g1, d, c, f2, f2_, f1, f1_, g2, g2_ - SWAP5 - SWAP3 - // stack: f2, d, c, f1, f2_, g1, f1_, g2, g2_ - DUP2 - MULFP254 - // stack: df2, d, c, f1, f2_, g1, f1_, g2, g2_ - SWAP1 - SWAP4 - // stack: f2_, df2, c, f1, d, g1, f1_, g2, g2_ - DUP3 - MULFP254 - // stack: cf2_, df2, c, f1, d, g1, f1_, g2, g2_ - ADDFP254 - // stack: g1_, c, f1, d, g1, f1_, g2, g2_ - SWAP5 - // stack: f1_, c, f1, d, g1, g1_, g2, g2_ - DUP1 - DUP5 - MULFP254 - // stack: df1_, f1_, c, f1, d, g1, g1_, g2, g2_ - DUP4 - DUP4 - MULFP254 - // stack: cf1, df1_, f1_, c, f1, d, g1, g1_, g2, g2_ - SUBFP254 - // stack: g0, f1_, c, f1, d, g1, g1_, g2, g2_ - SWAP3 - // stack: f1, f1_, c, g0, d, g1, g1_, g2, g2_ - SWAP2 - MULFP254 - // stack: cf1_, f1, g0, d, g1, g1_, g2, g2_ - SWAP3 - MULFP254 - // stack: df1, g0, cf1_, g1, g1_, g2, g2_ - SWAP1 - SWAP2 - // stack: cf1_, df1, g0, g1, g1_, g2, g2_ - ADDFP254 - // stack: g0_, g0, g1, g1_, g2, g2_ - SWAP1 - // stack: g0, g0_, g1, g1_, g2, g2_ -%endmacro - -%macro load_fp254_12 - // stack: ptr - %create_bn254_pairing_address - DUP1 - %add_const(10) - // stack: addr10, addr - MLOAD_GENERAL - // stack: x10, addr - DUP2 - %add_const(9) - // stack: addr09, x10, addr - MLOAD_GENERAL - // stack: x09, x10, addr - DUP3 - %add_const(8) - // stack: addr08, x09, x10, addr - MLOAD_GENERAL - // stack: x08, x09, x10, addr - DUP4 - %add_const(7) - // stack: addr07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x07, x08, x09, x10, addr - DUP5 - %add_const(6) - // stack: addr06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x06, x07, x08, x09, x10, addr - DUP6 - %add_const(5) - // stack: addr05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x05, x06, x07, x08, x09, x10, addr - DUP7 - %add_const(4) - // stack: addr04, x05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x04, x05, x06, x07, x08, x09, x10, addr - DUP8 - %add_const(3) - // stack: addr03, x04, x05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x03, x04, x05, x06, x07, x08, x09, x10, addr - DUP9 - %add_const(2) - // stack: addr02, x03, x04, x05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x02, x03, x04, x05, x06, x07, x08, x09, x10, addr - DUP10 - %add_const(1) - // stack: addr01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr - DUP11 - %add_const(11) - // stack: addr11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr - MLOAD_GENERAL - // stack: x11, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, addr - SWAP11 - // stack: addr00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 - MLOAD_GENERAL - // stack: x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 -%endmacro - -%macro store_fp254_12 - // stack: ptr, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11 - %create_bn254_pairing_address - SWAP11 - // stack: x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - DUP12 - %add_const(10) - // stack: addr10, x10, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - DUP11 - // stack: addr00, x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - DUP10 - %add_const(01) - // stack: addr01, x01, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - DUP9 - %add_const(02) - // stack: addr02, x02, x03, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x03, x04, x05, x06, x07, x08, x09, addr, x11 - DUP8 - %add_const(03) - // stack: addr03, x03, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x04, x05, x06, x07, x08, x09, addr, x11 - DUP7 - %add_const(04) - // stack: addr04, x04, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x05, x06, x07, x08, x09, addr, x11 - DUP6 - %add_const(05) - // stack: addr05, x05, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x06, x07, x08, x09, addr, x11 - DUP5 - %add_const(06) - // stack: addr06, x06, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x07, x08, x09, addr, x11 - DUP4 - %add_const(07) - // stack: addr07, x07, x08, x09, addr, x11 - %swap_mstore - // stack: x08, x09, addr, x11 - DUP3 - %add_const(08) - // stack: addr08, x08, x09, addr, x11 - %swap_mstore - // stack: x09, addr, x11 - DUP2 - %add_const(09) - // stack: addr09, x09, addr, x11 - %swap_mstore - // stack: addr, x11 - %add_const(11) - // stack: addr11, x11 - %swap_mstore - // stack: -%endmacro - -/// moves fp254_12 from src..src+12 to dest..dest+12 -/// these should not overlap. leaves scaled DEST on stack -%macro move_fp254_12 - // stack: src, dest - PUSH @SEGMENT_BN_PAIRING - GET_CONTEXT - %build_address_no_offset - DUP1 - // stack: base_addr, base_addr, src, dest - SWAP3 ADD - // stack: DEST, src, base_addr - SWAP2 ADD - // stack: SRC, DEST - DUP1 - // stack: addr00, SRC, DEST - MLOAD_GENERAL - // stack: x00, SRC, DEST - DUP3 - // stack: addr00', x00, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(1) - // stack: addr01, SRC, DEST - MLOAD_GENERAL - // stack: x01, SRC, DEST - DUP3 - %add_const(1) - // stack: addr01', x01, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(2) - // stack: addr02, SRC, DEST - MLOAD_GENERAL - // stack: x02, SRC, DEST - DUP3 - %add_const(2) - // stack: addr02', x02, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(3) - // stack: addr03, SRC, DEST - MLOAD_GENERAL - // stack: x03, SRC, DEST - DUP3 - %add_const(3) - // stack: addr03', x03, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(4) - // stack: addr04, SRC, DEST - MLOAD_GENERAL - // stack: x04, SRC, DEST - DUP3 - %add_const(4) - // stack: addr04', x04, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(5) - // stack: addr05, SRC, DEST - MLOAD_GENERAL - // stack: x05, SRC, DEST - DUP3 - %add_const(5) - // stack: addr05', x05, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(6) - // stack: addr06, SRC, DEST - MLOAD_GENERAL - // stack: x06, SRC, DEST - DUP3 - %add_const(6) - // stack: addr06', x06, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(7) - // stack: addr07, SRC, DEST - MLOAD_GENERAL - // stack: x07, SRC, DEST - DUP3 - %add_const(7) - // stack: addr07', x07, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(8) - // stack: addr08, SRC, DEST - MLOAD_GENERAL - // stack: x08, SRC, DEST - DUP3 - %add_const(8) - // stack: addr08', x08, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(9) - // stack: addr09, SRC, DEST - MLOAD_GENERAL - // stack: x09, SRC, DEST - DUP3 - %add_const(9) - // stack: addr09', x09, SRC, DEST - %swap_mstore - // stack: SRC, DEST - DUP1 - %add_const(10) - // stack: addr10, SRC, DEST - MLOAD_GENERAL - // stack: x10, SRC, DEST - DUP3 - %add_const(10) - // stack: addr10', x10, SRC, DEST - %swap_mstore - // stack: SRC, DEST - %add_const(11) - // stack: addr11, DEST - MLOAD_GENERAL - // stack: x11, DEST - DUP2 - %add_const(11) - // stack: addr11', x11, DEST - %swap_mstore -%endmacro - -%macro assert_eq_unit_fp254_12 - %assert_eq_const(1) - %rep 10 - OR - %endrep - %assert_zero -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/common.asm b/evm/src/cpu/kernel/asm/curve/common.asm deleted file mode 100644 index 50f174fac1..0000000000 --- a/evm/src/cpu/kernel/asm/curve/common.asm +++ /dev/null @@ -1,25 +0,0 @@ -global ret_zero_ec_mul: - // stack: x, y, s, retdest - %pop3 - // stack: retdest - PUSH 0 - // stack: 0, retdest - PUSH 0 - // stack: 0, 0, retdest - SWAP2 - // stack: retdest, 0, 0 - JUMP - -global ec_double_retself: - %stack (x, y, retdest) -> (retdest, x, y) - JUMP - -// Check if (x,y)==(0,0) -%macro ec_isidentity - // stack: x, y - OR - // stack: x | y - ISZERO - // stack: (x,y) == (0,0) -%endmacro - diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm deleted file mode 100644 index 43c47c5009..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/curve_add.asm +++ /dev/null @@ -1,287 +0,0 @@ -// #define N 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 // Secp256k1 scalar field order - -// Secp256k1 elliptic curve addition. -// Assumption: (x0,y0) and (x1,y1) are valid points. -global secp_add_valid_points: - // stack: x0, y0, x1, y1, retdest - - // Check if the first point is the identity. - DUP2 - // stack: y0, x0, y0, x1, y1, retdest - DUP2 - // stack: x0, y0, x0, y0, x1, y1, retdest - %ec_isidentity - // stack: (x0,y0)==(0,0), x0, y0, x1, y1, retdest - %jumpi(secp_add_first_zero) - // stack: x0, y0, x1, y1, retdest - - // Check if the second point is the identity. - DUP4 - // stack: y1, x0, y0, x1, y1, retdest - DUP4 - // stack: x1, y1, x0, y0, x1, y1, retdest - %ec_isidentity - // stack: (x1,y1)==(0,0), x0, y0, x1, y1, retdest - %jumpi(secp_add_snd_zero) - // stack: x0, y0, x1, y1, retdest - - // Check if both points have the same x-coordinate. - DUP3 - // stack: x1, x0, y0, x1, y1, retdest - DUP2 - // stack: x0, x1, x0, y0, x1, y1, retdest - EQ - // stack: x0 == x1, x0, y0, x1, y1, retdest - %jumpi(secp_add_equal_first_coord) -// Standard affine addition formula. -global secp_add_valid_points_no_edge_case: - // stack: x0, y0, x1, y1, retdest - // Compute lambda = (y0 - y1)/(x0 - x1) - %secp_base - // stack: N, x0, y0, x1, y1, retdest - DUP5 - DUP4 - // stack: y0, y1, N, x0, y0, x1, y1, retdest - SUBMOD - // stack: y0 - y1, x0, y0, x1, y1, retdest - %secp_base - // stack: N, y0 - y1, x0, y0, x1, y1, retdest - DUP5 - DUP4 - // stack: x0, x1, N, y0 - y1, x0, y0, x1, y1, retdest - SUBMOD - // stack: x0 - x1, y0 - y1, x0, y0, x1, y1, retdest - %moddiv_secp_base - // stack: lambda, x0, y0, x1, y1, retdest - %jump(secp_add_valid_points_with_lambda) - -// Secp256k1 elliptic curve addition. -// Assumption: (x0,y0) == (0,0) -secp_add_first_zero: - // stack: x0, y0, x1, y1, retdest - - // Just return (x1,y1) - %pop2 - // stack: x1, y1, retdest - SWAP1 - // stack: y1, x1, retdest - SWAP2 - // stack: retdest, x1, y1 - JUMP - -// Secp256k1 elliptic curve addition. -// Assumption: (x1,y1) == (0,0) -secp_add_snd_zero: - // stack: x0, y0, x1, y1, retdest - - // Just return (x1,y1) - SWAP2 - // stack: x1, y0, x0, y1, retdest - POP - // stack: y0, x0, y1, retdest - SWAP2 - // stack: y1, x0, y0, retdest - POP - // stack: x0, y0, retdest - SWAP1 - // stack: y0, x0, retdest - SWAP2 - // stack: retdest, x0, y0 - JUMP - -// Secp256k1 elliptic curve addition. -// Assumption: lambda = (y0 - y1)/(x0 - x1) -secp_add_valid_points_with_lambda: - // stack: lambda, x0, y0, x1, y1, retdest - - // Compute x2 = lambda^2 - x1 - x0 - %secp_base - // stack: N, lambda, x0, y0, x1, y1, retdest - DUP3 - // stack: x0, N, lambda, x0, y0, x1, y1, retdest - %secp_base - // stack: N, x0, N, lambda, x0, y0, x1, y1, retdest - DUP7 - // stack: x1, N, x0, N, lambda, x0, y0, x1, y1, retdest - %secp_base - // stack: N, x1, N, x0, N, lambda, x0, y0, x1, y1, retdest - DUP6 - // stack: lambda, N, x1, N, x0, N, lambda, x0, y0, x1, y1, retdest - DUP1 - // stack: lambda, lambda, N, x1, N, x0, N, lambda, x0, y0, x1, y1, retdest - MULMOD - // stack: lambda^2, x1, N, x0, N, lambda, x0, y0, x1, y1, retdest - SUBMOD - // stack: lambda^2 - x1, x0, N, lambda, x0, y0, x1, y1, retdest - SUBMOD - // stack: x2, lambda, x0, y0, x1, y1, retdest - - // Compute y2 = lambda*(x1 - x2) - y1 - %secp_base %secp_base %secp_base // Pre-load moduli for incoming SUBMODs - // stack: N, N, N, x2, lambda, x0, y0, x1, y1, retdest - DUP4 - // stack: x2, N, N, N, x2, lambda, x0, y0, x1, y1, retdest - DUP9 - // stack: x1, x2, N, N, N, x2, lambda, x0, y0, x1, y1, retdest - SUBMOD - // stack: x1 - x2, N, N, x2, lambda, x0, y0, x1, y1, retdest - DUP5 - // stack: lambda, x1 - x2, N, N, x2, lambda, x0, y0, x1, y1, retdest - MULMOD - // stack: lambda * (x1 - x2), N, x2, lambda, x0, y0, x1, y1, retdest - DUP8 - // stack: y1, lambda * (x1 - x2), N, x2, lambda, x0, y0, x1, y1, retdest - SWAP1 - // stack: lambda * (x1 - x2), y1, N, x2, lambda, x0, y0, x1, y1, retdest - SUBMOD - // stack: y2, x2, lambda, x0, y0, x1, y1, retdest - - // Return x2,y2 - SWAP5 - // stack: x1, x2, lambda, x0, y0, y2, y1, retdest - POP - // stack: x2, lambda, x0, y0, y2, y1, retdest - SWAP5 - // stack: y1, lambda, x0, y0, y2, x2, retdest - %pop4 - // stack: y2, x2, retdest - SWAP2 - // stack: retdest, x2, y2 - JUMP - -// Secp256k1 elliptic curve addition. -// Assumption: (x0,y0) and (x1,y1) are valid points and x0 == x1 -secp_add_equal_first_coord: - // stack: x0, y0, x1, y1, retdest with x0 == x1 - - // Check if the points are equal - DUP2 - // stack: y0, x0, y0, x1, y1, retdest - DUP5 - // stack: y1, y0, x0, y0, x1, y1, retdest - EQ - // stack: y1 == y0, x0, y0, x1, y1, retdest - %jumpi(secp_add_equal_points) - // stack: x0, y0, x1, y1, retdest - - // Otherwise, one is the negation of the other so we can return (0,0). - %pop4 - // stack: retdest - PUSH 0 - // stack: 0, retdest - PUSH 0 - // stack: 0, 0, retdest - SWAP2 - // stack: retdest, 0, 0 - JUMP - - -// Secp256k1 elliptic curve addition. -// Assumption: x0 == x1 and y0 == y1 -// Standard doubling formula. -secp_add_equal_points: - // Compute lambda = 3/2 * x0^2 / y0 - %stack (x0, y0, x1, y1, retdest) -> (x0, x0, @SECP_BASE, @SECP_BASE, x0, y0, x1, y1, retdest) - MULMOD - PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe19 // 3/2 in the base field - MULMOD - DUP3 - %moddiv_secp_base - %jump(secp_add_valid_points_with_lambda) - -// Secp256k1 elliptic curve doubling. -// Assumption: (x,y) is a valid point. -// Standard doubling formula. -global secp_double: - // stack: x, y, retdest - DUP2 DUP2 %ec_isidentity - // stack: (x,y)==(0,0), x, y, retdest - %jumpi(ec_double_retself) - - // Compute lambda = 3/2 * x0^2 / y0 - %stack (x, y, retdest) -> (x, x, @SECP_BASE, @SECP_BASE, x, y, x, y, retdest) - MULMOD - PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffe19 // 3/2 in the base field - MULMOD - DUP3 - %moddiv_secp_base - // stack: lambda, x, y, x, y, retdest - %jump(secp_add_valid_points_with_lambda) - -// Push the order of the Secp256k1 scalar field. -%macro secp_base - PUSH @SECP_BASE -%endmacro - -// Modular subtraction. -%macro submod_secp_base - // stack: x, y - %stack (x, y) -> (x, y, @SECP_BASE) - SUBMOD -%endmacro - -// Check if (x,y) is a valid curve point. -// Puts y^2 % N == (x^3 + 3) % N & (x < N) & (y < N) || (x,y)==(0,0) on top of the stack. -%macro secp_check - // stack: x, y - %secp_base - // stack: N, x, y - DUP2 - // stack: x, N, x, y - LT - // stack: x < N, x, y - %secp_base - // stack: N, x < N, x, y - DUP4 - // stack: y, N, x < N, x, y - LT - // stack: y < N, x < N, x, y - AND - // stack: (y < N) & (x < N), x, y - SWAP2 - // stack: y, x, (y < N) & (x < N), x - SWAP1 - // stack: x, y, (y < N) & (x < N) - %secp_base - // stack: N, x, y, b - %secp_base - // stack: N, N, x, y, b - DUP3 - // stack: x, N, N, x, y, b - %secp_base - // stack: N, x, N, N, x, y, b - DUP2 - // stack: x, N, x, N, N, x, y, b - DUP1 - // stack: x, x, N, x, N, N, x, y, b - MULMOD - // stack: x^2 % N, x, N, N, x, y, b - MULMOD - // stack: x^3 % N, N, x, y, b - PUSH 7 - // stack: 7, x^3 % N, N, x, y, b - ADDMOD - // stack: (x^3 + 7) % N, x, y, b - DUP3 - // stack: y, (x^3 + 7) % N, x, y, b - %secp_base - // stack: N, y, (x^3 + 7) % N, x, y, b - SWAP1 - // stack: y, N, (x^3 + 7) % N, x, y, b - DUP1 - // stack: y, y, N, (x^3 + 7) % N, x, y, b - MULMOD - // stack: y^2 % N, (x^3 + 7) % N, x, y, b - EQ - // stack: y^2 % N == (x^3 + 7) % N, x, y, b - SWAP2 - // stack: y, x, y^2 % N == (x^3 + 7) % N, b - %ec_isidentity - // stack: (x,y)==(0,0), y^2 % N == (x^3 + 7) % N, b - SWAP2 - // stack: b, y^2 % N == (x^3 + 7) % N, (x,y)==(0,0) - AND - // stack: y^2 % N == (x^3 + 7) % N & (x < N) & (y < N), (x,y)==(0,0) - OR - // stack: y^2 % N == (x^3 + 7) % N & (x < N) & (y < N) || (x,y)==(0,0) -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm deleted file mode 100644 index c11031004f..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ /dev/null @@ -1,186 +0,0 @@ -// ecrecover precompile. -global ecrecover: - // stack: hash, v, r, s, retdest - - // Check if inputs are valid. - %ecrecover_input_check - // stack: isValid(v,r,s), hash, v, r, s, retdest - - %stack (valid, hash, v, r, s, retdest) -> (v, 27, r, hash, valid, r, s, retdest) - SUB - // stack: v - 27, r, hash, isValid(v,r,s), r, s, retdest - SWAP1 - // stack: r, v - 27, hash, isValid(v,r,s), r, s, retdest - %secp_lift_x - // stack: y, sqrtOk, hash, isValid(v,r,s), r, s, retdest - - // If inputs are invalid or lifting fails, abort. - SWAP3 - // stack: isValid(v,r,s), sqrtOk, hash, y, r, s, retdest - AND - // stack: isValid(v,r,s) & sqrtOk, hash, y, r, s, retdest - %jumpi(ecrecover_valid_input) - // stack: hash, y, r, s, retdest - %pop4 - // stack: retdest - %ecrecover_invalid_input - -// ecrecover precompile. -// Assumption: Inputs are valid. -// Pseudo-code: -// let P = lift_x(r, recovery_id); -// let r_inv = r.inverse(); -// let u1 = s * r_inv; -// let u2 = -hash * r_inv; -// return u1*P + u2*GENERATOR; -ecrecover_valid_input: - // stack: hash, y, r, s, retdest - - // Compute u1 = s * r^(-1) - SWAP1 - // stack: y, hash, r, s, retdest - DUP3 - // stack: r, y, hash, x, s, retdest (r=x) - %inverse_secp_scalar - // stack: r^(-1), y, hash, x, s, retdest - DUP1 - // stack: r^(-1), r^(-1), y, hash, x, s, retdest - SWAP5 - // stack: s, r^(-1), y, hash, x, r^(-1), retdest - %mulmodn_secp_scalar - // stack: u1, y, hash, x, r^(-1), retdest - - // Compute u2 = -hash * r^(-1) - %stack (u1, y, hash, x, rinv, retdest) -> (hash, @SECP_SCALAR, @SECP_SCALAR, rinv, @SECP_SCALAR, u1, x, y, pubkey_to_addr, retdest) - MOD SWAP1 SUB MULMOD - // stack: u2, u1, x, y, pubkey_to_addr, retdest - %jump(ecdsa_msm_with_glv) - -// Computes `a * G + b * Q` using GLV+precomputation, where `G` is the Secp256k1 generator and `Q` is a point on the curve. -// Pseudo-code: -// precompute_table(G) -- precomputation table for the combinations of `G, phi(G), Q, phi(Q)`. -// let a0, a1 = glv_decompose(a) -// let b0, b1 = glv_decompose(b) -// return msm_with_precomputation([a0, a1, b0, b1], [G, phi(G), Q, phi(Q)]) -- phi is the Secp endomorphism. -ecdsa_msm_with_glv: - %stack (a, b, Qx, Qy, retdest) -> (a, ecdsa_after_glv_a, b, Qx, Qy, retdest) - %jump(secp_glv_decompose) -ecdsa_after_glv_a: - %stack (a1neg, a0, a1, b, Qx, Qy, retdest) -> (b, ecdsa_after_glv_b, a1neg, a0, a1, Qx, Qy, retdest) - %jump(secp_glv_decompose) -ecdsa_after_glv_b: - %stack (b1neg, b0, b1, a1neg, a0, a1, Qx, Qy, retdest) -> (a1neg, b1neg, Qx, Qy, ecdsa_after_precompute, a0, a1, b0, b1, retdest) - %jump(secp_precompute_table) -ecdsa_after_precompute: - // stack: a0, a1, b0, b1, retdest - PUSH 0 PUSH 0 PUSH 129 // 129 is the bit length of the GLV exponents - // stack: i, accx, accy, a0, a1, b0, b1, retdest -ecdsa_after_precompute_loop: - %stack (i, accx, accy, a0, a1, b0, b1, retdest) -> (i, b1, i, accx, accy, a0, a1, b0, b1, retdest) - SHR %and_const(1) - %stack (bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) - SHR %and_const(1) - %stack (bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) - SHR %and_const(1) - %stack (bit_a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) -> (i, a0, bit_a1, bit_b0, bit_b1, i, accx, accy, a0, a1, b0, b1, retdest) - SHR %and_const(1) - %mul_const(2) ADD %mul_const(2) ADD %mul_const(2) ADD - %stack (index, i, accx, accy, a0, a1, b0, b1, retdest) -> (index, index, i, accx, accy, a0, a1, b0, b1, retdest) - %mul_const(2) %add_const(1) - %mload_current(@SEGMENT_ECDSA_TABLE) - SWAP1 %mul_const(2) - %mload_current(@SEGMENT_ECDSA_TABLE) - %stack (Px, Py, i, accx, accy, a0, a1, b0, b1, retdest) -> (Px, Py, accx, accy, ecdsa_after_precompute_loop_contd, i, a0, a1, b0, b1, retdest) - %jump(secp_add_valid_points) -ecdsa_after_precompute_loop_contd: - %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, retdest) - ISZERO %jumpi(ecdsa_after_precompute_loop_end) - %jump(secp_double) -ecdsa_after_precompute_loop_contd2: - %stack (accx, accy, i, a0, a1, b0, b1, retdest) -> (i, 1, accx, accy, a0, a1, b0, b1, retdest) - SUB // i - 1 - %jump(ecdsa_after_precompute_loop) -ecdsa_after_precompute_loop_end: - // Check that the public key is not the point at infinity. See https://github.com/ethereum/eth-keys/pull/76 for discussion. - DUP2 DUP2 ISZERO SWAP1 ISZERO MUL %jumpi(pk_is_infinity) - %stack (accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, retdest) -> (retdest, accx, accy) - JUMP - -pk_is_infinity: - %stack (accx, accy, ecdsa_after_precompute_loop_contd2, i, a0, a1, b0, b1, pubkey_to_addr, retdest) -> (retdest, @U256_MAX) - JUMP - -// Take a public key (PKx, PKy) and return the associated address KECCAK256(PKx || PKy)[-20:]. -pubkey_to_addr: - // stack: PKx, PKy, retdest - %keccak256_u256_pair - // stack: hash, retdest - %u256_to_addr - // stack: address, retdest - SWAP1 - // stack: retdest, address - JUMP - -// Check if v, r, and s are in correct form. -// Returns r < N & r!=0 & s < N & s!=0 & (v==28 || v==27). -%macro ecrecover_input_check - // stack: hash, v, r, s, retdest - DUP2 - // stack: v, hash, v, r, s, retdest - %eq_const(27) - // stack: v==27, hash, v, r, s, retdest - DUP3 - // stack: v, v==27, hash, v, r, s, retdest - %eq_const(28) - // stack: v==28, v==27, hash, v, r, s, retdest - ADD // OR - // stack: (v==28 || v==27), hash, v, r, s, retdest - DUP5 - // stack: s, (v==28 || v==27), hash, v, r, s, retdest - %secp_is_out_of_bounds - // stack: (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest - DUP5 - // stack: r, (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest - %secp_is_out_of_bounds - // stack: (r >= N || r==0), (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest - ADD // OR - // stack: (r >= N || r==0 || s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest - ISZERO - // stack: (r < N & r!=0 & s < N & s!=0), (v==28 || v==27), hash, v, r, s, retdest - AND - // stack: r < N & r!=0 & s < N & s!=0 & (v==28 || v==27), hash, v, r, s, retdest -%endmacro - -%macro secp_is_out_of_bounds - // stack: x - DUP1 - // stack: x, x - ISZERO - // stack: x==0, x - SWAP1 - // stack: x, x==0 - %secp_scalar - // stack: N, x, x==0 - SWAP1 - // stack: x, N, x==0 - LT - // stack: x < N, x==0 - ISZERO - // stack: x >= N, x==0 - ADD // OR - // stack: x >= N || x==0 -%endmacro - -%macro secp_scalar - PUSH @SECP_SCALAR -%endmacro - -// Return u256::MAX which is used to indicate the input was invalid. -%macro ecrecover_invalid_input - // stack: retdest - PUSH @U256_MAX - // stack: u256::MAX, retdest - SWAP1 - // stack: retdest, u256::MAX - JUMP -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm deleted file mode 100644 index 26d887f269..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/glv.asm +++ /dev/null @@ -1,104 +0,0 @@ -// Inspired by https://github.com/AztecProtocol/weierstrudel/blob/master/huff_modules/endomorphism.huff -// See also Sage code in evm/src/cpu/kernel/tests/ecc/secp_glv_test_data -// Given scalar `k ∈ Secp256k1::ScalarField`, return `u, k1, k2` with `k1,k2 < 2^129` and such that -// `k = k1 - s*k2` if `u==0` otherwise `k = k1 + s*k2`, where `s` is the scalar value representing the endomorphism. -// In the comments below, N means @SECP_SCALAR -// -// Z3 proof that the resulting `k1, k2` satisfy `k1>0`, `k1 < 2^129` and `|k2| < 2^129`. -// ```python -// from z3 import Solver, Int, Or, unsat -// q = 115792089237316195423570985008687907852837564279074904382605163141518161494337 -// glv_s = 37718080363155996902926221483475020450927657555482586988616620542887997980018 -// g1 = 303414439467246543595250775667605759172 -// g2 = 64502973549206556628585045361533709077 -// b2 = 64502973549206556628585045361533709077 -// b1 = -303414439467246543595250775667605759171 -// k = Int("k") -// c1 = Int("c1") -// c2 = Int("c2") -// s = Solver() -// -// c2p = -c2 -// s.add(k < q) -// s.add(0 < k) -// s.add(c1 * (2**256) <= g2 * k) -// s.add((c1 + 1) * (2**256) > g2 * k) -// s.add(c2p * (2**256) <= g1 * k) -// s.add((c2p + 1) * (2**256) > g1 * k) -// -// q1 = c1 * b1 -// q2 = c2 * b2 -// -// k2 = q2 - q1 -// k2L = (glv_s * k2) % q -// k1 = k - k2L -// -// s.add(Or((k2 >= 2**129), (-k2 >= 2**129), (k1 >= 2**129), (k1 < 0))) -// assert s.check() == unsat -// ``` -global secp_glv_decompose: - // stack: k, retdest - PUSH @SECP_SCALAR DUP1 DUP1 - // Compute c2 which is the top 256 bits of k*g1. Use asm from https://medium.com/wicketh/mathemagic-full-multiply-27650fec525d. - PUSH @U256_MAX - // stack: -1, N, N, N, k, retdest - PUSH @SECP_GLV_MINUS_G1 DUP6 - // stack: k, g1, -1, N, N, N, k, retdest - MULMOD - // stack: (k * g1 % -1), N, N, N, k, retdest - PUSH @SECP_GLV_MINUS_G1 DUP6 - // stack: k, g1, (k * g1 % -1), N, N, N, k, retdest - MUL - // stack: bottom = (k * g1), (k * g1 % -1), N, N, N, k, retdest - DUP1 DUP3 - // stack: (k * g1 % -1), bottom, bottom, (k * g1 % -1), N, N, N, k, retdest - LT SWAP2 SUB SUB - // stack: c2, N, N, N, k, retdest - PUSH @SECP_GLV_B2 MULMOD - // stack: q2=c2*b2, N, N, k, retdest - - // Use the same trick to compute c1 = top 256 bits of g2*k. - PUSH @SECP_SCALAR PUSH @U256_MAX - PUSH @SECP_GLV_G2 DUP7 MULMOD - PUSH @SECP_GLV_G2 DUP7 MUL - DUP1 DUP3 LT - SWAP2 SUB SUB - // stack: c1, N, q2, N, N, k, retdest - PUSH @SECP_GLV_B1 MULMOD - // stack: q1, q2, N, N, k, retdest - - // We compute k2 = q1 + q2 - N, but we check for underflow and return N-q1-q2 instead if there is one, - // along with a flag `underflow` set to 1 if there is an underflow, 0 otherwise. - ADD %sub_check_underflow - // stack: k2, underflow, N, k, retdest - SWAP3 PUSH @SECP_SCALAR DUP5 PUSH @SECP_GLV_S - // stack: s, k2, N, k, underflow, N, k2, retdest - MULMOD - // stack: s*k2, k, underflow, N, k2, retdest - // Need to return `k + s*k2` if no underflow occur, otherwise return `k - s*k2` which is done in the `underflowed` fn. - SWAP2 DUP1 %jumpi(underflowed) - %stack (underflow, k, x, N, k2) -> (k, x, N, k2, underflow) - ADDMOD - %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) - JUMP - -underflowed: - // stack: underflow, k, s*k2, N, k2 - // Compute (k-s*k2)%N. - %stack (u, k, x, N, k2) -> (k, x, N, k2, u) - SUBMOD - %stack (k1, k2, underflow, retdest) -> (retdest, underflow, k1, k2) - JUMP - -%macro sub_check_underflow - // stack: x, y - DUP2 DUP2 LT - // stack: x=y, x (x, y, b, a, c) - SUB MUL ADD - %stack (res, bool) -> (res, @SECP_SCALAR, bool) - MOD -%endmacro - diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/inverse_scalar.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/inverse_scalar.asm deleted file mode 100644 index 6e1563e2f2..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/inverse_scalar.asm +++ /dev/null @@ -1,31 +0,0 @@ -/// Division modulo 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141, the Secp256k1 scalar field order -/// To replace with more efficient method using non-determinism later. - -%macro mulmodn_secp_scalar - // stack: x, y - %secp_scalar - // stack: N, x, y - SWAP2 - // stack: y, x, N - MULMOD -%endmacro - -%macro squaremodn_secp_scalar - // stack: x - DUP1 - // stack: x, x - %mulmodn_secp_scalar -%endmacro - -// Non-deterministically provide the inverse modulo N. -%macro inverse_secp_scalar - // stack: x - PROVER_INPUT(ff::secp256k1_scalar::inverse) - // stack: x^-1, x - %stack (inv, x) -> (inv, x, @SECP_SCALAR, inv) - // stack: x^-1, x, N, x^-1 - MULMOD - // stack: x^-1 * x, x^-1 - %assert_eq_const(1) - // stack: x^-1 -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/lift_x.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/lift_x.asm deleted file mode 100644 index 77e484be5c..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/lift_x.asm +++ /dev/null @@ -1,73 +0,0 @@ -// Returns y such that (x,y) is on Secp256k1 and y&1 = parity, -// as well as a flag indicating whether such a y exists. -%macro secp_lift_x - // stack: x, parity - %cubemodn_secp_base - // stack: x^3, parity - PUSH 7 - // stack: 7, x^3, parity - %addmodn_secp_base - // stack: x^3+7, x, parity - DUP1 - // stack: x^3+7, x^3+7, parity - %sqrt_secp_base_unsafe - // stack: y, x^3+7, x, parity - SWAP1 - // stack: x^3+7, y, parity - DUP2 - // stack: y, x^3+7, y, parity - %squaremodn_secp_base - // stack: y^2, x^3+7, y, parity - EQ - // stack: sqrtOk, y, parity - SWAP2 - // stack: parity, y, sqrtOk - DUP2 - // stack: y, parity, y, sqrtOk - PUSH 1 - // stack: 1, y, parity, y, sqrtOk - AND - // stack: 1 & y, parity, y, sqrtOk - EQ - // stack: correctParity, y, sqrtOk - DUP2 - // stack: y, correctParity, y, sqrtOk - %secp_base - // stack: N, y, correctParity, y, sqrtOk - SUB - // stack: N - y, correctParity, y, sqrtOk - SWAP1 - // stack: correctParity, N - y, y, sqrtOk - %select_bool - // stack: goody, sqrtOk -%endmacro - -%macro cubemodn_secp_base - // stack: x - DUP1 - // stack: x, x - %squaremodn_secp_base - // stack: x^2, x - %mulmodn_secp_base -%endmacro - -%macro addmodn_secp_base - // stack: x, y - %secp_base - // stack: N, x, y - SWAP2 - // stack: y, x, N - ADDMOD -%endmacro - -// Non-deterministically provide the square root modulo N. -// Note: The square root is not checked and the macro doesn't panic if `x` is not a square. -%macro sqrt_secp_base_unsafe - // stack: x - PROVER_INPUT(ff::secp256k1_base::sqrt) - // stack: √x, x - SWAP1 - // stack: x, √x - POP - // stack: √x -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/moddiv.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/moddiv.asm deleted file mode 100644 index d878dc1404..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/moddiv.asm +++ /dev/null @@ -1,39 +0,0 @@ -/// Division modulo 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, the Secp256k1 base field order -/// To replace with more efficient method using non-determinism later. - -// Returns y * (x^-1) where the inverse is taken modulo N -%macro moddiv_secp_base - // stack: x, y - %inverse_secp_base - // stack: x^-1, y - %mulmodn_secp_base -%endmacro - -%macro mulmodn_secp_base - // stack: x, y - %secp_base - // stack: N, x, y - SWAP2 - // stack: y, x, N - MULMOD -%endmacro - -%macro squaremodn_secp_base - // stack: x - DUP1 - // stack: x, x - %mulmodn_secp_base -%endmacro - -// Non-deterministically provide the inverse modulo N. -%macro inverse_secp_base - // stack: x - PROVER_INPUT(ff::secp256k1_base::inverse) - // stack: x^-1, x - %stack (inv, x) -> (inv, x, @SECP_BASE, inv) - // stack: x^-1, x, N, x^-1 - MULMOD - // stack: x^-1 * x, x^-1 - %assert_eq_const(1) - // stack: x^-1 -%endmacro diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm deleted file mode 100644 index b6bed1b0a9..0000000000 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/precomputation.asm +++ /dev/null @@ -1,74 +0,0 @@ -// Initial stack: Gneg, Qneg, Qx, Qy, retdest -// Compute a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4 and store its x-coordinate at location `2*(8a+4b+2c+d)` and its y-coordinate at location `2*(8a+4b+2c+d)+1` in the SEGMENT_ECDSA_TABLE segment. -global secp_precompute_table: - // First store G, ± phi(G), G ± phi(G) - // Use Gneg for the ±, e.g., ±phi(G) is computed as `Gneg * (-phi(G)) + (1-Gneg)*phi(G)` (note only the y-coordinate needs to be filtered). - // stack: Gneg, Qneg, Qx, Qy, retdest - PUSH 32670510020758816978083085130507043184471273380659243275938904335757337482424 PUSH 17 PUSH 55066263022277343669578718895168534326250603453777594175500187360389116729240 PUSH 16 - %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) - - DUP1 DUP1 %mul_const(32670510020758816978083085130507043184471273380659243275938904335757337482424) SWAP1 PUSH 1 SUB %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) ADD - PUSH 9 PUSH 85340279321737800624759429340272274763154997815782306132637707972559913914315 PUSH 8 - %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) - - DUP1 DUP1 %mul_const(83121579216557378445487899878180864668798711284981320763518679672151497189239) SWAP1 PUSH 1 SUB %mul_const(100652675408719987021357910538015346127426077519185866739835120963490438734674) ADD - PUSH 25 - %mstore_current(@SEGMENT_ECDSA_TABLE) - - DUP1 %mul_const(91177636130617246552803821781935006617134368061721227770777272682868638699771) SWAP1 PUSH 1 SUB %mul_const(66837770201594535779099350687042404727408598709762866365333192677982385899440) ADD - PUSH 24 - %mstore_current(@SEGMENT_ECDSA_TABLE) - - // Then store Q, ±phi(Q), Q ± phi(Q) - %stack (Qneg, Qx, Qy, retdest) -> (4, Qx, 5, Qy, Qx, @SECP_BASE, Qneg, Qx, Qy, retdest) - %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) - // stack: Qx, @SECP_BASE, Qx, Qy, retdest - PUSH @SECP_GLV_BETA MULMOD - %stack (betaQx, Qneg, Qx, Qy, retdest) -> (Qneg, Qy, Qneg, betaQx, Qx, Qy, retdest) - MUL SWAP1 PUSH 1 SUB - // stack: 1-Qneg, Qneg*Qy, betaQx, Qx, Qy, retdest - DUP5 PUSH @SECP_BASE SUB MUL ADD - %stack (selectQy, betaQx, Qx, Qy, retdest) -> (2, betaQx, 3, selectQy, betaQx, selectQy, Qx, Qy, precompute_table_contd, retdest) - %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) - %jump(secp_add_valid_points_no_edge_case) -precompute_table_contd: - %stack (x, y, retdest) -> (6, x, 7, y, retdest) - %mstore_current(@SEGMENT_ECDSA_TABLE) %mstore_current(@SEGMENT_ECDSA_TABLE) - PUSH 2 -// Use a loop to store a*G ± b*phi(G) + c*Q ± d*phi(Q) for a,b,c,d in {0,1}^4. -precompute_table_loop: - // stack: i, retdest - DUP1 %increment %mload_current(@SEGMENT_ECDSA_TABLE) - %stack (y, i, retdest) -> (i, y, i, retdest) - %mload_current(@SEGMENT_ECDSA_TABLE) - PUSH precompute_table_loop_contd - DUP3 DUP3 - PUSH 9 %mload_current(@SEGMENT_ECDSA_TABLE) - PUSH 8 %mload_current(@SEGMENT_ECDSA_TABLE) - // stack: Gx, Gy, x, y, precompute_table_loop_contd, x, y, i, retdest - %jump(secp_add_valid_points) -precompute_table_loop_contd: - %stack (Rx, Ry, x, y, i, retdest) -> (i, 8, Rx, i, 9, Ry, x, y, i, retdest) - ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) - DUP2 DUP2 - PUSH 17 %mload_current(@SEGMENT_ECDSA_TABLE) - PUSH 16 %mload_current(@SEGMENT_ECDSA_TABLE) - %stack (Gx, Gy, x, y, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd2, x, y, i, retdest) - %jump(secp_add_valid_points) -precompute_table_loop_contd2: - %stack (Rx, Ry, x, y, i, retdest) -> (i, 16, Rx, i, 17, Ry, x, y, i, retdest) - ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) - PUSH 25 %mload_current(@SEGMENT_ECDSA_TABLE) - PUSH 24 %mload_current(@SEGMENT_ECDSA_TABLE) - %stack (Gx, Gy, x, y, i, retdest) -> (Gx, Gy, x, y, precompute_table_loop_contd3, i, retdest) - %jump(secp_add_valid_points) -precompute_table_loop_contd3: - %stack (Rx, Ry, i, retdest) -> (i, 24, Rx, i, 25, Ry, i, retdest) - ADD %mstore_current(@SEGMENT_ECDSA_TABLE) ADD %mstore_current(@SEGMENT_ECDSA_TABLE) - %add_const(2) - DUP1 %eq_const(8) %jumpi(precompute_table_end) - %jump(precompute_table_loop) - -precompute_table_end: - // stack: i, retdest - POP JUMP diff --git a/evm/src/cpu/kernel/asm/curve/wnaf.asm b/evm/src/cpu/kernel/asm/curve/wnaf.asm deleted file mode 100644 index f554bc649d..0000000000 --- a/evm/src/cpu/kernel/asm/curve/wnaf.asm +++ /dev/null @@ -1,74 +0,0 @@ -// wNAF expansion with w=5. -// Stores the reversed expansion of the given scalar in memory at the given segment and offsets 0..130. -// Should be called with scalars of bit length <= 129, which is the case when using GLV. -// Pseudo-code: -// def wnaf(n): -// ans = [0 for _ in range(130)] -// o = 0 -// while n != 0: -// i = n.trailing_zero_bits() -// o += i -// n >>= i -// m = n & 31 -// ans[o] = m -// if m > 16: -// ne += 32 -// ne -= m -// return ans -global wnaf: - // stack: N, segment, n, retdest (N is the size of the group in which the mul is taking place) - DUP3 MOD ISZERO %jumpi(wnaf_zero_scalar) - PUSH 0 -wnaf_loop: - %stack (o, segment, n, retdest) -> (n, wnaf_loop_contd, o, segment, retdest) - %jump(trailing_zeros) -wnaf_loop_contd: - %stack (n, i, o, segment, retdest) -> (o, i, n, segment, retdest) - ADD - %stack (o, n, segment, retdest) -> (n, segment, o, retdest) - DUP1 %and_const(31) SWAP1 - PUSH 16 DUP3 GT - // stack: m>16, n, m, segment, o, retdest - %mul_const(32) ADD - // stack: n, m, segment, o, retdest - DUP2 SWAP1 SUB - %stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest) - SUB - // stack: i, m, o, segment, n, retdest - DUP4 - GET_CONTEXT - %build_address - // stack: addr, m, o, segment, n, retdest - SWAP1 - MSTORE_GENERAL - // stack: o, segment, n, retdest - DUP3 ISZERO %jumpi(wnaf_end) - // stack: o, segment, n, retdest - %jump(wnaf_loop) - -wnaf_end: - // stack: o, segment, n, retdest - %pop3 JUMP - -wnaf_zero_scalar: - // stack: segment, n, retdest - %pop2 JUMP - - - -// Number of trailing zeros computed with a simple loop and returning the scalar without its lsb zeros. -trailing_zeros: - // stack: x, retdest - PUSH 0 -trailing_zeros_loop: - // stack: count, x, retdest - PUSH 1 DUP3 AND - // stack: x&1, count, x, retdest - %jumpi(trailing_zeros_end) - // stack: count, x, retdest - %increment SWAP1 PUSH 1 SHR SWAP1 - // stack: count, x>>1, retdest - %jump(trailing_zeros_loop) -trailing_zeros_end: - %stack (count, x, retdest) -> (retdest, x, count) - JUMP diff --git a/evm/src/cpu/kernel/asm/exp.asm b/evm/src/cpu/kernel/asm/exp.asm deleted file mode 100644 index 4b798e841c..0000000000 --- a/evm/src/cpu/kernel/asm/exp.asm +++ /dev/null @@ -1,102 +0,0 @@ -/// Recursive implementation of exp. -/// Equivalent to: -/// def exp(x, e): -/// if e == 0: -/// # The path where JUMPI does not jump to `step_case` -/// return 1 -/// else: -/// # This is under the `step_case` label -/// return (x if e % 2 else 1) * exp(x * x, e // 2) -/// Note that this correctly handles exp(0, 0) == 1. - -global exp: - // stack: x, e, retdest - dup2 - // stack: e, x, e, retdest - %jumpi(step_case) - // stack: x, e, retdest - pop - // stack: e, retdest - pop - // stack: retdest - push 1 - // stack: 1, retdest - swap1 - // stack: retdest, 1 - jump - -step_case: - // stack: x, e, retdest - push recursion_return - // stack: recursion_return, x, e, retdest - push 2 - // stack: 2, recursion_return, x, e, retdest - dup4 - // stack: e, 2, recursion_return, x, e, retdest - div - // stack: e / 2, recursion_return, x, e, retdest - dup3 - // stack: x, e / 2, recursion_return, x, e, retdest - %square - // stack: x * x, e / 2, recursion_return, x, e, retdest - %jump(exp) -recursion_return: - // stack: exp(x * x, e / 2), x, e, retdest - push 2 - // stack: 2, exp(x * x, e / 2), x, e, retdest - dup4 - // stack: e, 2, exp(x * x, e / 2), x, e, retdest - mod - // stack: e % 2, exp(x * x, e / 2), x, e, retdest - push 1 - // stack: 1, e % 2, exp(x * x, e / 2), x, e, retdest - dup4 - // stack: x, 1, e % 2, exp(x * x, e / 2), x, e, retdest - sub - // stack: x - 1, e % 2, exp(x * x, e / 2), x, e, retdest - mul - // stack: (x - 1) * (e % 2), exp(x * x, e / 2), x, e, retdest - push 1 - // stack: 1, (x - 1) * (e % 2), exp(x * x, e / 2), x, e, retdest - add - // stack: 1 + (x - 1) * (e % 2), exp(x * x, e / 2), x, e, retdest - mul - // stack: (1 + (x - 1) * (e % 2)) * exp(x * x, e / 2), x, e, retdest - swap3 - // stack: retdest, x, e, (1 + (x - 1) * (e % 2)) * exp(x * x, e / 2) - swap2 - // stack: e, x, retdest, (1 + (x - 1) * (e % 2)) * exp(x * x, e / 2) - pop - // stack: x, retdest, (1 + (x - 1) * (e % 2)) * exp(x * x, e / 2) - pop - // stack: retdest, (1 + (x - 1) * (e % 2)) * exp(x * x, e / 2) - jump - -global sys_exp: - %stack (return_info, x, e) -> (x, e, return_info) - push 0 - // stack: shift, x, e, return_info - %jump(sys_exp_gas_loop_enter) -sys_exp_gas_loop: - %add_const(8) -sys_exp_gas_loop_enter: - dup3 - dup2 - shr - // stack: e >> shift, shift, x, e, return_info - %jumpi(sys_exp_gas_loop) - // stack: shift_bits, x, e, return_info - %shr_const(3) - // stack: byte_size_of_e := shift_bits / 8, x, e, return_info - %mul_const(@GAS_EXPBYTE) - %add_const(@GAS_EXP) - // stack: gas_cost := 10 + 50 * byte_size_of_e, x, e, return_info - %stack(gas_cost, x, e, return_info) -> (gas_cost, return_info, x, e) - %charge_gas - - %stack(return_info, x, e) -> (x, e, sys_exp_return, return_info) - %jump(exp) -sys_exp_return: - // stack: pow(x, e), return_info - swap1 - exit_kernel diff --git a/evm/src/cpu/kernel/asm/halt.asm b/evm/src/cpu/kernel/asm/halt.asm deleted file mode 100644 index 49561fd660..0000000000 --- a/evm/src/cpu/kernel/asm/halt.asm +++ /dev/null @@ -1,2 +0,0 @@ -global halt: - PANIC diff --git a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm b/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm deleted file mode 100644 index 3244cfa1f2..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/addresses.asm +++ /dev/null @@ -1,31 +0,0 @@ -// Address where the working version of the hash value is stored. -// It is ready to be used, i.e. already containing the current context -// and SEGMENT_KERNEL_GENERAL. -%macro blake2_hash_value_addr - %build_current_general_address_no_offset - DUP1 - MLOAD_GENERAL - // stack: num_blocks, addr - %block_size - %add_const(2) - // stack: num_bytes+2, addr - ADD - // stack: addr -%endmacro - -// Address where the working version of the compression internal state is stored. -%macro blake2_internal_state_addr - %blake2_hash_value_addr - %add_const(8) -%endmacro - -// Address where the current message block is stored. -%macro blake2_message_addr - %blake2_internal_state_addr - %add_const(16) -%endmacro - -// Block size is 128 bytes. -%macro block_size - %mul_const(128) -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm b/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm deleted file mode 100644 index d1a4a2ab64..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/blake2_f.asm +++ /dev/null @@ -1,141 +0,0 @@ -global blake2_f: - // stack: rounds, h0...h7, m0...m15, t0, t1, flag, retdest - - // Store the hash values. - %blake2_hash_value_addr - // stack: addr, rounds, h0...h7, m0...m15, t0, t1, flag, retdest - %rep 8 - // stack: addr, rounds, h_i, ... - %stack (addr, rounds, h_i) -> (h_i, addr, addr, rounds) - // stack: h_i, addr, addr, rounds, ... - MSTORE_GENERAL - %increment - %endrep - - // stack: addr, rounds, m0...m15, t0, t1, flag, retdest - POP - // stack: rounds, m0...m15, t0, t1, flag, retdest - - // Save the message to the message working space. - %blake2_message_addr - // stack: message_addr, rounds, m0...m15, t0, t1, flag, retdest - %rep 16 - // stack: message_addr, rounds, m_i, ... - %stack (message_addr, rounds, m_i) -> (m_i, message_addr, message_addr, rounds) - // stack: m_i, message_addr, message_addr, rounds, ... - MSTORE_GENERAL - %increment - %endrep - - // stack: message_addr, rounds, t0, t1, flag, retdest - POP - // stack: rounds, t0, t1, flag, retdest - - %blake2_hash_value_addr - %add_const(7) - %rep 8 - // stack: addr, ... - DUP1 - // stack: addr, addr, ... - MLOAD_GENERAL - // stack: val, addr, ... - SWAP1 - // stack: addr, val, ... - %decrement - %endrep - // stack: addr, h_0, ..., h_7, rounds, t0, t1, flag, retdest - POP - // stack: h_0, ..., h_7, rounds, t0, t1, flag, retdest - - // Store the initial 16 values of the internal state. - %blake2_internal_state_addr - // stack: start, h_0, ..., h_7, rounds, t0, t1, flag, retdest - - // First eight words of the internal state: current hash value h_0, ..., h_7. - %rep 8 - DUP1 - SWAP2 - MSTORE_GENERAL - %increment - %endrep - // stack: start + 8, rounds, t0, t1, flag, retdest - - // Next four values of the internal state: first four IV values. - PUSH 0 - // stack: 0, addr, rounds, t0, t1, flag, retdest - %rep 4 - // stack: i, addr, ... - DUP2 - DUP2 - // stack: i, addr, i, addr, ... - %blake2_iv - // stack: IV_i, addr, i, addr, ... - MSTORE_GENERAL - // stack: i, addr, ... - %increment - SWAP1 - %increment - SWAP1 - // stack: i + 1, addr + 1,... - %endrep - // stack: 4, start + 12, rounds, t0, t1, flag, retdest - POP - // stack: start + 12, rounds, t0, t1, flag, retdest - SWAP4 - // stack: flag, rounds, t0, t1, start + 12, retdest - %mul_const(0xFFFFFFFFFFFFFFFF) - // stack: invert_if_flag, rounds, t0, t1, start + 12, retdest - %stack (inv, r, t0, t1, s) -> (4, s, t0, t1, inv, 0, r) - // stack: 4, start + 12, t0, t1, invert_if_flag, 0, rounds, retdest - - // Last four values of the internal state: last four IV values, XOR'd with - // the values (t0, t1, invert_if_flag, 0). - %rep 4 - // stack: i, addr, val, next_val,... - DUP2 - DUP2 - // stack: i, addr, i, addr, val, next_val,... - %blake2_iv - // stack: IV_i, addr, i, addr, val, next_val,... - DUP5 - // stack: val, IV_i, addr, i, addr, val, next_val,... - XOR - // stack: val ^ IV_i, addr, i, addr, val, next_val,... - MSTORE_GENERAL - // stack: i, addr, val, next_val,... - %increment - // stack: i + 1, addr, val, next_val,... - SWAP2 - // stack: val, addr, i + 1, next_val,... - POP - // stack: addr, i + 1, next_val,... - %increment - // stack: addr + 1, i + 1, next_val,... - SWAP1 - // stack: i + 1, addr + 1, next_val,... - %endrep - // stack: 8, start + 16, rounds, retdest - %pop2 - // stack: rounds, retdest - - // Run rounds of G functions. - PUSH g_functions_return - // stack: g_functions_return, rounds, retdest - SWAP1 - // stack: rounds, g_functions_return, retdest - %blake2_internal_state_addr - // stack: start, rounds, g_functions_return, retdest - PUSH 0 - // stack: current_round=0, start, rounds, g_functions_return, retdest - %jump(run_rounds_g_function) -g_functions_return: - // Finalize hash value. - // stack: retdest - PUSH hash_generate_return - // stack: hash_generate_return, retdest - %jump(blake2_generate_all_hash_values) -hash_generate_return: - // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', retdest - %stack (h: 8, retdest) -> (retdest, h) - // stack: retdest, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7' - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/blake2/blake2b.asm b/evm/src/cpu/kernel/asm/hash/blake2/blake2b.asm deleted file mode 100644 index e3daed263e..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/blake2b.asm +++ /dev/null @@ -1,14 +0,0 @@ -global blake2b: - // stack: virt, num_bytes, retdest - DUP2 - // stack: num_bytes, virt, num_bytes, retdest - %ceil_div_const(128) - // stack: num_blocks, virt, num_bytes, retdest - DUP2 - // stack: virt, num_blocks, virt, num_bytes, retdest - %mstore_current_general - // stack: virt, num_bytes, retdest - %add_const(1) - %mstore_current_general - // stack: retdest - %jump(blake2_compression) diff --git a/evm/src/cpu/kernel/asm/hash/blake2/compression.asm b/evm/src/cpu/kernel/asm/hash/blake2/compression.asm deleted file mode 100644 index ba9ffc1343..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/compression.asm +++ /dev/null @@ -1,265 +0,0 @@ -global blake2_compression: - // stack: retdest - PUSH 0 - // stack: cur_block = 0, retdest - PUSH compression_loop - // stack: compression_loop, cur_block, retdest - %jump(blake2_initial_hash_value) -compression_loop: - // stack: h_0, ..., h_7, cur_block, retdest - - // Store the hash values. - %blake2_hash_value_addr - // stack: addr, h_0, ..., h_7, cur_block, retdest - %rep 8 - SWAP1 - DUP2 - %mstore_current_general - %increment - %endrep - - // stack: addr, cur_block, retdest - POP - // stack: cur_block, retdest - PUSH 1 - PUSH 0 - %mload_current_general - // stack: num_blocks, 1, cur_block, retdest - SUB - // stack: num_blocks - 1, cur_block, retdest - DUP2 - // stack: cur_block, num_blocks - 1, cur_block, retdest - EQ - // stack: is_last_block, cur_block, retdest - SWAP1 - // stack: cur_block, is_last_block, retdest - PUSH 1 - %mload_current_general - // stack: num_bytes, cur_block, is_last_block, retdest - - // Calculate t counter value. - DUP3 - // stack: is_last_block, num_bytes, cur_block, is_last_block, retdest - MUL - // stack: is_last_block * num_bytes, cur_block, is_last_block, retdest - DUP2 - // stack: cur_block, is_last_block * num_bytes, cur_block, is_last_block, retdest - %increment - %block_size - // stack: (cur_block + 1) * 128, is_last_block * num_bytes, cur_block, is_last_block, retdest - DUP4 - // stack: is_last_block, (cur_block + 1) * 128, is_last_block * num_bytes, cur_block, is_last_block, retdest - ISZERO - // stack: not_last_block, (cur_block + 1) * 128, is_last_block * num_bytes, cur_block, is_last_block, retdest - MUL - // stack: not_last_block * ((cur_block + 1) * 128), is_last_block * num_bytes, cur_block, is_last_block, retdest - ADD - // stack: t = not_last_block * ((cur_block + 1) * 128) + is_last_block * num_bytes, cur_block, is_last_block, retdest - SWAP1 - // stack: cur_block, t, is_last_block, retdest - DUP1 - // stack: cur_block, cur_block, t, is_last_block, retdest - %block_size - %add_const(2) - // stack: cur_block_start_byte, t, cur_block, is_last_block, retdest - - // Copy the message from the input space to the message working space. - %blake2_message_addr - // stack: message_addr, cur_block_start_byte, t, cur_block, is_last_block, retdest - %rep 16 - // stack: cur_message_addr, cur_block_byte, ... - DUP2 - // stack: cur_block_byte, cur_message_addr, cur_block_byte, ... - %mload_current_general_u64_LE - // stack: m_i, cur_message_addr, cur_block_byte, ... - DUP2 - // stack: cur_message_addr, m_i, cur_message_addr, cur_block_byte, ... - %mstore_current_general - // stack: cur_message_addr, cur_block_byte, ... - %increment - // stack: cur_message_addr + 1, cur_block_byte, ... - SWAP1 - // stack: cur_block_byte, cur_message_addr + 1, ... - %add_const(8) - // stack: cur_block_byte + 8, cur_message_addr + 1, ... - SWAP1 - // stack: cur_message_addr + 1, cur_block_byte + 8, ... - %endrep - // stack: end_message_addr, end_block_start_byte, t, cur_block, is_last_block, retdest - %pop2 - // stack: t, cur_block, is_last_block, retdest - SWAP1 - // stack: cur_block, t, is_last_block, retdest - SWAP2 - // stack: is_last_block, t, cur_block, retdest - %mul_const(0xFFFFFFFFFFFFFFFF) - // stack: invert_if_last_block, t, cur_block, retdest - %blake2_hash_value_addr - %add_const(7) - %rep 8 - // stack: addr, ... - DUP1 - // stack: addr, addr, ... - %mload_current_general - // stack: val, addr, ... - SWAP1 - // stack: addr, val, ... - %decrement - %endrep - // stack: addr, h_0, ..., h_7, invert_if_last_block, t, cur_block, retdest - POP - // stack: h_0, ..., h_7, invert_if_last_block, t, cur_block, retdest - - // Store the initial 16 values of the internal state. - %blake2_internal_state_addr - // stack: start, h_0, ..., h_7, invert_if_last_block, t, cur_block, retdest - - // First eight words of the internal state: current hash value h_0, ..., h_7. - %rep 8 - SWAP1 - DUP2 - %mstore_current_general - %increment - %endrep - // stack: start + 8, invert_if_last_block, t, cur_block, retdest - - // Next four values of the internal state: first four IV values. - PUSH 0 - // stack: 0, start + 8, invert_if_last_block, t, cur_block, retdest - %rep 4 - // stack: i, loc, ... - DUP1 - // stack: i, i, loc, ... - %blake2_iv - // stack: IV_i, i, loc, ... - DUP3 - // stack: loc, IV_i, i, loc, ... - %mstore_current_general - // stack: i, loc, ... - %increment - SWAP1 - %increment - SWAP1 - // stack: i + 1, loc + 1,... - %endrep - // stack: 4, start + 12, invert_if_last_block, t, cur_block, retdest - %stack (i, loc, inv, last, t) -> (t, t, i, loc, inv, last) - // stack: t, t, 4, start + 12, invert_if_last_block, cur_block, retdest - %shr_const(64) - // stack: t_hi = t >> 64, t, 4, start + 12, invert_if_last_block, cur_block, retdest - SWAP1 - // stack: t, t_hi, 4, start + 12, invert_if_last_block, cur_block, retdest - %mod_const(0x10000000000000000) - // stack: t_lo = t % (1 << 64), t_hi, 4, start + 12, invert_if_last_block, cur_block, retdest - %stack (t_lo, t_hi, i, loc, inv) -> (i, loc, t_lo, t_hi, inv, 0) - // stack: 4, start + 12, t_lo, t_hi, invert_if_last_block, 0, cur_block, retdest - - // Last four values of the internal state: last four IV values, XOR'd with - // the values (t % 2**64, t >> 64, invert_if, 0). - %rep 4 - // stack: i, loc, val, next_val,... - DUP1 - // stack: i, i, loc, val, next_val,... - %blake2_iv - // stack: IV_i, i, loc, val, next_val,... - DUP4 - // stack: val, IV_i, i, loc, val, next_val,... - XOR - // stack: val ^ IV_i, i, loc, val, next_val,... - DUP3 - // stack: loc, val ^ IV_i, i, loc, val, next_val,... - %mstore_current_general - // stack: i, loc, val, next_val,... - %increment - // stack: i + 1, loc, val, next_val,... - SWAP2 - // stack: val, loc, i + 1, next_val,... - POP - // stack: loc, i + 1, next_val,... - %increment - // stack: loc + 1, i + 1, next_val,... - SWAP1 - // stack: i + 1, loc + 1, next_val,... - %endrep - // stack: 8, loc + 16, cur_block, retdest - %pop2 - // stack: cur_block, retdest - - // Run 12 rounds of G functions. - PUSH g_functions_return - // stack: g_functions_return, cur_block, retdest - PUSH 12 - %blake2_internal_state_addr - // stack: start, 12, g_functions_return, cur_block, retdest - PUSH 0 - // stack: current_round=0, start, 12, g_functions_return, cur_block, retdest - %jump(run_rounds_g_function) -g_functions_return: - // Finalize hash value. - // stack: cur_block, retdest - PUSH hash_generate_return - // stack: hash_generate_return, cur_block, retdest - %jump(blake2_generate_all_hash_values) -hash_generate_return: - // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block, retdest - DUP9 - // stack: cur_block, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block, retdest - %increment - // stack: cur_block + 1, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block, retdest - SWAP9 - // stack: cur_block, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - %increment - // stack: cur_block + 1, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - PUSH 0 - %mload_current_general - // stack: num_blocks, cur_block + 1, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - GT - // stack: not_last_block, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - %jumpi(compression_loop) -compression_end: - // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - - // Invert the bytes of each hash value. - %reverse_bytes_u64 - // stack: h_0'', h_1', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - SWAP1 - // stack: h_1', h_0'', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_1'', h_0'', h_2', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - SWAP2 - // stack: h_2', h_0'', h_1'', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_2'', h_0'', h_1'', h_3', h_4', h_5', h_6', h_7', cur_block + 1, retdest - SWAP3 - // stack: h_3', h_0'', h_1'', h_2'', h_4', h_5', h_6', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_3'', h_0'', h_1'', h_2'', h_4', h_5', h_6', h_7', cur_block + 1, retdest - SWAP4 - // stack: h_4', h_0'', h_1'', h_2'', h_3'', h_5', h_6', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_4'', h_0'', h_1'', h_2'', h_3'', h_5', h_6', h_7', cur_block + 1, retdest - SWAP5 - // stack: h_5', h_0'', h_1'', h_2'', h_3'', h_4'', h_6', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_5'', h_0'', h_1'', h_2'', h_3'', h_4'', h_6', h_7', cur_block + 1, retdest - SWAP6 - // stack: h_6', h_0'', h_1'', h_2'', h_3'', h_4'', h_5'', h_7', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_6'', h_0'', h_1'', h_2'', h_3'', h_4'', h_5'', h_7', cur_block + 1, retdest - SWAP7 - // stack: h_7', h_0'', h_1'', h_2'', h_3'', h_4'', h_5'', h_6'', cur_block + 1, retdest - %reverse_bytes_u64 - // stack: h_7'', h_0'', h_1'', h_2'', h_3'', h_4'', h_5'', h_6'', cur_block + 1, retdest - %stack (h_7, h_s: 7) -> (h_s, h_7) - // stack: h_0'', h_1'', h_2'', h_3'', h_4'', h_5'', h_6'', h_7'', cur_block + 1, retdest - - // Combine hash values. - %u64s_to_u256 - // stack: h_0'' || h_1'' || h_2'' || h_3'', h_4'', h_5'', h_6'', h_7'', cur_block + 1, retdest - %stack (first, second: 4, cur) -> (second, first) - // stack: h_4'', h_5'', h_6'', h_7'', h_0'' || h_1'' || h_2'' || h_3'', retdest - %u64s_to_u256 - // stack: hash_second = h_4'' || h_5'' || h_6'' || h_7'', hash_first = h_0'' || h_1'' || h_2'' || h_3'', retdest - %stack (second, first, ret) -> (ret, second, first) - // stack: retdest, hash_first, hash_second - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm b/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm deleted file mode 100644 index d521da6d80..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/g_functions.asm +++ /dev/null @@ -1,175 +0,0 @@ -%macro blake2_g_function - // Function to mix two input words, x and y, into the four words indexed by a, b, c, d (which - // are in the range 0..16) in the internal state. - // The internal state is stored in memory starting at the address start. - // stack: a, b, c, d, x, y, start - DUP4 - DUP4 - DUP4 - DUP4 - // stack: a, b, c, d, a, b, c, d, x, y, start - DUP11 - // stack: start, a, b, c, d, a, b, c, d, x, y, start - ADD - MLOAD_GENERAL - // stack: v[a], b, c, d, a, b, c, d, x, y, start - SWAP1 - // stack: b, v[a], c, d, a, b, c, d, x, y, start - DUP11 - // stack: start, b, v[a], c, d, a, b, c, d, x, y, start - ADD - MLOAD_GENERAL - // stack: v[b], v[a], c, d, a, b, c, d, x, y, start - SWAP2 - // stack: c, v[a], v[b], d, a, b, c, d, x, y, start - DUP11 - // stack: start, c, v[a], v[b], d, a, b, c, d, x, y, start - ADD - MLOAD_GENERAL - // stack: v[c], v[a], v[b], d, a, b, c, d, x, y, start - SWAP3 - // stack: d, v[a], v[b], v[c], a, b, c, d, x, y, start - DUP11 - // stack: start, d, v[a], v[b], v[c], a, b, c, d, x, y, start - ADD - MLOAD_GENERAL - // stack: v[d], v[a], v[b], v[c], a, b, c, d, x, y, start - %stack (vd, vs: 3) -> (vs, vd) - // stack: v[a], v[b], v[c], v[d], a, b, c, d, x, y, start - DUP2 - // stack: v[b], v[a], v[b], v[c], v[d], a, b, c, d, x, y, start - DUP10 - // stack: x, v[b], v[a], v[b], v[c], v[d], a, b, c, d, x, y, start - ADD - ADD - %as_u64 - // stack: v[a]' = (v[a] + v[b] + x) % 2^64, v[b], v[c], v[d], a, b, c, d, x, y, start - %stack (a, b, c, d) -> (a, d, a, b, c, d) - // stack: v[a]', v[d], v[a]', v[b], v[c], v[d], a, b, c, d, x, y, start - XOR - %rotr_64(32) - // stack: v[d]' = (v[d] ^ v[a]') >>> 32, v[a]', v[b], v[c], v[d], a, b, c, d, x, y, start - %stack (top: 4, vd) -> (top) - // stack: v[d]', v[a]', v[b], v[c], a, b, c, d, x, y, start - %stack (d, a, b, c) -> (c, d, a, b, d) - // stack: v[c], v[d]', v[a]', v[b], v[d]', a, b, c, d, x, y, start - ADD - %as_u64 - // stack: v[c]' = (v[c] + v[d]') % 2^64, v[a]', v[b], v[d]', a, b, c, d, x, y, start - %stack (c, a, b, d) -> (b, c, a, c, d) - // stack: v[b], v[c]', v[a]', v[c]', v[d]', a, b, c, d, x, y, start - XOR - %rotr_64(24) - // stack: v[b]' = (v[b] ^ v[c]') >>> 24, v[a]', v[c]', v[d]', a, b, c, d, x, y, start - SWAP1 - // stack: v[a]', v[b]', v[c]', v[d]', a, b, c, d, x, y, start - DUP2 - // stack: v[b]', v[a]', v[b]', v[c]', v[d]', a, b, c, d, x, y, start - DUP11 - // stack: y, v[b]', v[a]', v[b]', v[c]', v[d]', a, b, c, d, x, y, start - ADD - ADD - %as_u64 - // stack: v[a]'' = (v[a]' + v[b]' + y) % 2^64, v[b]', v[c]', v[d]', a, b, c, d, x, y, start - SWAP3 - // stack: v[d]', v[b]', v[c]', v[a]'', a, b, c, d, x, y, start - DUP4 - // stack: v[a]'', v[d]', v[b]', v[c]', v[a]'', a, b, c, d, x, y, start - XOR - %rotr_64(16) - // stack: v[d]'' = (v[a]'' ^ v[d]') >>> 8, v[b]', v[c]', v[a]'', a, b, c, d, x, y, start - SWAP2 - // stack: v[c]', v[b]', v[d]'', v[a]'', a, b, c, d, x, y, start - DUP3 - // stack: v[d]'', v[c]', v[b]', v[d]'', v[a]'', a, b, c, d, x, y, start - ADD - %as_u64 - // stack: v[c]'' = (v[c]' + v[d]'') % 2^64, v[b]', v[d]'', v[a]'', a, b, c, d, x, y, start - DUP1 - // stack: v[c]'', v[c]'', v[b]', v[d]'', v[a]'', a, b, c, d, x, y, start - SWAP2 - // stack: v[b]', v[c]'', v[c]'', v[d]'', v[a]'', a, b, c, d, x, y, start - XOR - %rotr_64(63) - // stack: v[b]'' = (v[b]' ^ v[c]'') >>> 7, v[c]'', v[d]'', v[a]'', a, b, c, d, x, y, start - %stack (vb, vc, vd, va, a, b, c, d, x, y, start) -> (start, a, va, start, b, vb, start, c, vc, start, d, vd) - // stack: start, a, v[a]'', start, b, v[b]'', start, c, v[c]'', start, d, v[d]'' - ADD - %swap_mstore - ADD - %swap_mstore - ADD - %swap_mstore - ADD - %swap_mstore -%endmacro - -%macro call_blake2_g_function(a, b, c, d, x_idx, y_idx) - // stack: round, start - PUSH $y_idx - DUP2 - // stack: round, y_idx, round, start - %blake2_permutation - // stack: s[y_idx], round, start - %blake2_message_addr - ADD - MLOAD_GENERAL - // stack: m[s[y_idx]], round, start - PUSH $x_idx - DUP3 - // stack: round, 2, m[s[y_idx]], round, start - %blake2_permutation - // stack: s[x_idx], m[s[y_idx]], round, start - %blake2_message_addr - ADD - MLOAD_GENERAL - // stack: m[s[x_idx]], m[s[y_idx]], round, start - %stack (ss: 2, r, s) -> (ss, s, r, s) - // stack: m[s[x_idx]], m[s[y_idx]], start, round, start - PUSH $d - PUSH $c - PUSH $b - PUSH $a - // stack: a, b, c, d, m[s[x_idx]], m[s[y_idx]], start, round, start - %blake2_g_function - // stack: round, start -%endmacro - -run_g_function_round: - // stack: round, start, retdest - %call_blake2_g_function(0, 4, 8, 12, 0, 1) - %call_blake2_g_function(1, 5, 9, 13, 2, 3) - %call_blake2_g_function(2, 6, 10, 14, 4, 5) - %call_blake2_g_function(3, 7, 11, 15, 6, 7) - %call_blake2_g_function(0, 5, 10, 15, 8, 9) - %call_blake2_g_function(1, 6, 11, 12, 10, 11) - %call_blake2_g_function(2, 7, 8, 13, 12, 13) - %call_blake2_g_function(3, 4, 9, 14, 14, 15) - %stack (r, s, ret) -> (ret, r, s) - // stack: retdest, round, start - JUMP - -global run_rounds_g_function: - // stack: current_round, start, rounds, retdest - DUP3 - // stack: rounds, current_round, start, rounds, retdest - DUP2 - // stack: current_round, rounds, current_round, start, rounds, retdest - EQ - %jumpi(run_rounds_g_function_end) - // stack: current_round, start, rounds, retdest - PUSH run_rounds_g_function_return - // stack: run_rounds_g_function_return, current_round, start, rounds, retdest - %stack (ret, r, s) -> (r, s, ret) - // stack: current_round, start, run_rounds_g_function_return, rounds, retdest - %jump(run_g_function_round) -run_rounds_g_function_return: - // stack: round, start, rounds, retdest - %increment - // stack: round + 1, start, rounds, retdest - %jump(run_rounds_g_function) -run_rounds_g_function_end: - // stack: current_round, start, rounds, retdest - %pop3 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/blake2/hash.asm b/evm/src/cpu/kernel/asm/hash/blake2/hash.asm deleted file mode 100644 index ab0d247633..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/hash.asm +++ /dev/null @@ -1,55 +0,0 @@ -// Generate a new hash value from the previous hash value and two elements of the internal state. -blake2_generate_new_hash_value: - // stack: i, retdest - %blake2_hash_value_addr - // stack: addr, i, retdest - DUP2 - ADD - MLOAD_GENERAL - // stack: h_i, i, retdest - %blake2_internal_state_addr - // stack: addr, h_i, i, retdest - DUP3 - ADD - MLOAD_GENERAL - // stack: v_i, h_i, i, retdest - %blake2_internal_state_addr - // stack: addr, v_i, h_i, i, retdest - SWAP1 - // stack: v_i, addr, h_i, i, retdest - SWAP3 - // stack: i, addr, h_i, v_i, retdest - ADD - %add_const(8) - MLOAD_GENERAL - // stack: v_(i+8), h_i, v_i, retdest - XOR - XOR - // stack: h_i' = v_(i+8) ^ v_i ^ h_i, retdest - SWAP1 - JUMP - -global blake2_generate_all_hash_values: - // stack: retdest - PUSH 8 - // stack: i=8, retdest -blake2_generate_hash_loop: - // stack: i, h_i', ..., h_7', retdest - %decrement - // stack: i-1, h_i', ..., h_7', retdest - PUSH blake2_generate_hash_return - // stack: blake2_generate_hash_return, i-1, h_i', ..., h_7', retdest - DUP2 - // stack: i-1, blake2_generate_hash_return, i-1, h_i', ..., h_7', retdest - %jump(blake2_generate_new_hash_value) -blake2_generate_hash_return: - // stack: h_(i-1)', i-1, h_i', ..., h_7', retdest - SWAP1 - // stack: i-1, h_(i-1)', h_i', ..., h_7', retdest - DUP1 - // stack: i-1, i-1, h_(i-1)', ..., h_7', retdest - %jumpi(blake2_generate_hash_loop) - // stack: i-1=0, h_0', ..., h_7', retdest - %stack (i, h: 8, ret) -> (ret, h) - // stack: retdest, h_0'...h_7' - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/blake2/iv.asm b/evm/src/cpu/kernel/asm/hash/blake2/iv.asm deleted file mode 100644 index 72058ae4ad..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/iv.asm +++ /dev/null @@ -1,95 +0,0 @@ -global blake2_iv_const: - // IV constants (big-endian) - - // IV_0 - BYTES 106, 9, 230, 103 - BYTES 243, 188, 201, 8 - - // IV_1 - BYTES 187, 103, 174, 133 - BYTES 132, 202, 167, 59 - - // IV_2 - BYTES 60, 110, 243, 114 - BYTES 254, 148, 248, 43 - - // IV_3 - BYTES 165, 79, 245, 58 - BYTES 95, 29, 54, 241 - - // IV_4 - BYTES 81, 14, 82, 127 - BYTES 173, 230, 130, 209 - - // IV_5 - BYTES 155, 5, 104, 140 - BYTES 43, 62, 108, 31 - - // IV_6 - BYTES 31, 131, 217, 171 - BYTES 251, 65, 189, 107 - - // IV_7 - BYTES 91, 224, 205, 25 - BYTES 19, 126, 33, 121 - -global blake2_iv: - // stack: i, retdest - PUSH blake2_iv_const - // stack: blake2_iv_const, i, retdest - SWAP1 - // stack: i, blake2_iv_const, retdest - %mul_const(8) - ADD - // stack: blake2_iv_const + 2 * i, retdest - DUP1 - // stack: blake2_iv_const + 2 * i, blake2_iv_const + 2 * i, retdest - %add_const(4) - // stack: blake2_iv_const + 2 * i + 1, blake2_iv_const + 2 * i, retdest - %mload_kernel_code_u32 - SWAP1 - %mload_kernel_code_u32 - // stack: IV_i[32:], IV_i[:32], retdest - %shl_const(32) - // stack: IV_i[32:] << 32, IV_i[:32], retdest - ADD // OR - // stack: IV_i, retdest - SWAP1 - JUMP - -%macro blake2_iv - %stack (i) -> (i, %%after) - %jump(blake2_iv) -%%after: -%endmacro - -// Load the initial hash value (the IV, but with params XOR'd into the first word). -global blake2_initial_hash_value: - // stack: retdest - PUSH 8 - // stack: i=8, retdest -blake2_initial_hash_loop: - // stack: i, IV_i, ..., IV_7, retdest - %decrement - // stack: i-1, IV_i, ..., IV_7, retdest - PUSH blake2_initial_hash_return - // stack: blake2_initial_hash_return, i-1, IV_i, ..., IV_7, retdest - DUP2 - // stack: i-1, blake2_initial_hash_return, i-1, IV_i, ..., IV_7, retdest - %jump(blake2_iv) -blake2_initial_hash_return: - // stack: IV_(i-1), i-1, IV_i, ..., IV_7, retdest - SWAP1 - // stack: i-1, IV_(i-1), IV_i, ..., IV_7, retdest - DUP1 - // stack: i-1, i-1, IV_(i-1), ..., IV_7, retdest - %jumpi(blake2_initial_hash_loop) - // stack: i-1=0, IV_0, ..., IV_7, retdest - POP - // stack: IV_0, ..., IV_7, retdest - PUSH 0x01010040 // params: key = 00, digest_size = 64 = 0x40 - XOR - // stack: IV_0 ^ params, IV_1, IV_2, IV_3, IV_4, IV_5, IV_6, IV_7, retdest - %stack(iv: 8, ret) -> (ret, iv) - JUMP - diff --git a/evm/src/cpu/kernel/asm/hash/blake2/ops.asm b/evm/src/cpu/kernel/asm/hash/blake2/ops.asm deleted file mode 100644 index 2b40db7f66..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/ops.asm +++ /dev/null @@ -1,21 +0,0 @@ -// 64-bit right rotation -%macro rotr_64(rot) - // stack: value - PUSH $rot - // stack: rot, value - DUP2 - DUP2 - // stack: rot, value, rot, value - SHR - // stack: value >> rot, rot, value - %stack (shifted, rot, value) -> (rot, value, shifted) - // stack: rot, value, value >> rot - PUSH 64 - SUB - // stack: 64 - rot, value, value >> rot - SHL - // stack: value << (64 - rot), value >> rot - %as_u64 - // stack: (value << (64 - rot)) % (1 << 64), value >> rot - ADD -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/blake2/permutations.asm b/evm/src/cpu/kernel/asm/hash/blake2/permutations.asm deleted file mode 100644 index 44070b7ae6..0000000000 --- a/evm/src/cpu/kernel/asm/hash/blake2/permutations.asm +++ /dev/null @@ -1,85 +0,0 @@ -global permutation_0_constants: - BYTES 0, 1, 2, 3 - BYTES 4, 5, 6, 7 - BYTES 8, 9, 10, 11 - BYTES 12, 13, 14, 15 - -global permutation_1_constants: - BYTES 14, 10, 4, 8 - BYTES 9, 15, 13, 6 - BYTES 1, 12, 0, 2 - BYTES 11, 7, 5, 3 - -global permutation_2_constants: - BYTES 11, 8, 12, 0 - BYTES 5, 2, 15, 13 - BYTES 10, 14, 3, 6 - BYTES 7, 1, 9, 4 - -global permutation_3_constants: - BYTES 7, 9, 3, 1 - BYTES 13, 12, 11, 14 - BYTES 2, 6, 5, 10 - BYTES 4, 0, 15, 8 - -global permutation_4_constants: - BYTES 9, 0, 5, 7 - BYTES 2, 4, 10, 15 - BYTES 14, 1, 11, 12 - BYTES 6, 8, 3, 13 - -global permutation_5_constants: - BYTES 2, 12, 6, 10 - BYTES 0, 11, 8, 3 - BYTES 4, 13, 7, 5 - BYTES 15, 14, 1, 9 - -global permutation_6_constants: - BYTES 12, 5, 1, 15 - BYTES 14, 13, 4, 10 - BYTES 0, 7, 6, 3 - BYTES 9, 2, 8, 11 - -global permutation_7_constants: - BYTES 13, 11, 7, 14 - BYTES 12, 1, 3, 9 - BYTES 5, 0, 15, 4 - BYTES 8, 6, 2, 10 - -global permutation_8_constants: - BYTES 6, 15, 14, 9 - BYTES 11, 3, 0, 8 - BYTES 12, 2, 13, 7 - BYTES 1, 4, 10, 5 - -global permutation_9_constants: - BYTES 10, 2, 8, 4 - BYTES 7, 6, 1, 5 - BYTES 15, 11, 9, 14 - BYTES 3, 12, 13, 0 - -global blake2_permutation: - // stack: i, round, retdest - PUSH permutation_0_constants - // stack: permutation_0_constants, i, round, retdest - SWAP2 - // stack: round, i, permutation_0_constants, retdest - %mod_const(10) - // stack: round % 10, i, permutation_0_constants, retdest - %mul_const(16) - ADD - ADD - %mload_kernel_code - // stack: permutation_(round%10)_constants[i], retdest - SWAP1 - JUMP - -%macro blake2_permutation - // stack: round, i - PUSH %%after - // stack: %%after, round, i - SWAP2 - // stack: i, round, %%after - %jump(blake2_permutation) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/box.asm b/evm/src/cpu/kernel/asm/hash/ripemd/box.asm deleted file mode 100644 index 6cb16c6e8a..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/box.asm +++ /dev/null @@ -1,96 +0,0 @@ -/// Note that we unpack STATE: 5 to a, b, c, d, e -/// All additions are u32 -/// -/// def box(a, b, c, d, e, F, K): -/// -/// box = get_box(sides, rounds, boxes) -/// a += F(b, c, d) -/// r = load(r)(box) -/// x = load_offset(r) -/// a += x + K -/// s = load(s)(box) -/// a = rol(s, a) -/// a += e -/// c = rol(10, c) -/// -/// return e, a, b, c, d, F, K - -global box: - // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt - PUSH pre_rol - DUP5 - DUP5 - DUP5 - DUP10 - // stack: F, b, c, d, pre_rol, a, b, c, d, e, F, K, boxes, rounds, sides, virt - JUMP -pre_rol: - // stack: F(b, c, d), a, b, c, d, e, F, K, boxes, rounds, sides, virt - ADD - // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt - %get_box - // stack: box, a, b, c, d, e, F, K, boxes, rounds, sides, virt - DUP12 - DUP2 - %mload_kernel_code(r_data) - ADD - // stack: virt + r, box, a, b, c, d, e, F, K, boxes, rounds, sides, virt - %mload_current_general_u32_LE - // stack: x, box, a, b, c, d, e, F, K, boxes, rounds, sides, virt - SWAP1 - SWAP2 - // stack: a, x, box, b, c, d, e, F, K, boxes, rounds, sides, virt - ADD - DUP8 - ADD - %as_u32 - // stack: a, box, b, c, d, e, F, K, boxes, rounds, sides, virt - PUSH mid_rol - SWAP2 - // stack: box, a, mid_rol, b, c, d, e, F, K, boxes, rounds, sides, virt - %mload_kernel_code(s_data) - // stack: s, a, mid_rol, b, c, d, e, F, K, boxes, rounds, sides, virt - %jump(rol) -mid_rol: - // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt - DUP5 - // stack: e, a, b, c, d, e, F, K, boxes, rounds, sides, virt - ADD - %as_u32 - // stack: a, b, c, d, e, F, K, boxes, rounds, sides, virt - %stack (a, b, c) -> (10, c, post_rol, a, b) - // stack: 10, c, post_rol, a, b, d, e, F, K, boxes, rounds, sides, virt - %jump(rol) -post_rol: - // stack: c, a, b, d, e, F, K, boxes , rounds, sides, virt - %stack (c, a, b, d, e, F, K, boxes) -> (boxes, 1, a, b, c, d, F, K, e) - // stack: boxes, 1, a, b, c, d, F, K, e, rounds, sides, virt - SUB - SWAP7 - // stack: e, a, b, c, d, F, K, boxes-1, rounds, sides, virt - %jump(round) - - -%macro get_round - // stack: sides , rounds - %mul_const(5) - PUSH 10 - SUB - SUB - // stack: 10 - 5*sides - rounds -%endmacro - -%macro get_box - // stack: ARGS: 7, boxes, rounds, sides - DUP10 - %mul_const(80) - DUP10 - %mul_const(16) - DUP10 - // stack: boxes , 16*rounds , 80*sides, ARGS: 7, boxes, rounds, sides - PUSH 176 - SUB - SUB - SUB - // stack: 176 - boxes - 16*rounds - 80*sides, ARGS: 7, boxes, rounds, sides -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/compression.asm b/evm/src/cpu/kernel/asm/hash/ripemd/compression.asm deleted file mode 100644 index a83bf8322a..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/compression.asm +++ /dev/null @@ -1,160 +0,0 @@ -/// _block is stored in memory: its address virt stays on the stack -/// def compress(STATE: 5, _block): -/// -/// STATEL = STATE -/// STATEL = loop(STATEL) -/// -/// STATER = state -/// STATER = loop(STATER) -/// -/// return mix(STATER, STATEL, STATE) -/// -/// -/// def mix(STATER, STATEL, STATE): -/// return -/// u32(s1 + l2 + r3), -/// u32(s2 + l3 + r4), -/// u32(s3 + l4 + r0), -/// u32(s4 + l0 + r1), -/// u32(s0 + l1 + r2) -/// -/// where si, li, ri, oi, VR, RD respectively denote -/// STATE[i], STATEL[i], STATER[i], OUTPUT[i], virt, retdest - -global compress: - // stack: STATE, virt, retdest - PUSH switch - DUP7 - %stack () -> (0, 0, 16, 5, 1) - // stack: 0, 0, 16, 5, 1, virt, switch, STATE, virt, retdest - DUP12 - DUP12 - DUP12 - DUP12 - DUP12 - // stack: STATE, 0, 0, 16, 5, 1, virt, switch, STATE, virt, retdest - %jump(loop) -switch: - // stack: STATEL, STATE, virt, retdest - PUSH mix - DUP12 - %stack () -> (16, 5, 0) - // stack: 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest - DUP15 - DUP15 - DUP15 - DUP15 - DUP15 - // stack: STATE, 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest - %stack (STATE: 5) -> (STATE, 0, 0) - // stack: STATE, 0, 0, 16, 5, 0, virt, mix, STATEL, STATE, virt, retdest - %jump(loop) -mix: - // stack: r0, r1, r2, r3, r4, l0, l1, l2, l3, l4, s0, s1, s2, s3, s4, VR, RD - SWAP10 - // stack: s0, r1, r2, r3, r4, l0, l1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD - SWAP1 - // stack: r1, s0, r2, r3, r4, l0, l1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD - SWAP6 - // stack: l1, s0, r2, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD - %add3_u32 - // stack: o4, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, RD - SWAP14 - // stack: RD, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, s3, s4, VR, o4 - SWAP11 - // stack: s3, r3, r4, l0, r1, l2, l3, l4, r0, s1, s2, RD, s4, VR, o4 - SWAP10 - // stack: s2, r3, r4, l0, r1, l2, l3, l4, r0, s1, s3, RD, s4, VR, o4 - SWAP1 - // stack: r3, s2, r4, l0, r1, l2, l3, l4, r0, s1, s3, RD, s4, VR, o4 - SWAP6 - // stack: l3, s2, r4, l0, r1, l2, r3, l4, r0, s1, s3, RD, s4, VR, o4 - %add3_u32 - // stack: o1, l0, r1, l2, r3, l4, r0, s1, s3, RD, s4, VR, o4 - SWAP9 - // stack: RD, l0, r1, l2, r3, l4, r0, s1, s3, o1, s4, VR, o4 - SWAP10 - // stack: s4, l0, r1, l2, r3, l4, r0, s1, s3, o1, RD, VR, o4 - %add3_u32 - // stack: o3, l2, r3, l4, r0, s1, s3, o1, RD, VR, o4 - SWAP9 - // stack: VR, l2, r3, l4, r0, s1, s3, o1, RD, o3, o4 - SWAP5 - // stack: s1, l2, r3, l4, r0, VR, s3, o1, RD, o3, o4 - %add3_u32 - // stack: o0, l4, r0, VR, s3, o1, RD, o3, o4 - SWAP4 - // stack: s3, l4, r0, VR, o0, o1, RD, o3, o4 - %add3_u32 - // stack: o2, VR, o0, o1, RD, o3, o4 - SWAP4 - // stack: RD, VR, o0, o1, o2, o3, o4 - SWAP1 - // stack: VR, RD, o0, o1, o2, o3, o4 - POP - // stack: RD, o0, o1, o2, o3, o4 - JUMP - - -/// def loop(STATE: 5): -/// while rounds: -/// update_round_vars() -/// round(STATE: 5, F, K, rounds, sides) -/// -/// def update_round_vars(): -/// F = load(F)(sides, rounds) -/// K = load(K)(sides, rounds) -/// -/// def round(STATE, rounds, sides): -/// while boxes: -/// box(STATE, F, K) -/// boxes -= 1 -/// boxes = 16 -/// rounds -= 1 - -loop: - // stack: STATE, F, K, 16, rounds, sides, virt, retdest - DUP9 - // stack: round, STATE, F, K, 16, rounds, sides, virt, retdest - %jumpi(update_round_vars) - // stack: STATE, F, K, 16, 0, sides, virt, retdest - %stack (STATE: 5, F, K, boxes, rounds, sides, virt, retdest) -> (retdest, STATE) - // stack: retdest, STATE - JUMP -update_round_vars: - // stack: STATE, F , K , 16, rounds, sides, virt, retdest - DUP9 - DUP11 - %get_round - DUP1 - // stack: rnd, rnd, STATE, F , K , 16, rounds, sides, virt, retdest - SWAP7 - POP - %push_f - SWAP7 - // stack: rnd, rnd, STATE, F', K , 16, rounds, sides, virt, retdest - SWAP8 - POP - %mload_kernel_code_u32(k_data) - SWAP7 - POP - // stack: STATE, F', K', 16, rounds, sides, virt, retdest - %jump(round) -global round: - // stack: STATE, F, K, boxes, rounds , sides, virt, retdest - DUP8 - // stack: boxes, STATE, F, K, boxes, rounds , sides, virt, retdest - %jumpi(box) - // stack: STATE, F, K, 0, rounds , sides, virt, retdest - SWAP7 - POP - PUSH 16 - SWAP7 - // stack: STATE, F, K, 16, rounds , sides, virt, retdest - PUSH 1 - DUP10 - SUB - SWAP9 - POP - // stack: STATE, F, K, 16, rounds-1, sides, virt, retdest - %jump(loop) diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/constants.asm b/evm/src/cpu/kernel/asm/hash/ripemd/constants.asm deleted file mode 100644 index 7a8959feda..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/constants.asm +++ /dev/null @@ -1,117 +0,0 @@ -global k_data: - // Left - BYTES 0x00, 0x00, 0x00, 0x00 - BYTES 0x5A, 0x82, 0x79, 0x99 - BYTES 0x6E, 0xD9, 0xEB, 0xA1 - BYTES 0x8F, 0x1B, 0xBC, 0xDC - BYTES 0xA9, 0x53, 0xFD, 0x4E - // Right - BYTES 0x50, 0xA2, 0x8B, 0xE6 - BYTES 0x5C, 0x4D, 0xD1, 0x24 - BYTES 0x6D, 0x70, 0x3E, 0xF3 - BYTES 0x7A, 0x6D, 0x76, 0xE9 - BYTES 0x00, 0x00, 0x00, 0x00 - -global s_data: - // Left Round 0 - BYTES 11, 14, 15, 12 - BYTES 05, 08, 07, 09 - BYTES 11, 13, 14, 15 - BYTES 06, 07, 09, 08 - // Left Round 1 - BYTES 07, 06, 08, 13 - BYTES 11, 09, 07, 15 - BYTES 07, 12, 15, 09 - BYTES 11, 07, 13, 12 - // Left Round 2 - BYTES 11, 13, 06, 07 - BYTES 14, 09, 13, 15 - BYTES 14, 08, 13, 06 - BYTES 05, 12, 07, 05 - // Left Round 3 - BYTES 11, 12, 14, 15 - BYTES 14, 15, 09, 08 - BYTES 09, 14, 05, 06 - BYTES 08, 06, 05, 12 - // Left Round 4 - BYTES 09, 15, 05, 11 - BYTES 06, 08, 13, 12 - BYTES 05, 12, 13, 14 - BYTES 11, 08, 05, 06 - // Right Round 0 - BYTES 08, 09, 09, 11 - BYTES 13, 15, 15, 05 - BYTES 07, 07, 08, 11 - BYTES 14, 14, 12, 06 - // Right Round 1 - BYTES 09, 13, 15, 07 - BYTES 12, 08, 09, 11 - BYTES 07, 07, 12, 07 - BYTES 06, 15, 13, 11 - // Right Round 2 - BYTES 09, 07, 15, 11 - BYTES 08, 06, 06, 14 - BYTES 12, 13, 05, 14 - BYTES 13, 13, 07, 05 - // Right Round 3 - BYTES 15, 05, 08, 11 - BYTES 14, 14, 06, 14 - BYTES 06, 09, 12, 09 - BYTES 12, 05, 15, 08 - // Right Round 4 - BYTES 08, 05, 12, 09 - BYTES 12, 05, 14, 06 - BYTES 08, 13, 06, 05 - BYTES 15, 13, 11, 11 - -global r_data: - // Left Round 0 - BYTES 00, 04, 08, 12 - BYTES 16, 20, 24, 28 - BYTES 32, 36, 40, 44 - BYTES 48, 52, 56, 60 - // Left Round 1 - BYTES 28, 16, 52, 04 - BYTES 40, 24, 60, 12 - BYTES 48, 00, 36, 20 - BYTES 08, 56, 44, 32 - // Left Round 2 - BYTES 12, 40, 56, 16 - BYTES 36, 60, 32, 04 - BYTES 08, 28, 00, 24 - BYTES 52, 44, 20, 48 - // Left Round 3 - BYTES 04, 36, 44, 40 - BYTES 00, 32, 48, 16 - BYTES 52, 12, 28, 60 - BYTES 56, 20, 24, 08 - // Left Round 4 - BYTES 16, 00, 20, 36 - BYTES 28, 48, 08, 40 - BYTES 56, 04, 12, 32 - BYTES 44, 24, 60, 52 - // Right Round 0 - BYTES 20, 56, 28, 00 - BYTES 36, 08, 44, 16 - BYTES 52, 24, 60, 32 - BYTES 04, 40, 12, 48 - // Right Round 1 - BYTES 24, 44, 12, 28 - BYTES 00, 52, 20, 40 - BYTES 56, 60, 32, 48 - BYTES 16, 36, 04, 08 - // Right Round 2 - BYTES 60, 20, 04, 12 - BYTES 28, 56, 24, 36 - BYTES 44, 32, 48, 08 - BYTES 40, 00, 16, 52 - // Right Round 3 - BYTES 32, 24, 16, 04 - BYTES 12, 44, 60, 00 - BYTES 20, 48, 08, 52 - BYTES 36, 28, 40, 56 - // Right Round 4 - BYTES 48, 60, 40, 16 - BYTES 04, 20, 32, 28 - BYTES 24, 08, 52, 56 - BYTES 00, 12, 36, 44 diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/functions.asm b/evm/src/cpu/kernel/asm/hash/ripemd/functions.asm deleted file mode 100644 index de2fdcf625..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/functions.asm +++ /dev/null @@ -1,150 +0,0 @@ -/// def rol(n, x): -/// return (u32(x << n)) | (x >> (32 - n)) - -global rol: - // stack: n, x, retdest - SWAP1 - DUP1 - DUP3 - // stack: n, x, x, n, retdest - PUSH 32 - SUB - // stack: 32-n, x, x, n, retdest - SHR - // stack: x >> (32-n), x, n, retdest - SWAP2 - // stack: n, x, x >> (32-n), retdest - SHL - // stack: x << n, x >> (32-n), retdest - %as_u32 - // stack: u32(x << n), x >> (32-n), retdest - ADD // OR - // stack: u32(x << n) | (x >> (32-n)), retdest - SWAP1 - JUMP - -// def push_f(rnd): -// Fs = [F0, F1, F2, F3, F4, F4, F3, F2, F1, F0] -// acc = 0 -// for i, F in enumerate(Fs): -// acc += (i==rnd)*F -// return acc, rnd -// -// %this_f(i,F) enacts -// acc += (i==rnd)*F - -%macro push_f - // stack: rnd - PUSH 0 - %this_f(0,F0) - %this_f(1,F1) - %this_f(2,F2) - %this_f(3,F3) - %this_f(4,F4) - %this_f(5,F4) - %this_f(6,F3) - %this_f(7,F2) - %this_f(8,F1) - %this_f(9,F0) - // stack: F, rnd -%endmacro - -%macro this_f(i, F) - // stack: acc, rnd - DUP2 - // stack: rnd , acc, rnd - %eq_const($i) - // stack: rnd==i , acc, rnd - %mul_const($F) - // stack: (rnd==i)*F , acc, rnd - ADD - // stack: (rnd==j)*F + acc, rnd -%endmacro - -/// def F0(x, y, z): -/// return x ^ y ^ z - -global F0: - // stack: x , y , z, retdest - XOR - // stack: x ^ y , z, retdest - XOR - // stack: x ^ y ^ z, retdest - SWAP1 - JUMP - -/// def F1(x, y, z): -/// return (x & y) | (u32(~x) & z) - -global F1: - // stack: x, y, z, retdest - DUP1 - // stack: x, x, y, z, retdest - SWAP2 - // stack: y, x, x, z, retdest - AND - // stack: y & x, x, z, retdest - SWAP2 - // stack: z, x, y & x , retdest - SWAP1 - // stack: x, z, y & x , retdest - %not_u32 - // stack: ~x, z, y & x , retdest - AND - // stack: ~x & z , y & x , retdest - OR - // stack: (~x & z) | (y & x), retdest - SWAP1 - JUMP - -/// def F2(x, y, z): -/// return (x | u32(~y)) ^ z - -global F2: - // stack: x , y, z, retdest - SWAP1 - // stack: y , x, z, retdest - %not_u32 - // stack: ~y , x , z, retdest - OR - // stack: ~y | x , z, retdest - XOR - // stack: (~y | x) ^ z, retdest - SWAP1 - JUMP - -/// def F3(x, y, z): -/// return (x & z) | (u32(~z) & y) - -global F3: - // stack: x, y , z , retdest - DUP3 - // stack: z , x, y , z , retdest - AND - // stack: z & x, y , z , retdest - SWAP2 - // stack: z, y, z & x , retdest - %not_u32 - // stack: ~z , y, z & x , retdest - AND - // stack: ~z & y, z & x , retdest - OR - // stack: (~z & y) | (z & x), retdest - SWAP1 - JUMP - -/// def F4(x, y, z): -/// return x ^ (y | u32(~z)) - -global F4: - // stack: x, y, z, retdest - SWAP2 - // stack: z, y, x, retdest - %not_u32 - // stack: ~z, y, x, retdest - OR - // stack: ~z | y, x, retdest - XOR - // stack: (~z | y) ^ x, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/main.asm b/evm/src/cpu/kernel/asm/hash/ripemd/main.asm deleted file mode 100644 index 19016127f9..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/main.asm +++ /dev/null @@ -1,131 +0,0 @@ -/// Variables beginning with _ are in memory -/// -/// def ripemd160(_input): -/// STATE, count, _buffer = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0], 0, [0]*64 -/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, len(input) , bytes = _input ) -/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, padlength(len(input)), bytes = [0x80]+[0]*63) -/// STATE, count, _buffer = ripemd_update(STATE, count, _buffer, 8, bytes = size(len(_input))) -/// return process(STATE) -/// -/// The hardcoded memory structure, where each register is only a byte, is given as follows -/// { 0-63: buffer, 64-71: bytes(8*len(_input)), 72-135: [0x80]+[0]*63 } -/// -/// ripemd_update receives and return the stack in the form: -/// stack: STATE, count, length, virt -/// where virt is the virtual address of the bytes argument -/// - -global ripemd: - // stack: virt, length - %stack (virt, length) -> (length, 0x80, virt, length) - // stack: length, 0x80, virt, length - - // stack: length - %shl_const(3) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(31, 64) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(30, 65) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(29, 66) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(28, 67) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(27, 68) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(26, 69) - // stack: abcdefgh - DUP1 - %extract_and_store_byte(25, 70) - // stack: abcdefgh - %extract_and_store_byte(24, 71) - - // stack: 0x80 - %mstore_current_general(72) - - // stack: virt, length - %stack (virt, length) -> ( 0, length, virt, ripemd_1, ripemd_2, process) - // stack: count = 0, length, virt, ripemd_1, ripemd_2, process - %stack () -> (0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0) - // stack: STATE, count, length, virt, LABELS - %jump(ripemd_update) - -ripemd_1: - // stack: STATE, count, length , virt , LABELS - DUP7 - // stack: length, STATE, count, length , virt , LABELS - %padlength - // stack: padlength, STATE, count, length , virt , LABELS - SWAP7 - POP - // stack: STATE, count, length = padlength, virt , LABELS - %stack (STATE: 5, count, length, virt) -> (STATE, count, length, 72) - // STATE, count, length , virt = 72, LABELS - %jump(ripemd_update) -ripemd_2: - // stack: STATE, count, length , virt , LABELS - %stack (STATE: 5, count, length, virt) -> (STATE, count, 8, 64) - // stack: STATE, count, length = 8, virt = 64, LABELS - %jump(ripemd_update) -process: - // stack: a , b, c, d, e, count, length, virt - %reverse_bytes_u32 - %shl_const(128) - // stack: a', b, c, d, e, VARS - SWAP1 - %reverse_bytes_u32 - %shl_const(96) - ADD // OR - // stack: b' a', c, d, e, VARS - SWAP1 - %reverse_bytes_u32 - %shl_const(64) - ADD // OR - // stack: c' b' a', d, e, VARS - SWAP1 - %reverse_bytes_u32 - %shl_const(32) - ADD // OR - // stack: d' c' b' a', e, VARS - SWAP1 - %reverse_bytes_u32 - ADD // OR - // stack: e' d' c' b' a', VARS - %stack (result, VARS: 3, retdest) -> (retdest, result) - // stack: 0xdeadbeef, result - JUMP - - -/// def padlength(length): -/// t = length % 64 -/// return 56 + 64*(t > 55) - t - -%macro padlength - // stack: count - %mod_const(64) - // stack: t = count % 64 - PUSH 55 - DUP2 - // stack: t , 55 , t - GT - // stack: t > 55 , t - %mul_const(64) - %add_const(56) - // stack: 56 + 64*(t > 55), t - SUB -%endmacro - -%macro extract_and_store_byte(byte, offset) - // stack: xs - PUSH $byte - BYTE - // stack: xs[byte] - %mstore_current_general($offset) - // stack: -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/ripemd/update.asm b/evm/src/cpu/kernel/asm/hash/ripemd/update.asm deleted file mode 100644 index c5783cc71d..0000000000 --- a/evm/src/cpu/kernel/asm/hash/ripemd/update.asm +++ /dev/null @@ -1,134 +0,0 @@ -/// ripemd_update will receive and return the stack in the form: -/// stack: STATE, count, length, virt -/// -/// def ripemd_update(state, count, buffer, length, bytestring): -/// have = (count // 8) % 64 -/// need = 64 - have -/// shift = 0 -/// P = length >= need and have -/// Q = length >= need -/// if P: -/// update_1() -/// if Q: -/// update_2() -/// R = length > shift -/// if R: -/// buffer_update(virt + shift, have, length - shift) -/// -/// return state, count + 8*length, buffer - -global ripemd_update: - // stack: STATE, count, length, virt, retdest - %stack (STATE: 5, count, length, virt) -> (count, 8, 64, STATE, count, length, virt) - DIV - MOD - // stack: have, STATE, count, length, virt, retdest - DUP1 - PUSH 64 - SUB - PUSH 0 - // stack: shift, need, have, STATE, count, length, virt, retdest - %stack (shift, need, have, STATE: 5, count, length) -> (length, need, STATE, shift, need, have, count, length) - // stack: length, need, STATE, shift, need, have, count, length, virt, retdest - LT - ISZERO - // stack: Q, STATE, shift, need, have, count, length, virt, retdest - %stack (Q, STATE: 5, shift, need, have) -> (have, Q, Q, STATE, shift, need, have) - %gt_const(0) - AND - // stack: P, Q, STATE, shift, need, have, count, length, virt, retdest - %jumpi(update_1) - // stack: Q, STATE, shift, need, have, count, length, virt, retdest - %jumpi(update_2) -final_update: - // stack: STATE, shift, need, have, count, length, virt, retdest - %stack (STATE: 5, shift, need, have, count, length) -> (length, shift, return_step, STATE, shift, need, have, count, length) - SUB - // stack: ARGS: 2, STATE, shift, need, have, count, length, virt, retdest - %stack (ARGS: 2, STATE: 5, shift, need, have, count, length, virt) -> (shift, virt, have, ARGS, STATE, shift, need, have, count, length, virt) - ADD - // stack: ARGS: 4, STATE, shift, need, have, count, length, virt, retdest - %stack (ARGS: 4, STATE: 5, shift, need, have, count, length) -> (length, shift, ARGS, STATE, shift, need, have, count, length) - GT - // stack: R, ARGS: 4, STATE, shift, need, have, count, length, virt, retdest - %jumpi(buffer_update) - // stack: ARGS: 4, STATE, shift, need, have, count, length, virt, retdest - %pop3 - JUMP -return_step: - // stack: STATE, shift, need, have, count, length, virt, retdest - SWAP8 - DUP10 - %mul_const(8) - ADD - SWAP8 - // stack: STATE, shift, need, have, count, length, virt, retdest - %stack (STATE: 5, shift, need, have, count, length, virt, retdest) -> (retdest, STATE, count, length, virt) - JUMP - - -/// def update_1(): -/// buffer_update(virt, have, need) -/// shift = need -/// have = 0 -/// state = compress(state, buffer) - -update_1: - // stack: Q, STATE, shift, need, have, count, length, virt, retdest - %stack (Q, STATE: 5, shift, need, have, count, length, virt) -> (virt, have, need, update_1a, STATE, shift, need, have, count, length, virt) - %jump(buffer_update) -update_1a: - // stack: STATE, shift, need, have, count, length, virt, retdest - %stack (STATE: 5, shift, need, have) -> (STATE, 0, update_2, need, need, 0) - // stack: STATE, 0, update_2, shift = need, need, have = 0, count, length, virt, retdest - %jump(compress) - -/// def update_2(): -/// while length >= shift + 64: -/// shift += 64 -/// state = compress(state, bytestring[shift-64:]) - -update_2: - // stack: STATE, shift, need, have, count, length, virt, retdest - %stack (STATE: 5, shift, need, have, count, length) -> (64, shift, length, STATE, shift, need, have, count, length) - ADD - GT - // stack: cond, STATE, shift, need, have, count, length, virt, retdest - %jumpi(final_update) - SWAP5 - %add_const(64) - SWAP5 - %stack (STATE: 5, shift) -> (shift, 64, STATE, shift) - DUP13 - ADD - SUB - // stack: offset, STATE, shift, need, have, count, length, virt, retdest - %stack (offset, STATE: 5) -> (STATE, offset, update_2) - // stack: STATE, offset, update_2, shift, need, have, count, length, virt, retdest - %jump(compress) - - -/// def buffer_update(get, set, times): -/// for i in range(times): -/// buffer[set+i] = bytestring[get+i] - -buffer_update: - // stack: get , set , times , retdest - DUP2 - DUP2 - // stack: get, set, get , set , times , retdest - %mupdate_current_general - // stack: get , set , times , retdest - %increment - SWAP1 - %increment - SWAP1 - SWAP2 - %decrement - SWAP2 - // stack: get+1, set+1, times-1, retdest - DUP3 - %jumpi(buffer_update) - // stack: get , set , 0 , retdest - %pop3 - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm b/evm/src/cpu/kernel/asm/hash/sha2/compression.asm deleted file mode 100644 index a9467a00bc..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/compression.asm +++ /dev/null @@ -1,159 +0,0 @@ -// We use memory starting at 320 * num_blocks + 2 (after the message schedule -// space) as scratch space to store stack values. -%macro scratch_space_addr_from_num_blocks - // stack: num_blocks - %mul_const(320) - %add_const(2) - %build_current_general_address -%endmacro - -global sha2_compression: - // stack: message_schedule_addr, retdest - // Push the initial hash values; these constants are called H^(0) in the spec. - PUSH 0x1f83d9ab // H^(0)_6 - PUSH 0x9b05688c // H^(0)_5 - PUSH 0x510e527f // H^(0)_4 - PUSH 0xa54ff53a // H^(0)_3 - PUSH 0x3c6ef372 // H^(0)_2 - PUSH 0xbb67ae85 // H^(0)_1 - PUSH 0x6a09e667 // H^(0)_0 - PUSH 0x5be0cd19 // H^(0)_7 - // stack: h[0], a[0], b[0], c[0], d[0], e[0], f[0], g[0], message_schedule_addr, retdest - SWAP8 - // stack: message_schedule_addr, a[0], b[0], c[0], d[0], e[0], f[0], g[0], h[0], retdest - PUSH 0 - // stack: i=0, message_schedule_addr, a[0]..h[0], retdest - SWAP1 - // stack: message_schedule_addr, i=0, a[0]..h[0], retdest - %mload_current_general_no_offset - // stack: num_blocks, message_schedule_addr, i=0, a[0]..h[0], retdest - DUP1 - // stack: num_blocks, num_blocks, message_schedule_addr, i=0, a[0]..h[0], retdest - %scratch_space_addr_from_num_blocks - // stack: scratch_space_addr, num_blocks, message_schedule_addr, i=0, a[0]..h[0], retdest - SWAP1 - // stack: num_blocks, scratch_space_addr, message_schedule_addr, i=0, a[0]..h[0], retdest -compression_start_block: - // We keep the current values of the working variables saved at the end of the stack. - // These are the "initial values" to be added back in at the end of this block. - // stack: num_blocks, scratch_space_addr, message_schedule_addr, i=0, a[0]..h[0], retdest - %rep 8 - DUP12 - %endrep - // stack: a[0], b[0], c[0], d[0], e[0], f[0], g[0], h[0], num_blocks, scratch_space_addr, message_schedule_addr, i=0, a[0]..h[0], retdest -compression_loop: - // Update the eight working variables, using the next constant K[i] and the next message schedule chunk W[i]. - // stack: a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP11 - // stack: message_schedule_addr, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP13 - // stack: i, message_schedule_addr, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %mul_const(4) - // stack: 4*i, message_schedule_addr, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - ADD - // stack: message_schedule_addr + 4*i, a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %mload_u32 - // stack: W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - PUSH sha2_constants_k - // stack: sha2_constants_k, W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP14 - // stack: i, sha2_constants_k, W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %mul_const(4) - // stack: 4*i, sha2_constants_k, W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - ADD - // stack: sha2_constants_k + 4*i, W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %mload_kernel_code_u32 - // stack: K[i], W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP10 - DUP10 - DUP10 - DUP10 - // stack: e[i], f[i], g[i], h[i], K[i], W[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %sha2_temp_word1 - // stack: T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP4 - DUP4 - DUP4 - // stack: a[i], b[i], c[i], T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %sha2_temp_word2 - // stack: T2[i], T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP6 - // stack: d[i], T2[i], T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP3 - // stack: T1[i], d[i], T2[i], T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %add_u32 - // stack: e[i+1]=T1[i]+d[i], T2[i], T1[i], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - SWAP2 - // stack: T2[i], T1[i], e[i+1], a[i], b[i], c[i], d[i], e[i], f[i], g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %add_u32 - // stack: a[i+1]=T1[i]+T2[i], e[i+1], b[i+1]=a[i], c[i+1]=b[i], d[i+1]=c[i], d[i], f[i+1]=e[i], g[i+1]=f[i], h[i+1]=g[i], h[i], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %stack (a, e, b, c, d, old_d, f, g, h, old_h) -> (a, b, c, d, e, f, g, h) - // stack: a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP12 - // stack: i, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %increment - // stack: i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP1 - // stack: i+1, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - %eq_const(64) - // stack: i+1==64, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP1 - // stack: i+1==64, i+1==64, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - DUP12 - // stack: num_blocks, i+1==64, i+1==64, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - SUB - // stack: num_blocks new, i+1==64, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]..h[0], retdest - SWAP13 - // stack: message_schedule_addr, i+1==64, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, num_blocks new, i, a[0]..h[0], retdest - SWAP1 - // stack: i+1==64, message_schedule_addr, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, num_blocks new, i, a[0]..h[0], retdest - %mul_const(256) - // stack: (i+1==64)*256, message_schedule_addr, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, num_blocks new, i, a[0]..h[0], retdest - ADD - // stack: message_schedule_addr new, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, num_blocks new, i, a[0]..h[0], retdest - SWAP12 - // stack: num_blocks new, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks, scratch_space_addr, message_schedule_addr new, i, a[0]..h[0], retdest - SWAP10 - // stack: num_blocks, i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, i, new_a[0]..h[0], retdest - POP - // stack: i+1, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, i, new_a[0]..h[0], retdest - %and_const(63) - // stack: (i+1)%64, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, i, a[0]..h[0], retdest - SWAP12 - // stack: i, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, (i+1)%64, a[0]..h[0], retdest - POP - // stack: a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, (i+1)%64, a[0]..h[0], retdest - DUP12 - // stack: (i+1)%64, a[i+1], b[i+1], c[i+1], d[i+1], e[i+1], f[i+1], g[i+1], h[i+1], num_blocks new, scratch_space_addr, message_schedule_addr new, (i+1)%64, a[0]..h[0], retdest - %jumpi(compression_loop) -compression_end_block: - // Add the initial values of the eight working variables (from the start of this block's compression) back into them. - // stack: a[64], b[64], c[64], d[64], e[64], f[64], g[64], h[64], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0], b[0], c[0], d[0], e[0], f[0], g[0], h[0], retdest - PUSH 0 - // stack: 0, a[64], b[64], c[64], d[64], e[64], f[64], g[64], h[64], num_blocks, scratch_space_addr, message_schedule_addr, i, a[0], b[0], c[0], d[0], e[0], f[0], g[0], h[0], retdest - %rep 8 - SWAP13 - %add_u32 - SWAP12 - %endrep - // stack: 0, num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - POP - // stack: num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - DUP1 - // stack: num_blocks, num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - ISZERO - // In this case, we've finished all the blocks. - %jumpi(compression_end) - // stack: num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - %jump(compression_start_block) -compression_end: - // stack: num_blocks, scratch_space_addr, message_schedule_addr, i, a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - %pop4 - // stack: a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64], retdest - %rep 7 - %shl_const(32) - ADD // OR - %endrep - // stack: sha2_result = concat(a[0]+a[64], b[0]+b[64], c[0]+c[64], d[0]+d[64], e[0]+e[64], f[0]+f[64], g[0]+g[64], h[0]+h[64]), retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/hash/sha2/constants.asm b/evm/src/cpu/kernel/asm/hash/sha2/constants.asm deleted file mode 100644 index 6ce4d907b2..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/constants.asm +++ /dev/null @@ -1,65 +0,0 @@ -global sha2_constants_k: - BYTES 66, 138, 47, 152 - BYTES 113, 55, 68, 145 - BYTES 181, 192, 251, 207 - BYTES 233, 181, 219, 165 - BYTES 57, 86, 194, 91 - BYTES 89, 241, 17, 241 - BYTES 146, 63, 130, 164 - BYTES 171, 28, 94, 213 - BYTES 216, 7, 170, 152 - BYTES 18, 131, 91, 1 - BYTES 36, 49, 133, 190 - BYTES 85, 12, 125, 195 - BYTES 114, 190, 93, 116 - BYTES 128, 222, 177, 254 - BYTES 155, 220, 6, 167 - BYTES 193, 155, 241, 116 - BYTES 228, 155, 105, 193 - BYTES 239, 190, 71, 134 - BYTES 15, 193, 157, 198 - BYTES 36, 12, 161, 204 - BYTES 45, 233, 44, 111 - BYTES 74, 116, 132, 170 - BYTES 92, 176, 169, 220 - BYTES 118, 249, 136, 218 - BYTES 152, 62, 81, 82 - BYTES 168, 49, 198, 109 - BYTES 176, 3, 39, 200 - BYTES 191, 89, 127, 199 - BYTES 198, 224, 11, 243 - BYTES 213, 167, 145, 71 - BYTES 6, 202, 99, 81 - BYTES 20, 41, 41, 103 - BYTES 39, 183, 10, 133 - BYTES 46, 27, 33, 56 - BYTES 77, 44, 109, 252 - BYTES 83, 56, 13, 19 - BYTES 101, 10, 115, 84 - BYTES 118, 106, 10, 187 - BYTES 129, 194, 201, 46 - BYTES 146, 114, 44, 133 - BYTES 162, 191, 232, 161 - BYTES 168, 26, 102, 75 - BYTES 194, 75, 139, 112 - BYTES 199, 108, 81, 163 - BYTES 209, 146, 232, 25 - BYTES 214, 153, 6, 36 - BYTES 244, 14, 53, 133 - BYTES 16, 106, 160, 112 - BYTES 25, 164, 193, 22 - BYTES 30, 55, 108, 8 - BYTES 39, 72, 119, 76 - BYTES 52, 176, 188, 181 - BYTES 57, 28, 12, 179 - BYTES 78, 216, 170, 74 - BYTES 91, 156, 202, 79 - BYTES 104, 46, 111, 243 - BYTES 116, 143, 130, 238 - BYTES 120, 165, 99, 111 - BYTES 132, 200, 120, 20 - BYTES 140, 199, 2, 8 - BYTES 144, 190, 255, 250 - BYTES 164, 80, 108, 235 - BYTES 190, 249, 163, 247 - BYTES 198, 113, 120, 242 diff --git a/evm/src/cpu/kernel/asm/hash/sha2/main.asm b/evm/src/cpu/kernel/asm/hash/sha2/main.asm deleted file mode 100644 index 53967f8a17..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/main.asm +++ /dev/null @@ -1,56 +0,0 @@ -global sha2: - // stack: virt, num_bytes, retdest - %build_current_general_address - // stack: addr, num_bytes, retdest - DUP1 SWAP2 - // stack: num_bytes, addr, addr, retdest - MSTORE_GENERAL - // stack: addr, retdest - - -// Precondition: input is in memory, starting at addr of kernel general segment, of the form -// num_bytes, x[0], x[1], ..., x[num_bytes - 1] -// Postcodition: output is in memory, starting at 0, of the form -// num_blocks, block0[0], ..., block0[63], block1[0], ..., blocklast[63] -global sha2_pad: - // stack: addr, retdest - MLOAD_GENERAL - // stack: num_bytes, retdest - // STEP 1: append 1 - // insert 128 (= 1 << 7) at x[num_bytes+1] - // stack: num_bytes, retdest - PUSH 0x80 - // stack: 128, num_bytes, retdest - DUP2 - // stack: num_bytes, 128, num_bytes, retdest - %increment - // stack: num_bytes+1, 128, num_bytes, retdest - %mstore_current_general - // stack: num_bytes, retdest - // STEP 2: calculate num_blocks := (num_bytes+8)//64 + 1 - DUP1 - // stack: num_bytes, num_bytes, retdest - %add_const(8) - %shr_const(6) - - %increment - // stack: num_blocks = (num_bytes+8)//64 + 1, num_bytes, retdest - // STEP 3: calculate length := num_bytes*8 - SWAP1 - // stack: num_bytes, num_blocks, retdest - %mul_const(8) - // stack: length = num_bytes*8, num_blocks, retdest - // STEP 4: write length to x[num_blocks*64-7..num_blocks*64] - DUP2 - // stack: num_blocks, length, num_blocks, retdest - %mul_const(64) - // stack: last_addr = num_blocks*64, length, num_blocks, retdest - %sha2_write_length - // stack: num_blocks, retdest - DUP1 - // stack: num_blocks, num_blocks, retdest - // STEP 5: write num_blocks to x[0] - %mstore_current_general_no_offset - // stack: num_blocks, retdest - %message_schedule_addr_from_num_blocks - %jump(sha2_gen_all_message_schedules) diff --git a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm b/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm deleted file mode 100644 index 66fa67a9b7..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/message_schedule.asm +++ /dev/null @@ -1,219 +0,0 @@ -// We put the message schedule in memory starting at 64 * num_blocks + 2. -%macro message_schedule_addr_from_num_blocks - // stack: num_blocks - %mul_const(64) - %add_const(2) - %build_current_general_address -%endmacro - -// Precondition: stack contains address of one message block, followed by output address -// Postcondition: 256 bytes starting at given output address contain the 64 32-bit chunks -// of message schedule (in four-byte increments) -gen_message_schedule_from_block: - // stack: block_addr, output_addr, retdest - DUP1 - // stack: block_addr, block_addr, output_addr, retdest - %add_const(32) - // stack: block_addr + 32, block_addr, output_addr, retdest - SWAP1 - // stack: block_addr, block_addr + 32, output_addr, retdest - %mload_u256 - // stack: block[0], block_addr + 32, output_addr, retdest - SWAP1 - // stack: block_addr + 32, block[0], output_addr, retdest - %mload_u256 - // stack: block[1], block[0], output_addr, retdest - SWAP2 - // stack: output_addr, block[0], block[1], retdest - %add_const(28) - PUSH 8 - // stack: counter=8, output_addr + 28, block[0], block[1], retdest -gen_message_schedule_from_block_0_loop: - // Split the first half (256 bits) of the block into the first eight (32-bit) chunks of the message sdchedule. - // stack: counter, output_addr, block[0], block[1], retdest - SWAP2 - // stack: block[0], output_addr, counter, block[1], retdest - DUP1 - // stack: block[0], block[0], output_addr, counter, block[1], retdest - %shr_const(32) - // stack: block[0] >> 32, block[0], output_addr, counter, block[1], retdest - SWAP1 - // stack: block[0], block[0] >> 32, output_addr, counter, block[1], retdest - %as_u32 - // stack: block[0] % (1 << 32), block[0] >> 32, output_addr, counter, block[1], retdest - DUP3 - // stack: output_addr, block[0] % (1 << 32), block[0] >> 32, output_addr, counter, block[1], retdest - %mstore_u32 - // stack: block[0] >> 32, output_addr, counter, block[1], retdest - SWAP1 - // stack: output_addr, block[0] >> 32, counter, block[1], retdest - %sub_const(4) - // stack: output_addr - 4, block[0] >> 32, counter, block[1], retdest - SWAP1 - // stack: block[0] >> 32, output_addr - 4, counter, block[1], retdest - SWAP2 - // stack: counter, output_addr - 4, block[0] >> 32, block[1], retdest - %decrement - DUP1 - %jumpi(gen_message_schedule_from_block_0_loop) -gen_message_schedule_from_block_0_end: - // stack: old counter=0, output_addr, block[0], block[1], retdest - POP - // stack: output_addr, block[0], block[1], retdest - %stack (out, b0, b1) -> (out, 8, b1, b0) - // stack: output_addr, counter=8, block[1], block[0], retdest - %add_const(64) - // stack: output_addr + 64, counter, block[1], block[0], retdest - SWAP1 - // stack: counter, output_addr + 64, block[1], block[0], retdest -gen_message_schedule_from_block_1_loop: - // Split the second half (256 bits) of the block into the next eight (32-bit) chunks of the message sdchedule. - // stack: counter, output_addr, block[1], block[0], retdest - SWAP2 - // stack: block[1], output_addr, counter, block[0], retdest - DUP1 - // stack: block[1], block[1], output_addr, counter, block[0], retdest - %shr_const(32) - // stack: block[1] >> 32, block[1], output_addr, counter, block[0], retdest - SWAP1 - // stack: block[1], block[1] >> 32, output_addr, counter, block[0], retdest - %as_u32 - // stack: block[1] % (1 << 32), block[1] >> 32, output_addr, counter, block[0], retdest - DUP3 - // stack: output_addr, block[1] % (1 << 32), block[1] >> 32, output_addr, counter, block[0], retdest - %mstore_u32 - // stack: block[1] >> 32, output_addr, counter, block[0], retdest - SWAP1 - // stack: output_addr, block[1] >> 32, counter, block[0], retdest - %sub_const(4) - // stack: output_addr - 4, block[1] >> 32, counter, block[0], retdest - SWAP1 - // stack: block[1] >> 32, output_addr - 4, counter, block[0], retdest - SWAP2 - // stack: counter, output_addr - 4, block[1] >> 32, block[0], retdest - %decrement - DUP1 - %jumpi(gen_message_schedule_from_block_1_loop) -gen_message_schedule_from_block_1_end: - // stack: old counter=0, output_addr, block[1], block[0], retdest - POP - // stack: output_addr, block[0], block[1], retdest - PUSH 48 - // stack: counter=48, output_addr, block[0], block[1], retdest - SWAP1 - // stack: output_addr, counter, block[0], block[1], retdest - %add_const(36) - // stack: output_addr + 36, counter, block[0], block[1], retdest - SWAP1 - // stack: counter, output_addr + 36, block[0], block[1], retdest -gen_message_schedule_remaining_loop: - // Generate the next 48 chunks of the message schedule, one at a time, from prior chunks. - // stack: counter, output_addr, block[0], block[1], retdest - SWAP1 - // stack: output_addr, counter, block[0], block[1], retdest - PUSH 8 - DUP2 - // stack: output_addr, 2*4, output_addr, counter, block[0], block[1], retdest - SUB - // stack: output_addr - 2*4, output_addr, counter, block[0], block[1], retdest - %mload_u32 - // stack: x[output_addr - 2*4], output_addr, counter, block[0], block[1], retdest - %sha2_sigma_1 - // stack: sigma_1(x[output_addr - 2*4]), output_addr, counter, block[0], block[1], retdest - SWAP1 - // stack: output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - PUSH 28 - DUP2 - // stack: output_addr, 7*4, output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SUB - // stack: output_addr - 7*4, output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_u32 - // stack: x[output_addr - 7*4], output_addr, sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SWAP1 - // stack: output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - PUSH 60 - DUP2 - // stack: output_addr, 15*4, output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SUB - // stack: output_addr - 15*4, output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_u32 - // stack: x[output_addr - 15*4], output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %sha2_sigma_0 - // stack: sigma_0(x[output_addr - 15*4]), output_addr, x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SWAP1 - // stack: output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - PUSH 64 - DUP2 - // stack: output_addr, 16*4, output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SUB - // stack: output_addr - 16*4, output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - %mload_u32 - // stack: x[output_addr - 16*4], output_addr, sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SWAP1 - // stack: output_addr, x[output_addr - 16*4], sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], sigma_1(x[output_addr - 2*4]), counter, block[0], block[1], retdest - SWAP4 - // stack: sigma_1(x[output_addr - 2*4]), x[output_addr - 16*4], sigma_0(x[output_addr - 15*4]), x[output_addr - 7*4], output_addr, counter, block[0], block[1], retdest - %add_u32 - %add_u32 - %add_u32 - // stack: sigma_1(x[output_addr - 2*4]) + x[output_addr - 16*4] + sigma_0(x[output_addr - 15*4]) + x[output_addr - 7*4], output_addr, counter, block[0], block[1], retdest - DUP2 - // stack: output_addr, sigma_1(x[output_addr - 2*4]) + x[output_addr - 16*4] + sigma_0(x[output_addr - 15*4]) + x[output_addr - 7*4], output_addr, counter, block[0], block[1], retdest - %mstore_u32 - // stack: output_addr, counter, block[0], block[1], retdest - %add_const(4) - // stack: output_addr + 4, counter, block[0], block[1], retdest - SWAP1 - // stack: counter, output_addr + 4, block[0], block[1], retdest - %decrement - // stack: counter - 1, output_addr + 4, block[0], block[1], retdest - DUP1 - %jumpi(gen_message_schedule_remaining_loop) -gen_message_schedule_remaining_end: - // stack: counter=0, output_addr, block[0], block[1], retdest - %pop4 - JUMP - -// Precodition: memory, starting at 0, contains num_blocks, block0[0], ..., block0[63], block1[0], ..., blocklast[63] -// stack contains output_addr -// Postcondition: starting at output_addr, set of 256 bytes per block -// each contains the 64 32-bit chunks of the message schedule for that block (in four-byte increments) -global sha2_gen_all_message_schedules: - // stack: output_addr, retdest - DUP1 - // stack: output_addr, output_addr, retdest - %mload_current_general_no_offset - // stack: num_blocks, output_addr, output_addr, retdest - PUSH 1 - // stack: cur_offset = 1, counter = num_blocks, output_addr, output_addr, retdest - %build_current_general_address - // stack: cur_addr, counter, output_addr, output_addr, retdest -gen_all_message_schedules_loop: - // stack: cur_addr, counter, cur_output_addr, output_addr, retdest - PUSH gen_all_message_schedules_loop_end - // stack: new_retdest = gen_all_message_schedules_loop_end, cur_addr, counter, cur_output_addr, output_addr, retdest - DUP4 - // stack: cur_output_addr, new_retdest, cur_addr, counter, cur_output_addr, output_addr, retdest - DUP3 - // stack: cur_addr, cur_output_addr, new_retdest, cur_addr, counter, cur_output_addr, output_addr, retdest - %jump(gen_message_schedule_from_block) -gen_all_message_schedules_loop_end: - // stack: cur_addr, counter, cur_output_addr, output_addr, retdest - %add_const(64) - // stack: cur_addr + 64, counter, cur_output_addr, output_addr, retdest - SWAP1 - %decrement - SWAP1 - // stack: cur_addr + 64, counter - 1, cur_output_addr, output_addr, retdest - SWAP2 - %add_const(256) - SWAP2 - // stack: cur_addr + 64, counter - 1, cur_output_addr + 256, output_addr, retdest - DUP2 - // stack: counter - 1, cur_addr + 64, counter - 1, cur_output_addr + 256, output_addr, retdest - %jumpi(gen_all_message_schedules_loop) -gen_all_message_schedules_end: - // stack: cur_addr + 64, counter - 1, cur_output_addr + 256, output_addr, retdest - %pop3 - // stack: output_addr, retdest - %jump(sha2_compression) diff --git a/evm/src/cpu/kernel/asm/hash/sha2/ops.asm b/evm/src/cpu/kernel/asm/hash/sha2/ops.asm deleted file mode 100644 index d50e5c9a89..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/ops.asm +++ /dev/null @@ -1,143 +0,0 @@ -// 32-bit right rotation -%macro rotr(rot) - // stack: value - PUSH $rot - // stack: rot, value - DUP2 - DUP2 - // stack: rot, value, rot, value - SHR - // stack: value >> rot, rot, value - %stack (shifted, rot, value) -> (rot, value, shifted) - // stack: rot, value, value >> rot - PUSH 32 - SUB - // stack: 32 - rot, value, value >> rot - SHL - // stack: value << (32 - rot), value >> rot - %as_u32 - // stack: (value << (32 - rot)) % (1 << 32), value >> rot - ADD -%endmacro - -%macro sha2_sigma_0 - // stack: x - DUP1 - // stack: x, x - %rotr(7) - // stack: rotr(x, 7), x - SWAP1 - // stack: x, rotr(x, 7) - DUP1 - // stack: x, x, rotr(x, 7) - %rotr(18) - // stack: rotr(x, 18), x, rotr(x, 7) - SWAP1 - // stack: x, rotr(x, 18), rotr(x, 7) - %shr_const(3) - // stack: shr(x, 3), rotr(x, 18), rotr(x, 7) - XOR - XOR -%endmacro - -%macro sha2_sigma_1 - // stack: x - DUP1 - // stack: x, x - %rotr(17) - // stack: rotr(x, 17), x - SWAP1 - // stack: x, rotr(x, 17) - DUP1 - // stack: x, x, rotr(x, 17) - %rotr(19) - // stack: rotr(x, 19), x, rotr(x, 17) - SWAP1 - // stack: x, rotr(x, 19), rotr(x, 17) - PUSH 10 - SHR - // stack: shr(x, 10), rotr(x, 19), rotr(x, 17) - XOR - XOR -%endmacro - -%macro sha2_bigsigma_0 - // stack: x - DUP1 - // stack: x, x - %rotr(2) - // stack: rotr(x, 2), x - SWAP1 - // stack: x, rotr(x, 2) - DUP1 - // stack: x, x, rotr(x, 2) - %rotr(13) - // stack: rotr(x, 13), x, rotr(x, 2) - SWAP1 - // stack: x, rotr(x, 13), rotr(x, 2) - %rotr(22) - // stack: rotr(x, 22), rotr(x, 13), rotr(x, 2) - XOR - XOR -%endmacro - -%macro sha2_bigsigma_1 - // stack: x - DUP1 - // stack: x, x - %rotr(6) - // stack: rotr(x, 6), x - SWAP1 - // stack: x, rotr(x, 6) - DUP1 - // stack: x, x, rotr(x, 6) - %rotr(11) - // stack: rotr(x, 11), x, rotr(x, 6) - SWAP1 - // stack: x, rotr(x, 11), rotr(x, 6) - %rotr(25) - // stack: rotr(x, 25), rotr(x, 11), rotr(x, 6) - XOR - XOR -%endmacro - -%macro sha2_choice - // stack: x, y, z - DUP1 - // stack: x, x, y, z - NOT - // stack: not x, x, y, z - SWAP1 - // stack: x, not x, y, z - SWAP3 - // stack: z, not x, y, x - AND - // stack: (not x) and z, y, x - SWAP2 - // stack: x, y, (not x) and z - AND - // stack: x and y, (not x) and z - OR -%endmacro - -%macro sha2_majority - // stack: x, y, z - DUP1 - // stack: x, x, y, z - DUP3 - // stack: y, x, x, y, z - DUP5 - // stack: z, y, x, x, y, z - AND - // stack: z and y, x, x, y, z - SWAP4 - // stack: z, x, x, y, z and y - AND - // stack: z and x, x, y, z and y - SWAP2 - // stack: y, x, z and x, z and y - AND - // stack: y and x, z and x, z and y - OR - OR -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/sha2/temp_words.asm b/evm/src/cpu/kernel/asm/hash/sha2/temp_words.asm deleted file mode 100644 index ed610947f2..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/temp_words.asm +++ /dev/null @@ -1,32 +0,0 @@ -// "T_1" in the SHA-256 spec -%macro sha2_temp_word1 - // stack: e, f, g, h, K[i], W[i] - DUP1 - // stack: e, e, f, g, h, K[i], W[i] - %sha2_bigsigma_1 - // stack: Sigma_1(e), e, f, g, h, K[i], W[i] - %stack (sig, e, f, g) -> (e, f, g, sig) - // stack: e, f, g, Sigma_1(e), h, K[i], W[i] - %sha2_choice - // stack: Ch(e, f, g), Sigma_1(e), h, K[i], W[i] - %add_u32 - %add_u32 - %add_u32 - %add_u32 - // stack: Ch(e, f, g) + Sigma_1(e) + h + K[i] + W[i] -%endmacro - -// "T_2" in the SHA-256 spec -%macro sha2_temp_word2 - // stack: a, b, c - DUP1 - // stack: a, a, b, c - %sha2_bigsigma_0 - // stack: Sigma_0(a), a, b, c - SWAP3 - // stack: c, a, b, Sigma_0(a) - %sha2_majority - // stack: Maj(c, a, b), Sigma_0(a) - %add_u32 - // stack: Maj(c, a, b) + Sigma_0(a) -%endmacro diff --git a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm b/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm deleted file mode 100644 index 9c2707b8d1..0000000000 --- a/evm/src/cpu/kernel/asm/hash/sha2/write_length.asm +++ /dev/null @@ -1,35 +0,0 @@ -%macro sha2_write_length - // stack: last_addr_offset, length - %build_current_general_address - SWAP1 - // stack: length, last_addr - DUP1 - // stack: length, length, last_addr - %and_const(0xff) - // stack: length % (1 << 8), length, last_addr - DUP3 - // stack: last_addr, length % (1 << 8), length, last_addr - %swap_mstore - - %rep 7 - // For i = 0 to 6 - // stack: length >> (8 * i), last_addr - i - 1 - SWAP1 - %decrement - SWAP1 - // stack: length >> (8 * i), last_addr - i - 2 - %shr_const(8) - // stack: length >> (8 * (i + 1)), last_addr - i - 2 - PUSH 256 - DUP2 - // stack: length >> (8 * (i + 1)), 256, length >> (8 * (i + 1)), last_addr - i - 2 - MOD - // stack: (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 - DUP3 - // stack: last_addr - i - 2, (length >> (8 * (i + 1))) % (1 << 8), length >> (8 * (i + 1)), last_addr - i - 2 - %swap_mstore - %endrep - - %pop2 - // stack: (empty) -%endmacro diff --git a/evm/src/cpu/kernel/asm/journal/account_created.asm b/evm/src/cpu/kernel/asm/journal/account_created.asm deleted file mode 100644 index 4748d5cbcb..0000000000 --- a/evm/src/cpu/kernel/asm/journal/account_created.asm +++ /dev/null @@ -1,13 +0,0 @@ -// struct AccountCreated { address } - -%macro journal_add_account_created - %journal_add_1(@JOURNAL_ENTRY_ACCOUNT_CREATED) -%endmacro - -global revert_account_created: - // stack: entry_type, ptr, retdest - POP - %journal_load_1 - // stack: address, retdest - %delete_account - JUMP diff --git a/evm/src/cpu/kernel/asm/journal/account_destroyed.asm b/evm/src/cpu/kernel/asm/journal/account_destroyed.asm deleted file mode 100644 index 3806a891dc..0000000000 --- a/evm/src/cpu/kernel/asm/journal/account_destroyed.asm +++ /dev/null @@ -1,32 +0,0 @@ -// struct AccountDestroyed { address, target, prev_balance } - -%macro journal_add_account_destroyed - %journal_add_3(@JOURNAL_ENTRY_ACCOUNT_DESTROYED) -%endmacro - -global revert_account_destroyed: - // stack: entry_type, ptr, retdest - POP - %journal_load_3 - // stack: address, target, prev_balance, retdest - PUSH revert_account_destroyed_contd DUP2 - %jump(remove_selfdestruct_list) -revert_account_destroyed_contd: - // stack: address, target, prev_balance, retdest - SWAP1 - // Remove `prev_balance` from `target`'s balance. - // stack: target, address, prev_balance, retdest - %mpt_read_state_trie - %add_const(1) - // stack: target_balance_ptr, address, prev_balance, retdest - DUP3 - DUP2 %mload_trie_data - // stack: target_balance, prev_balance, target_balance_ptr, address, prev_balance, retdest - SUB SWAP1 %mstore_trie_data - // Set `address`'s balance to `prev_balance`. - // stack: address, prev_balance, retdest - %mpt_read_state_trie - %add_const(1) - %mstore_trie_data - JUMP - diff --git a/evm/src/cpu/kernel/asm/journal/account_loaded.asm b/evm/src/cpu/kernel/asm/journal/account_loaded.asm deleted file mode 100644 index 6c3c4ba045..0000000000 --- a/evm/src/cpu/kernel/asm/journal/account_loaded.asm +++ /dev/null @@ -1,19 +0,0 @@ -// struct AccountLoaded { address } - -%macro journal_add_account_loaded - %journal_add_1(@JOURNAL_ENTRY_ACCOUNT_LOADED) -%endmacro - -global revert_account_loaded: - // stack: entry_type, ptr, retdest - POP - %journal_load_1 - // stack: address, retdest - DUP1 %eq_const(@RIP160) %jumpi(ripemd) - %jump(remove_accessed_addresses) - -// The address 0x3 shouldn't become unloaded. -// See https://github.com/ethereum/EIPs/issues/716. -ripemd: - // stack: address, retdest - POP JUMP diff --git a/evm/src/cpu/kernel/asm/journal/account_touched.asm b/evm/src/cpu/kernel/asm/journal/account_touched.asm deleted file mode 100644 index a5aea2194f..0000000000 --- a/evm/src/cpu/kernel/asm/journal/account_touched.asm +++ /dev/null @@ -1,19 +0,0 @@ -// struct AccountTouched { address } - -%macro journal_add_account_touched - %journal_add_1(@JOURNAL_ENTRY_ACCOUNT_TOUCHED) -%endmacro - -global revert_account_touched: - // stack: entry_type, ptr, retdest - POP - %journal_load_1 - // stack: address, retdest - DUP1 %eq_const(@RIP160) %jumpi(ripemd) - %jump(remove_touched_addresses) - -// The address 0x3 shouldn't become untouched. -// See https://github.com/ethereum/EIPs/issues/716. -ripemd: - // stack: address, retdest - POP JUMP diff --git a/evm/src/cpu/kernel/asm/journal/balance_transfer.asm b/evm/src/cpu/kernel/asm/journal/balance_transfer.asm deleted file mode 100644 index a9a5894133..0000000000 --- a/evm/src/cpu/kernel/asm/journal/balance_transfer.asm +++ /dev/null @@ -1,24 +0,0 @@ -// struct BalanceTransfer { from, to, balance } - -%macro journal_add_balance_transfer - // stack: from, to, balance - DUP3 ISZERO %jumpi(%%zero) - %journal_add_3(@JOURNAL_ENTRY_BALANCE_TRANSFER) - %jump(%%after) -%%zero: - // stack: from, to, balance - %pop3 -%%after: - // stack: (empty) -%endmacro - -global revert_balance_transfer: - // stack: entry_type, ptr, retdest - POP - %journal_load_3 - // stack: from, to, balance, retdest - SWAP1 - // stack: to, from, balance, retdest - %transfer_eth - %jumpi(panic) // This should never happen. - JUMP diff --git a/evm/src/cpu/kernel/asm/journal/code_change.asm b/evm/src/cpu/kernel/asm/journal/code_change.asm deleted file mode 100644 index 5bb637c726..0000000000 --- a/evm/src/cpu/kernel/asm/journal/code_change.asm +++ /dev/null @@ -1,18 +0,0 @@ -// struct CodeChange { address, prev_codehash } - -%macro journal_add_code_change - %journal_add_2(@JOURNAL_ENTRY_CODE_CHANGE) -%endmacro - -global revert_code_change: - // stack: entry_ptr, ptr, retdest - POP - %journal_load_2 - // stack: address, prev_codehash, retdest - %mpt_read_state_trie - // stack: account_ptr, prev_codehash, retdest - %add_const(3) - // stack: codehash_ptr, prev_codehash, retdest - %mstore_trie_data - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/journal/journal.asm b/evm/src/cpu/kernel/asm/journal/journal.asm deleted file mode 100644 index 9ba4350878..0000000000 --- a/evm/src/cpu/kernel/asm/journal/journal.asm +++ /dev/null @@ -1,210 +0,0 @@ -%macro journal_size - %mload_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN) -%endmacro - -%macro mstore_journal - // stack: virtual, value - %mstore_kernel(@SEGMENT_JOURNAL) - // stack: (empty) -%endmacro - -%macro mload_journal - // stack: virtual - %mload_kernel(@SEGMENT_JOURNAL) - // stack: value -%endmacro - -%macro append_journal - // stack: pointer - %journal_size - // stack: journal_size, pointer - SWAP1 DUP2 - // stack: journal_size, pointer, journal_size - %mstore_journal - // stack: journal_size - %increment - %mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN) -%endmacro - -%macro journal_data_size - %mload_global_metadata(@GLOBAL_METADATA_JOURNAL_DATA_LEN) -%endmacro - -%macro mstore_journal_data - // stack: virtual, value - %mstore_kernel(@SEGMENT_JOURNAL_DATA) - // stack: (empty) -%endmacro - -%macro mload_journal_data - // stack: virtual - %mload_kernel(@SEGMENT_JOURNAL_DATA) - // stack: value -%endmacro - -%macro append_journal_data - // stack: value - %journal_data_size - // stack: size, value - SWAP1 DUP2 - // stack: size, value, size - %mstore_journal_data - // stack: size - %increment - %mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_DATA_LEN) -%endmacro - -%macro journal_add_1(type) - // stack: w - %journal_data_size - // stack: ptr, w - PUSH $type %append_journal_data - // stack: ptr, w - SWAP1 - // stack: w, ptr - %append_journal_data - // stack: ptr - %append_journal -%endmacro - -%macro journal_add_2(type) - // stack: w, x - %journal_data_size - // stack: ptr, w, x - PUSH $type %append_journal_data - // stack: ptr, w, x - SWAP1 %append_journal_data - // stack: ptr, x - SWAP1 %append_journal_data - // stack: ptr - %append_journal -%endmacro - -%macro journal_add_3(type) - // stack: w, x, y - %journal_data_size - // stack: ptr, w, x, y - PUSH $type %append_journal_data - // stack: ptr, w, x, y - SWAP1 %append_journal_data - // stack: ptr, x, y - SWAP1 %append_journal_data - // stack: ptr, y - SWAP1 %append_journal_data - // stack: ptr - %append_journal -%endmacro - -%macro journal_add_4(type) - // stack: w, x, y, z - %journal_data_size - // stack: ptr, w, x, y, z - PUSH $type %append_journal_data - // stack: ptr, w, x, y, z - SWAP1 %append_journal_data - // stack: ptr, x, y, z - SWAP1 %append_journal_data - // stack: ptr, y, z - SWAP1 %append_journal_data - // stack: ptr, z - SWAP1 %append_journal_data - // stack: ptr - %append_journal -%endmacro - -%macro journal_load_1 - // ptr - %add_const(1) - %mload_journal_data - // w -%endmacro - -%macro journal_load_2 - // ptr - DUP1 - %add_const(2) - %mload_journal_data - // x, ptr - SWAP1 - %add_const(1) - %mload_journal_data - // w, x -%endmacro - -%macro journal_load_3 - // ptr - DUP1 - %add_const(3) - %mload_journal_data - // y, ptr - SWAP1 - DUP1 - // ptr, ptr, y - %add_const(2) - %mload_journal_data - // x, ptr, y - SWAP1 - %add_const(1) - %mload_journal_data - // w, x, y -%endmacro - -%macro journal_load_4 - // ptr - DUP1 - %add_const(4) - %mload_journal_data - // z, ptr - SWAP1 - DUP1 - // ptr, ptr, z - %add_const(3) - %mload_journal_data - // y, ptr, z - SWAP1 - DUP1 - // ptr, ptr, y, z - %add_const(2) - %mload_journal_data - // x, ptr, y, z - SWAP1 - %add_const(1) - %mload_journal_data - // w, x, y, z -%endmacro - -%macro current_checkpoint - %mload_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT) -%endmacro - - -%macro checkpoint - // stack: (empty) - %current_checkpoint - // stack: current_checkpoint - DUP1 - PUSH @SEGMENT_JOURNAL_CHECKPOINTS - %build_kernel_address - %journal_size - // stack: journal_size, addr, current_checkpoint - MSTORE_GENERAL - // stack: current_checkpoint - %mload_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) - // stack: i, current_checkpoint - DUP2 DUP2 %mstore_current(@SEGMENT_CONTEXT_CHECKPOINTS) - // stack: i, current_checkpoint - %increment - %mstore_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) - // stack: current_checkpoint - %increment - %mstore_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT) - // stack: (empty) -%endmacro - -%macro pop_checkpoint - PUSH 1 - %mload_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) - // stack: i - SUB - %mstore_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) -%endmacro diff --git a/evm/src/cpu/kernel/asm/journal/log.asm b/evm/src/cpu/kernel/asm/journal/log.asm deleted file mode 100644 index e1794397b7..0000000000 --- a/evm/src/cpu/kernel/asm/journal/log.asm +++ /dev/null @@ -1,21 +0,0 @@ -// struct Log { logs_data_len, logs_payload_len } - -%macro journal_add_log - %journal_add_2(@JOURNAL_ENTRY_LOG) -%endmacro - -global revert_log: - // stack: entry_type, ptr, retdest - POP - // First, reduce the number of logs. - PUSH 1 - %mload_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - SUB - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_LEN) - // stack: ptr, retdest - // Second, restore payload length. - %journal_load_2 - // stack: prev_logs_data_len, prev_payload_len, retdest - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_DATA_LEN) - %mstore_global_metadata(@GLOBAL_METADATA_LOGS_PAYLOAD_LEN) - JUMP diff --git a/evm/src/cpu/kernel/asm/journal/nonce_change.asm b/evm/src/cpu/kernel/asm/journal/nonce_change.asm deleted file mode 100644 index 3ab8f13677..0000000000 --- a/evm/src/cpu/kernel/asm/journal/nonce_change.asm +++ /dev/null @@ -1,17 +0,0 @@ -// struct NonceChange { address, prev_nonce } - -%macro journal_add_nonce_change - %journal_add_2(@JOURNAL_ENTRY_NONCE_CHANGE) -%endmacro - -global revert_nonce_change: - // stack: entry_type, ptr, retdest - POP - %journal_load_2 - // stack: address, prev_nonce, retdest - %mpt_read_state_trie - // stack: nonce_ptr, prev_nonce retdest - %mstore_trie_data - // stack: retdest - JUMP - diff --git a/evm/src/cpu/kernel/asm/journal/refund.asm b/evm/src/cpu/kernel/asm/journal/refund.asm deleted file mode 100644 index b0e34cc614..0000000000 --- a/evm/src/cpu/kernel/asm/journal/refund.asm +++ /dev/null @@ -1,15 +0,0 @@ -// struct Refund { amount } - -%macro journal_refund - %journal_add_1(@JOURNAL_ENTRY_REFUND) -%endmacro - -global revert_refund: - // stack: entry_type, ptr, retdest - POP - %journal_load_1 - // stack: amount, retdest - %mload_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) - SUB - %mstore_global_metadata(@GLOBAL_METADATA_REFUND_COUNTER) - JUMP diff --git a/evm/src/cpu/kernel/asm/journal/revert.asm b/evm/src/cpu/kernel/asm/journal/revert.asm deleted file mode 100644 index 857bf612b2..0000000000 --- a/evm/src/cpu/kernel/asm/journal/revert.asm +++ /dev/null @@ -1,91 +0,0 @@ -%macro revert - // stack: journal_size - %decrement - %stack (journal_size_m_1) -> (journal_size_m_1, %%after, journal_size_m_1) - %mload_journal - // stack: ptr, %%after, journal_size-1 - DUP1 %mload_journal_data - // stack: entry_type, ptr, %%after, journal_size-1 - DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_LOADED) %jumpi(revert_account_loaded) - DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_DESTROYED) %jumpi(revert_account_destroyed) - DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_TOUCHED) %jumpi(revert_account_touched) - DUP1 %eq_const(@JOURNAL_ENTRY_BALANCE_TRANSFER) %jumpi(revert_balance_transfer) - DUP1 %eq_const(@JOURNAL_ENTRY_NONCE_CHANGE) %jumpi(revert_nonce_change) - DUP1 %eq_const(@JOURNAL_ENTRY_STORAGE_CHANGE) %jumpi(revert_storage_change) - DUP1 %eq_const(@JOURNAL_ENTRY_STORAGE_LOADED) %jumpi(revert_storage_loaded) - DUP1 %eq_const(@JOURNAL_ENTRY_CODE_CHANGE) %jumpi(revert_code_change) - DUP1 %eq_const(@JOURNAL_ENTRY_REFUND) %jumpi(revert_refund) - DUP1 %eq_const(@JOURNAL_ENTRY_ACCOUNT_CREATED) %jumpi(revert_account_created) - DUP1 %eq_const(@JOURNAL_ENTRY_LOG) %jumpi(revert_log) - PANIC // This should never happen. -%%after: - // stack: journal_size-1 -%endmacro - -global revert_batch: - // stack: target_size, retdest - %journal_size - // stack: journal_size, target_size, retdest - DUP2 DUP2 LT %jumpi(panic) // Sanity check to avoid infinite loop. -while_loop: - // stack: journal_size, target_size, retdest - DUP2 DUP2 EQ %jumpi(revert_batch_done) - // stack: journal_size, target_size, retdest - %revert - // stack: journal_size-1, target_size, retdest - %jump(while_loop) - -revert_batch_done: - // stack: journal_size, target_size, retdest - %mstore_global_metadata(@GLOBAL_METADATA_JOURNAL_LEN) - POP JUMP - -revert_one_checkpoint: - // stack: current_checkpoint, retdest - DUP1 ISZERO %jumpi(first_checkpoint) - // stack: current_checkpoint, retdest - %decrement - // stack: current_checkpoint-1, retdest - DUP1 %mload_kernel(@SEGMENT_JOURNAL_CHECKPOINTS) - // stack: target_size, current_checkpoints-1, retdest - %jump(do_revert) -first_checkpoint: - // stack: current_checkpoint, retdest - %decrement - // stack: current_checkpoint-1, retdest - PUSH 0 - // stack: target_size, current_checkpoints-1, retdest -do_revert: - %stack (target_size, current_checkpoints_m_1, retdest) -> (target_size, after_revert, current_checkpoints_m_1, retdest) - %jump(revert_batch) -after_revert: - // stack: current_checkpoint-1, retdest - SWAP1 JUMP - - -global revert_checkpoint: - // stack: retdest - PUSH 1 %mload_context_metadata(@CTX_METADATA_CHECKPOINTS_LEN) SUB - %mload_current(@SEGMENT_CONTEXT_CHECKPOINTS) - // stack: target_checkpoint, retdest - %current_checkpoint - // stack: current_checkpoint, target_checkpoint, retdest - DUP2 DUP2 LT %jumpi(panic) // Sanity check that current_cp >= target_cp. This should never happen. -while: - // stack: current_checkpoint, target_checkpoint, retdest - DUP2 DUP2 EQ %jumpi(revert_checkpoint_done) - %stack (current_checkpoint) -> (current_checkpoint, while) - %jump(revert_one_checkpoint) -revert_checkpoint_done: - // stack: current_checkpoint, target_checkpoint, retdest - POP - %mstore_global_metadata(@GLOBAL_METADATA_CURRENT_CHECKPOINT) - %pop_checkpoint - JUMP - -%macro revert_checkpoint - PUSH %%after - %jump(revert_checkpoint) -%%after: - // stack: (empty) -%endmacro diff --git a/evm/src/cpu/kernel/asm/journal/storage_change.asm b/evm/src/cpu/kernel/asm/journal/storage_change.asm deleted file mode 100644 index 752674d1e1..0000000000 --- a/evm/src/cpu/kernel/asm/journal/storage_change.asm +++ /dev/null @@ -1,57 +0,0 @@ -// struct StorageChange { address, slot, prev_value } - -%macro journal_add_storage_change - %journal_add_3(@JOURNAL_ENTRY_STORAGE_CHANGE) -%endmacro - -global revert_storage_change: - // stack: entry_type, ptr, retdest - POP - %journal_load_3 - // stack: address, slot, prev_value, retdest - DUP3 ISZERO %jumpi(delete) - // stack: address, slot, prev_value, retdest - SWAP1 %slot_to_storage_key - // stack: storage_key, address, prev_value, retdest - PUSH 64 // storage_key has 64 nibbles - // stack: 64, storage_key, address, prev_value, retdest - DUP3 %mpt_read_state_trie - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr, 64, storage_key, address, prev_value, retdest - %add_const(2) - // stack: storage_root_ptr_ptr, 64, storage_key, address, prev_value, retdest - %mload_trie_data - %get_trie_data_size - DUP6 %append_to_trie_data - %stack (prev_value_ptr, storage_root_ptr, num_nibbles, storage_key, address, prev_value, retdest) -> - (storage_root_ptr, num_nibbles, storage_key, prev_value_ptr, new_storage_root, address, retdest) - %jump(mpt_insert) - -delete: - // stack: address, slot, prev_value, retdest - SWAP2 POP - %stack (slot, address, retdest) -> (slot, new_storage_root, address, retdest) - %slot_to_storage_key - // stack: storage_key, new_storage_root, address, retdest - PUSH 64 // storage_key has 64 nibbles - // stack: 64, storage_key, new_storage_root, address, retdest - DUP4 %mpt_read_state_trie - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr, 64, storage_key, new_storage_root, address, retdest - %add_const(2) - // stack: storage_root_ptr_ptr, 64, storage_key, new_storage_root, address, retdest - %mload_trie_data - // stack: storage_root_ptr, 64, storage_key, new_storage_root, address, retdest - %jump(mpt_delete) - -new_storage_root: - // stack: new_storage_root_ptr, address, retdest - DUP2 %mpt_read_state_trie - // stack: account_ptr, new_storage_root_ptr, address, retdest - - // Update account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, address, retdest - %mstore_trie_data - // stack: address, retdest - POP JUMP diff --git a/evm/src/cpu/kernel/asm/journal/storage_loaded.asm b/evm/src/cpu/kernel/asm/journal/storage_loaded.asm deleted file mode 100644 index 9d1f37453a..0000000000 --- a/evm/src/cpu/kernel/asm/journal/storage_loaded.asm +++ /dev/null @@ -1,12 +0,0 @@ -// struct StorageLoaded { address, slot } - -%macro journal_add_storage_loaded - %journal_add_2(@JOURNAL_ENTRY_STORAGE_LOADED) -%endmacro - -global revert_storage_loaded: - // stack: entry_type, ptr, retdest - POP - %journal_load_2 - // stack: address, slot, retdest - %jump(remove_accessed_storage_keys) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm deleted file mode 100644 index d78152f4be..0000000000 --- a/evm/src/cpu/kernel/asm/main.asm +++ /dev/null @@ -1,96 +0,0 @@ -global main: - // First, hash the kernel code - %mload_global_metadata(@GLOBAL_METADATA_KERNEL_LEN) - PUSH 0 - // stack: addr, len - KECCAK_GENERAL - // stack: hash - %mload_global_metadata(@GLOBAL_METADATA_KERNEL_HASH) - // stack: expected_hash, hash - %assert_eq - - // Initialise the shift table - %shift_table_init - - // Initialize the RLP DATA pointer to its initial position (ctx == virt == 0, segment = RLP) - PUSH @SEGMENT_RLP_RAW - %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - - // Encode constant nodes - %initialize_rlp_segment - - // Initialize the state, transaction and receipt trie root pointers. - PROVER_INPUT(trie_ptr::state) - %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - PROVER_INPUT(trie_ptr::txn) - %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - PROVER_INPUT(trie_ptr::receipt) - %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - -global hash_initial_tries: - // We compute the length of the trie data segment in `mpt_hash` so that we - // can check the value provided by the prover. - // We initialize the segment length with 1 because the segment contains - // the null pointer `0` when the tries are empty. - PUSH 1 - %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE) %assert_eq - // stack: trie_data_len - %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE) %assert_eq - // stack: trie_data_len - %mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE) %assert_eq - // stack: trie_data_full_len - %mstore_global_metadata(@GLOBAL_METADATA_TRIE_DATA_SIZE) - -global start_txn: - // stack: (empty) - // The special case of an empty trie (i.e. for the first transaction) - // is handled outside of the kernel. - %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) - // stack: txn_nb - DUP1 %scalar_to_rlp - // stack: txn_counter, txn_nb - DUP1 %num_bytes %mul_const(2) - // stack: num_nibbles, txn_counter, txn_nb - %increment_bounded_rlp - // stack: txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE) - - // stack: init_gas_used, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb - - // If the prover has no txn for us to process, halt. - PROVER_INPUT(no_txn) - %jumpi(execute_withdrawals) - - // Call route_txn. When we return, we will process the txn receipt. - PUSH txn_after - // stack: retdest, prev_gas_used, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb - DUP4 DUP4 - - %jump(route_txn) - -global txn_after: - // stack: success, leftover_gas, cur_cum_gas, prev_txn_counter, prev_num_nibbles, txn_counter, num_nibbles, txn_nb - %process_receipt - // stack: new_cum_gas, txn_counter, num_nibbles, txn_nb - SWAP3 %increment SWAP3 - %jump(execute_withdrawals_post_stack_op) - -global execute_withdrawals: - // stack: cum_gas, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles, txn_nb - %stack (cum_gas, txn_counter, num_nibbles, next_txn_counter, next_num_nibbles) -> (cum_gas, txn_counter, num_nibbles) -execute_withdrawals_post_stack_op: - %withdrawals - -global hash_final_tries: - // stack: cum_gas, txn_counter, num_nibbles, txn_nb - // Check that we end up with the correct `cum_gas`, `txn_nb` and bloom filter. - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_AFTER) %assert_eq - DUP3 %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_AFTER) %assert_eq - %pop3 - PUSH 1 // initial trie data length - %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq - %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER) %assert_eq - %mpt_hash_receipt_trie %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER) %assert_eq - // We don't need the trie data length here. - POP - %jump(halt) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm deleted file mode 100644 index 070e474f6e..0000000000 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ /dev/null @@ -1,474 +0,0 @@ -// Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). -%macro mload_u32 - // stack: addr - %stack (addr) -> (addr, 4) - MLOAD_32BYTES -%endmacro - -// Load a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3). -%macro mload_u32_LE - // stack: addr - DUP1 - MLOAD_GENERAL - // stack: c0, addr - DUP2 - %increment - MLOAD_GENERAL - %shl_const(8) - ADD - // stack: c0 | (c1 << 8), addr - DUP2 - %add_const(2) - MLOAD_GENERAL - %shl_const(16) - ADD - // stack: c0 | (c1 << 8) | (c2 << 16), addr - SWAP1 - %add_const(3) - MLOAD_GENERAL - %shl_const(24) - ADD // OR - // stack: c0 | (c1 << 8) | (c2 << 16) | (c3 << 24) -%endmacro - -// Load a little-endian u64, consisting of 8 bytes (c_0, ..., c_7). -%macro mload_u64_LE - // stack: addr - DUP1 - %mload_u32_LE - // stack: lo, addr - SWAP1 - %add_const(4) - %mload_u32_LE - // stack: hi, lo - %shl_const(32) - // stack: hi << 32, lo - ADD // OR - // stack: (hi << 32) | lo -%endmacro - -// Load a big-endian u256. -%macro mload_u256 - // stack: addr - %stack (addr) -> (addr, 32) - MLOAD_32BYTES -%endmacro - -// Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). -%macro mstore_u32 - // stack: addr, value - MSTORE_32BYTES_4 - // stack: offset - POP -%endmacro - -// Load a value from the given segment of the current context's memory space. -// Note that main memory values are one byte each, but in general memory values -// can be 256 bits. This macro deals with a single address (unlike MLOAD), so -// if it is used with main memory, it will load a single byte. -%macro mload_current(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - GET_CONTEXT - // stack: context, segment, offset - %build_address - MLOAD_GENERAL - // stack: value -%endmacro - -// Store a value to the given segment of the current context's memory space. -// Note that main memory values are one byte each, but in general memory values -// can be 256 bits. This macro deals with a single address (unlike MSTORE), so -// if it is used with main memory, it will store a single byte. -%macro mstore_current(segment) - // stack: offset, value - PUSH $segment - // stack: segment, offset, value - GET_CONTEXT - // stack: context, segment, offset, value - %build_address - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro mstore_current(segment, offset) - // stack: value - PUSH $offset - // stack: offset, value - PUSH $segment - // stack: segment, offset, value - GET_CONTEXT - // stack: context, segment, offset, value - %build_address - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Load a single byte from user code. -%macro mload_current_code - // stack: offset - // SEGMENT_CODE == 0 - GET_CONTEXT ADD - // stack: addr - MLOAD_GENERAL - // stack: value -%endmacro - -// Load a single value from the kernel general memory, in the current context (not the kernel's context). -%macro mload_current_general - // stack: offset - %mload_current(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a single value from the kernel general memory, in the current context (not the kernel's context). -%macro mload_current_general_no_offset - // stack: - %build_current_general_address_no_offset - MLOAD_GENERAL - // stack: value -%endmacro - -// Load a big-endian u32 from kernel general memory in the current context. -%macro mload_current_general_u32 - // stack: offset - %build_current_general_address - %mload_u32 - // stack: value -%endmacro - -// Load a little-endian u32 from kernel general memory in the current context. -%macro mload_current_general_u32_LE - // stack: offset - %build_current_general_address - %mload_u32_LE - // stack: value -%endmacro - -// Load a little-endian u64 from kernel general memory in the current context. -%macro mload_current_general_u64_LE - // stack: offset - %build_current_general_address - %mload_u64_LE - // stack: value -%endmacro - -// Load a u256 from kernel general memory in the current context. -%macro mload_current_general_u256 - // stack: offset - %build_current_general_address - %mload_u256 - // stack: value -%endmacro - -// Store a single value to kernel general memory in the current context. -%macro mstore_current_general - // stack: offset, value - %build_current_general_address - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Store a single value to kernel general memory in the current context. -%macro mstore_current_general_no_offset - // stack: value - %build_current_general_address_no_offset - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro mstore_current_general(offset) - // stack: value - PUSH $offset - // stack: offset, value - %mstore_current_general - // stack: (empty) -%endmacro - -// Store a big-endian u32 to kernel general memory in the current context. -%macro mstore_current_general_u32 - // stack: offset, value - %build_current_general_address - %mstore_u32 - // stack: (empty) -%endmacro - -// set offset i to offset j in kernel general -%macro mupdate_current_general - // stack: j, i - %mload_current_general - // stack: x, i - SWAP1 - %mstore_current_general - // stack: (empty) -%endmacro - -// Load a single value from the given segment of kernel (context 0) memory. -%macro mload_kernel(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - %build_kernel_address - MLOAD_GENERAL - // stack: value -%endmacro - -// Load a single value from the given segment of kernel (context 0) memory. -%macro mload_kernel_no_offset(segment) - // stack: empty - PUSH $segment - // stack: addr - MLOAD_GENERAL - // stack: value -%endmacro - -// Store a single value from the given segment of kernel (context 0) memory. -%macro mstore_kernel(segment) - // stack: offset, value - PUSH $segment - // stack: segment, offset, value - %build_kernel_address - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Store a single value from the given segment of kernel (context 0) memory. -%macro mstore_kernel_no_offset(segment) - // stack: value - PUSH $segment - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Store a single value from the given segment of kernel (context 0) memory. -%macro mstore_kernel(segment, offset) - // stack: value - PUSH $offset - // stack: offset, value - PUSH $segment - // stack: segment, offset, value - %build_kernel_address - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Load from the kernel a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0) -%macro mload_kernel_u32(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - %build_kernel_address - %mload_u32 -%endmacro - -// Load from the kernel a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3). -%macro mload_kernel_u32_LE(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - %build_kernel_address - %mload_u32_LE -%endmacro - -// Load from the kernel a little-endian u64, consisting of 8 bytes (c_0, ..., c_7). -%macro mload_kernel_u64_LE(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - %build_kernel_address - %mload_u64_LE -%endmacro - -// Load a u256 (big-endian) from the kernel. -%macro mload_kernel_u256(segment) - // stack: offset - PUSH $segment - // stack: segment, offset - %build_kernel_address - %mload_u256 -%endmacro - -// Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// to the kernel. -%macro mstore_kernel_u32(segment) - // stack: offset, value - PUSH $segment - // stack: segment, offset, value - %build_kernel_address - // stack: addr, value - %mstore_u32 -%endmacro - -// Load a single byte from kernel code. -%macro mload_kernel_code - // stack: offset - // ctx == SEGMENT_CODE == 0 - MLOAD_GENERAL - // stack: value -%endmacro - -%macro mload_kernel_code(label) - // stack: shift - PUSH $label - ADD - // stack: label + shift - %mload_kernel_code - // stack: byte -%endmacro - -// Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// from kernel code. -%macro mload_kernel_code_u32 - // stack: offset - // ctx == SEGMENT_CODE == 0 - %mload_u32 - // stack: value -%endmacro - -%macro mload_kernel_code_u32(label) - // stack: u32_shift - %mul_const(4) - // stack: byte_shift - PUSH $label - ADD - // stack: offset - // ctx == SEGMENT_CODE == 0 - %mload_u32 - // stack: value -%endmacro - -// Load a single value from kernel general memory. -%macro mload_kernel_general - // stack: offset - %mload_kernel(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a single value from kernel general memory. -%macro mload_kernel_general(offset) - PUSH $offset - %mload_kernel(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// from kernel general memory. -%macro mload_kernel_general_u32 - // stack: offset - %mload_kernel_u32(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3), -// from kernel general memory. -%macro mload_kernel_general_u32_LE - // stack: offset - %mload_kernel_u32_LE(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a little-endian u64, consisting of 8 bytes -// (c_0, c_1, c_2, c_3, c_4, c_5, c_6, c_7), from kernel general memory. -%macro mload_kernel_general_u64_LE - // stack: offset - %mload_kernel_u64_LE(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Load a u256 (big-endian) from kernel code. -%macro mload_kernel_code_u256 - // stack: offset - // ctx == SEGMENT_CODE == 0 - %mload_u256 - // stack: value -%endmacro - -// Load a u256 (big-endian) from kernel general memory. -%macro mload_kernel_general_u256 - // stack: offset - %mload_kernel_u256(@SEGMENT_KERNEL_GENERAL) - // stack: value -%endmacro - -// Store a single byte to kernel code. -%macro mstore_kernel_code - // stack: offset, value - // ctx == SEGMENT_CODE == 0 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// to kernel code. -%macro mstore_kernel_code_u32 - // stack: offset, value - // ctx == SEGMENT_CODE == 0 - %mstore_u32 -%endmacro - -%macro swap_mstore - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro mstore_kernel_general - // stack: offset, value - %mstore_kernel(@SEGMENT_KERNEL_GENERAL) - // stack: (empty) -%endmacro - -%macro mstore_kernel_general(offset) - // stack: value - PUSH $offset - // stack: offset, value - %mstore_kernel_general - // stack: (empty) -%endmacro - -// Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0), -// to kernel general memory. -%macro mstore_kernel_general_u32 - // stack: offset, value - %mstore_kernel_u32(@SEGMENT_KERNEL_GENERAL) -%endmacro - -// Load a single value from kernel general 2 memory. -%macro mload_kernel_general_2 - // stack: offset - %mload_kernel(@SEGMENT_KERNEL_GENERAL_2) - // stack: value -%endmacro - -// Load a single value from kernel general memory. -%macro mload_kernel_general_2(offset) - PUSH $offset - %mload_kernel(@SEGMENT_KERNEL_GENERAL_2) - // stack: value -%endmacro - -%macro mstore_kernel_general_2 - // stack: offset, value - %mstore_kernel(@SEGMENT_KERNEL_GENERAL_2) - // stack: (empty) -%endmacro - -%macro mstore_kernel_general_2(offset) - // stack: value - PUSH $offset - // stack: offset, value - %mstore_kernel_general_2 - // stack: (empty) -%endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memcpy.asm b/evm/src/cpu/kernel/asm/memory/memcpy.asm deleted file mode 100644 index a7819bf6e8..0000000000 --- a/evm/src/cpu/kernel/asm/memory/memcpy.asm +++ /dev/null @@ -1,106 +0,0 @@ -// Copies `count` values from SRC to DST. -global memcpy: - // stack: DST, SRC, count, retdest - DUP3 - // stack: count, DST, SRC, count, retdest - ISZERO - // stack: count == 0, DST, SRC, count, retdest - %jumpi(memcpy_finish) - // stack: DST, SRC, count, retdest - DUP1 - - // Copy the next value. - DUP3 - // stack: SRC, DST, DST, SRC, count, retdest - MLOAD_GENERAL - // stack: value, DST, DST, SRC, count, retdest - MSTORE_GENERAL - // stack: DST, SRC, count, retdest - - // Increment dst_addr. - %increment - // Increment src_addr. - SWAP1 - %increment - SWAP1 - // Decrement count. - PUSH 1 DUP4 SUB SWAP3 POP - - // Continue the loop. - %jump(memcpy) - -%macro memcpy - %stack (dst, src, count) -> (dst, src, count, %%after) - %jump(memcpy) -%%after: -%endmacro - -// Similar logic to memcpy, but optimized for copying sequences of bytes. -global memcpy_bytes: - // stack: DST, SRC, count, retdest - - // Handle small case - DUP3 - // stack: count, DST, SRC, count, retdest - %lt_const(0x21) - // stack: count <= 32, DST, SRC, count, retdest - %jumpi(memcpy_bytes_finish) - - // We will pack 32 bytes into a U256 from the source, and then unpack it at the destination. - // Copy the next chunk of bytes. - // stack: DST, SRC, count, retdest - PUSH 32 - DUP3 - // stack: SRC, 32, DST, SRC, count, retdest - MLOAD_32BYTES - // stack: value, DST, SRC, count, retdest - SWAP1 - // stack: DST, value, SRC, count, retdest - MSTORE_32BYTES_32 - // stack: DST', SRC, count, retdest - // Increment SRC by 32. - SWAP1 - %add_const(0x20) - SWAP1 - // Decrement count by 32. - PUSH 32 DUP4 SUB SWAP3 POP - - // Continue the loop. - %jump(memcpy_bytes) - -memcpy_bytes_finish: - // stack: DST, SRC, count, retdest - - // Handle empty case - DUP3 - // stack: count, DST, SRC, count, retdest - ISZERO - // stack: count == 0, DST, SRC, count, retdest - %jumpi(memcpy_finish) - - // stack: DST, SRC, count, retdest - - // Copy the last chunk of `count` bytes. - DUP3 - DUP1 - DUP4 - // stack: SRC, count, count, DST, SRC, count, retdest - MLOAD_32BYTES - // stack: value, count, DST, SRC, count, retdest - DUP3 - // stack: DST, value, count, DST, SRC, count, retdest - %mstore_unpacking - // stack: new_offset, DST, SRC, count, retdest - POP - -memcpy_finish: - // stack: DST, SRC, count, retdest - %pop3 - // stack: retdest - JUMP - -%macro memcpy_bytes - %stack (dst, src, count) -> (dst, src, count, %%after) - %jump(memcpy_bytes) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memset.asm b/evm/src/cpu/kernel/asm/memory/memset.asm deleted file mode 100644 index 792aeabc68..0000000000 --- a/evm/src/cpu/kernel/asm/memory/memset.asm +++ /dev/null @@ -1,49 +0,0 @@ -// Sets `count` values to 0 at DST. -global memset: - // stack: DST, count, retdest - - // Handle small case - DUP2 - // stack: count, DST, count, retdest - %lt_const(0x21) - // stack: count <= 32, DST, count, retdest - %jumpi(memset_finish) - - // stack: DST, count, retdest - PUSH 0 - SWAP1 - // stack: DST, 0, count, retdest - MSTORE_32BYTES_32 - // stack: DST', count, retdest - // Decrement count. - PUSH 32 DUP3 SUB SWAP2 POP - - // Continue the loop. - %jump(memset) - -memset_finish: - // stack: DST, final_count, retdest - - // Handle empty case - DUP2 - // stack: final_count, DST, final_count, retdest - ISZERO - // stack: final_count == 0, DST, final_count, retdest - %jumpi(memset_bytes_empty) - - // stack: DST, final_count, retdest - DUP2 - PUSH 0 - DUP3 - // stack: DST, 0, final_count, DST, final_count, retdest - %mstore_unpacking - // stack: DST, final_count, retdest - %pop3 - // stack: retdest - JUMP - -memset_bytes_empty: - // stack: DST, 0, retdest - %pop2 - // stack: retdest - JUMP diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm deleted file mode 100644 index e69e292b64..0000000000 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ /dev/null @@ -1,436 +0,0 @@ -// Load the given global metadata field from memory. -%macro mload_global_metadata(field) - // Global metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: (empty) - PUSH $field - MLOAD_GENERAL - // stack: value -%endmacro - -// Store the given global metadata field to memory. -%macro mstore_global_metadata(field) - // Global metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: value - PUSH $field - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Load the given context metadata field from memory. -%macro mload_context_metadata(field) - // Context metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: (empty) - PUSH $field - GET_CONTEXT - ADD - // stack: addr - MLOAD_GENERAL - // stack: value -%endmacro - -// Store the given context metadata field to memory. -%macro mstore_context_metadata(field) - // Context metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: value - PUSH $field - GET_CONTEXT - ADD - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -// Store the given context metadata field to memory. -%macro mstore_context_metadata(field, value) - // Context metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - PUSH $field - GET_CONTEXT - ADD - // stack: addr - PUSH $value - // stack: value, addr - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro mstore_parent_context_metadata(field) - // Context metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: value - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - - // stack: parent_ctx, value - PUSH $field ADD - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro mstore_parent_context_metadata(field, value) - // Context metadata are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: (empty) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - - // stack: parent_ctx - PUSH $field ADD - // stack: addr - PUSH $value - // stack: value, addr - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro address - %mload_context_metadata(@CTX_METADATA_ADDRESS) -%endmacro - -global sys_address: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %address - // stack: address, kexit_info - SWAP1 - EXIT_KERNEL - -%macro caller - %mload_context_metadata(@CTX_METADATA_CALLER) -%endmacro - -global sys_caller: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %caller - // stack: caller, kexit_info - SWAP1 - EXIT_KERNEL - -%macro callvalue - %mload_context_metadata(@CTX_METADATA_CALL_VALUE) -%endmacro - -%macro codesize - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) -%endmacro - -global sys_codesize: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %codesize - // stack: codesize, kexit_info - SWAP1 - EXIT_KERNEL - -global sys_callvalue: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %callvalue - // stack: callvalue, kexit_info - SWAP1 - EXIT_KERNEL - -%macro mem_words - %mload_context_metadata(@CTX_METADATA_MEM_WORDS) -%endmacro - -%macro msize - %mem_words - %mul_const(32) -%endmacro - -global sys_msize: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %msize - // stack: msize, kexit_info - SWAP1 - EXIT_KERNEL - -%macro calldatasize - %mload_context_metadata(@CTX_METADATA_CALLDATA_SIZE) -%endmacro - -global sys_calldatasize: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %calldatasize - // stack: calldatasize, kexit_info - SWAP1 - EXIT_KERNEL - -%macro returndatasize - %mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) -%endmacro - -global sys_returndatasize: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %returndatasize - // stack: returndatasize, kexit_info - SWAP1 - EXIT_KERNEL - -%macro coinbase - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY) -%endmacro - -global sys_coinbase: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %coinbase - // stack: coinbase, kexit_info - SWAP1 - EXIT_KERNEL - -%macro timestamp - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_TIMESTAMP) -%endmacro - -global sys_timestamp: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %timestamp - // stack: timestamp, kexit_info - SWAP1 - EXIT_KERNEL - -%macro blocknumber - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER) -%endmacro - -global sys_number: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %blocknumber - // stack: blocknumber, kexit_info - SWAP1 - EXIT_KERNEL - -%macro blockgaslimit - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_LIMIT) -%endmacro - -global sys_gaslimit: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %blockgaslimit - // stack: blockgaslimit, kexit_info - SWAP1 - EXIT_KERNEL - -%macro blockchainid - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_CHAIN_ID) -%endmacro - -global sys_chainid: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %blockchainid - // stack: chain_id, kexit_info - SWAP1 - EXIT_KERNEL - -%macro basefee - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BASE_FEE) -%endmacro - -global sys_basefee: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %basefee - // stack: basefee, kexit_info - SWAP1 - EXIT_KERNEL - -global sys_blockhash: - // stack: kexit_info, block_number - %charge_gas_const(@GAS_BLOCKHASH) - SWAP1 - // stack: block_number, kexit_info - %blockhash - // stack: blockhash, kexit_info - SWAP1 - EXIT_KERNEL - -global blockhash: - // stack: block_number, retdest - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER) - // stack: cur_block_number, block_number, retdest - // Check for an overflow, since we're incrementing `block_number` afterwards. - DUP2 %eq_const(@U256_MAX) %jumpi(zero_hash) - // stack: cur_block_number, block_number, retdest - DUP1 DUP3 %increment GT %jumpi(zero_hash) // if block_number >= cur_block_number - // stack: cur_block_number, block_number, retdest - DUP2 PUSH 256 ADD - // stack: block_number+256, cur_block_number, block_number, retdest - DUP2 GT %jumpi(zero_hash) // if cur_block_number > block_number + 256 - // If we are here, the provided block number is correct - SUB - // stack: cur_block_number - block_number, retdest - PUSH 256 SUB - // stack: block_hash_number, retdest - %mload_kernel(@SEGMENT_BLOCK_HASHES) - SWAP1 JUMP - -%macro blockhash - // stack: block_number - %stack (block_number) -> (block_number, %%after) - %jump(blockhash) -%%after: -%endmacro - -zero_hash: - // stack: cur_block_number, block_number, retdest - %pop2 - PUSH 0 SWAP1 - JUMP - -%macro update_mem_words - // stack: num_words, kexit_info - %mem_words - // stack: old_num_words, num_words, kexit_info - DUP2 DUP2 GT - // stack: old_num_words > num_words, old_num_words, num_words, kexit_info - %jumpi(%%no_update) - // stack: old_num_words, num_words, kexit_info - %memory_cost - // stack: old_cost, num_words, kexit_info - SWAP1 - // stack: num_words, old_cost, kexit_info - DUP1 %mstore_context_metadata(@CTX_METADATA_MEM_WORDS) - // stack: num_words, old_cost, kexit_info - %memory_cost - // stack: new_cost, old_cost, kexit_info - SUB - // stack: additional_cost, kexit_info - %charge_gas - %jump(%%end) -%%no_update: - // stack: old_num_words, num_words, kexit_info - %pop2 -%%end: - // stack: kexit_info -%endmacro - -%macro update_mem_bytes - // stack: num_bytes, kexit_info - %num_bytes_to_num_words - // stack: num_words, kexit_info - %update_mem_words - // stack: kexit_info -%endmacro - -%macro num_bytes_to_num_words - // stack: num_bytes - %add_const(31) - // stack: 31 + num_bytes - %shr_const(5) - // stack: (num_bytes + 31) / 32 -%endmacro - -%macro memory_cost - // stack: num_words - DUP1 - // stack: num_words, msize - %mul_const(@GAS_MEMORY) - // stack: num_words * GAS_MEMORY, msize - SWAP1 - // stack: num_words, num_words * GAS_MEMORY - %square - %shr_const(9) - // stack: num_words^2 / 512, num_words * GAS_MEMORY - ADD - // stack: cost = num_words^2 / 512 + num_words * GAS_MEMORY -%endmacro - -// Faults if the given offset is "unreasonable", i.e. the associated memory expansion cost -// would exceed any reasonable block limit. -// We do this to avoid overflows in future gas-related calculations. -%macro ensure_reasonable_offset - // stack: offset - // The memory expansion cost, (50000000 / 32)^2 / 512, is around 2^32 gas, - // i.e. greater than any reasonable block limit. - %gt_const(50000000) - // stack: is_unreasonable - %jumpi(fault_exception) - // stack: (empty) -%endmacro - -// Convenience macro for checking if the current context is static. -// Called before state-changing opcodes. -%macro check_static - %mload_context_metadata(@CTX_METADATA_STATIC) - %jumpi(fault_exception) -%endmacro - -// Adds the two top elements of the stack, and faults in case of overflow. -%macro add_or_fault - // stack: x, y - DUP2 ADD - // stack: sum, y - DUP1 SWAP2 - // stack: y, sum, sum - GT - // stack: is_overflow, sum - %jumpi(fault_exception) - // stack: sum -%endmacro - -%macro call_depth - %mload_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) -%endmacro - -%macro increment_call_depth - %mload_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) - %increment - %mstore_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) -%endmacro - -%macro decrement_call_depth - PUSH 1 - %mload_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) - SUB - %mstore_global_metadata(@GLOBAL_METADATA_CALL_STACK_DEPTH) -%endmacro - -global sys_prevrandao: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - %mload_global_metadata(@GLOBAL_METADATA_BLOCK_RANDOM) - %stack (random, kexit_info) -> (kexit_info, random) - EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/memory/packing.asm b/evm/src/cpu/kernel/asm/memory/packing.asm deleted file mode 100644 index a1bf5a09ad..0000000000 --- a/evm/src/cpu/kernel/asm/memory/packing.asm +++ /dev/null @@ -1,321 +0,0 @@ -// Methods for encoding integers as bytes in memory, as well as the reverse, -// decoding bytes as integers. All big-endian unless specified. - -global mload_packing_u64_LE: - // stack: addr, retdest - DUP1 MLOAD_GENERAL - DUP2 %add_const(1) MLOAD_GENERAL %shl_const( 8) ADD - DUP2 %add_const(2) MLOAD_GENERAL %shl_const(16) ADD - DUP2 %add_const(3) MLOAD_GENERAL %shl_const(24) ADD - DUP2 %add_const(4) MLOAD_GENERAL %shl_const(32) ADD - DUP2 %add_const(5) MLOAD_GENERAL %shl_const(40) ADD - DUP2 %add_const(6) MLOAD_GENERAL %shl_const(48) ADD - DUP2 %add_const(7) MLOAD_GENERAL %shl_const(56) ADD - %stack (value, addr, retdest) -> (retdest, value) - JUMP - -%macro mload_packing_u64_LE - %stack (addr) -> (addr, %%after) - %jump(mload_packing_u64_LE) -%%after: -%endmacro - -// Pre stack: addr, value, len, retdest -// Post stack: addr' -global mstore_unpacking: - // stack: addr, value, len, retdest - DUP3 ISZERO - // stack: len == 0, addr, value, len, retdest - %jumpi(mstore_unpacking_empty) - %stack(addr, value, len, retdest) -> (len, addr, value, retdest) - PUSH 3 - // stack: BYTES_PER_JUMP, len, addr, value, retdest - MUL - // stack: jump_offset, addr, value, retdest - PUSH mstore_unpacking_0 - // stack: mstore_unpacking_0, jump_offset, addr, value, retdest - ADD - // stack: address_unpacking, addr, value, retdest - JUMP - -mstore_unpacking_empty: - %stack(addr, value, len, retdest) -> (retdest, addr) - JUMP - -// This case can never be reached. It's only here to offset the table correctly. -mstore_unpacking_0: - %rep 3 - PANIC - %endrep -mstore_unpacking_1: - // stack: addr, value, retdest - MSTORE_32BYTES_1 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_2: - // stack: addr, value, retdest - MSTORE_32BYTES_2 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_3: - // stack: addr, value, retdest - MSTORE_32BYTES_3 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_4: - // stack: addr, value, retdest - MSTORE_32BYTES_4 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_5: - // stack: addr, value, retdest - MSTORE_32BYTES_5 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_6: - // stack: addr, value, retdest - MSTORE_32BYTES_6 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_7: - // stack: addr, value, retdest - MSTORE_32BYTES_7 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_8: - // stack: addr, value, retdest - MSTORE_32BYTES_8 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_9: - // stack: addr, value, retdest - MSTORE_32BYTES_9 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_10: - // stack: addr, value, retdest - MSTORE_32BYTES_10 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_11: - // stack: addr, value, retdest - MSTORE_32BYTES_11 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_12: - // stack: addr, value, retdest - MSTORE_32BYTES_12 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_13: - // stack: addr, value, retdest - MSTORE_32BYTES_13 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_14: - // stack: addr, value, retdest - MSTORE_32BYTES_14 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_15: - // stack: addr, value, retdest - MSTORE_32BYTES_15 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_16: - // stack: addr, value, retdest - MSTORE_32BYTES_16 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_17: - // stack: addr, value, retdest - MSTORE_32BYTES_17 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_18: - // stack: addr, value, retdest - MSTORE_32BYTES_18 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_19: - // stack: addr, value, retdest - MSTORE_32BYTES_19 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_20: - // stack: addr, value, retdest - MSTORE_32BYTES_20 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_21: - // stack: addr, value, retdest - MSTORE_32BYTES_21 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_22: - // stack: addr, value, retdest - MSTORE_32BYTES_22 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_23: - // stack: addr, value, retdest - MSTORE_32BYTES_23 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_24: - // stack: addr, value, retdest - MSTORE_32BYTES_24 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_25: - // stack: addr, value, retdest - MSTORE_32BYTES_25 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_26: - // stack: addr, value, retdest - MSTORE_32BYTES_26 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_27: - // stack: addr, value, retdest - MSTORE_32BYTES_27 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_28: - // stack: addr, value, retdest - MSTORE_32BYTES_28 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_29: - // stack: addr, value, retdest - MSTORE_32BYTES_29 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_30: - // stack: addr, value, retdest - MSTORE_32BYTES_30 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_31: - // stack: addr, value, retdest - MSTORE_32BYTES_31 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP -mstore_unpacking_32: - // stack: addr, value, retdest - MSTORE_32BYTES_32 - // stack: addr', retdest - SWAP1 - // stack: retdest, addr' - JUMP - -%macro mstore_unpacking - %stack (addr, value, len) -> (addr, value, len, %%after) - %jump(mstore_unpacking) -%%after: -%endmacro - -// Pre stack: addr, value, retdest -// Post stack: addr' -global mstore_unpacking_u64_LE: - %stack (addr, value) -> (0xff, value, addr, addr, value) - AND - MSTORE_GENERAL // First byte - DUP1 %add_const(1) - %stack (new_addr, addr, value) -> (0xff00, value, new_addr, addr, value) - AND %shr_const(8) - MSTORE_GENERAL // Second byte - DUP1 %add_const(2) - %stack (new_addr, addr, value) -> (0xff0000, value, new_addr, addr, value) - AND %shr_const(16) - MSTORE_GENERAL // Third byte - DUP1 %add_const(3) - %stack (new_addr, addr, value) -> (0xff000000, value, new_addr, addr, value) - AND %shr_const(24) - MSTORE_GENERAL // Fourth byte - DUP1 %add_const(4) - %stack (new_addr, addr, value) -> (0xff00000000, value, new_addr, addr, value) - AND %shr_const(32) - MSTORE_GENERAL // Fifth byte - DUP1 %add_const(5) - %stack (new_addr, addr, value) -> (0xff0000000000, value, new_addr, addr, value) - AND %shr_const(40) - MSTORE_GENERAL // Sixth byte - DUP1 %add_const(6) - %stack (new_addr, addr, value) -> (0xff000000000000, value, new_addr, addr, value) - AND %shr_const(48) - MSTORE_GENERAL // Seventh byte - DUP1 %add_const(7) - %stack (new_addr, addr, value) -> (0xff00000000000000, value, new_addr, addr, value) - AND %shr_const(56) - MSTORE_GENERAL // Eighth byte - %pop2 JUMP - -%macro mstore_unpacking_u64_LE - %stack (addr, value) -> (addr, value, %%after) - %jump(mstore_unpacking_u64_LE) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm deleted file mode 100644 index d20f2d0e6c..0000000000 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ /dev/null @@ -1,256 +0,0 @@ -global sys_mload: - // stack: kexit_info, offset - DUP2 %ensure_reasonable_offset - // stack: kexit_info, offset - %charge_gas_const(@GAS_VERYLOW) - // stack: kexit_info, offset - DUP2 %add_const(32) - // stack: expanded_num_bytes, kexit_info, offset - %update_mem_bytes - // stack: kexit_info, offset - %stack(kexit_info, offset) -> (offset, 32, kexit_info) - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - // stack: addr, len, kexit_info - MLOAD_32BYTES - %stack (value, kexit_info) -> (kexit_info, value) - EXIT_KERNEL - -global sys_mstore: - // stack: kexit_info, offset, value - DUP2 %ensure_reasonable_offset - // stack: kexit_info, offset, value - %charge_gas_const(@GAS_VERYLOW) - // stack: kexit_info, offset, value - DUP2 %add_const(32) - // stack: expanded_num_bytes, kexit_info, offset, value - %update_mem_bytes - // stack: kexit_info, offset, value - %stack(kexit_info, offset, value) -> (offset, value, kexit_info) - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - // stack: addr, value, kexit_info - MSTORE_32BYTES_32 - POP - // stack: kexit_info - EXIT_KERNEL - -global sys_mstore8: - // stack: kexit_info, offset, value - DUP2 %ensure_reasonable_offset - // stack: kexit_info, offset, value - %charge_gas_const(@GAS_VERYLOW) - // stack: kexit_info, offset, value - DUP2 %increment - // stack: expanded_num_bytes, kexit_info, offset, value - %update_mem_bytes - // stack: kexit_info, offset, value - %stack (kexit_info, offset, value) -> (value, 0x100, offset, kexit_info) - MOD SWAP1 - %mstore_current(@SEGMENT_MAIN_MEMORY) - // stack: kexit_info - EXIT_KERNEL - -global sys_calldataload: - // stack: kexit_info, i - %charge_gas_const(@GAS_VERYLOW) - // stack: kexit_info, i - %mload_context_metadata(@CTX_METADATA_CALLDATA_SIZE) - %stack (calldata_size, kexit_info, i) -> (calldata_size, i, kexit_info, i) - LT %jumpi(calldataload_large_offset) - %stack (kexit_info, i) -> (@SEGMENT_CALLDATA, i, 32, kexit_info) - GET_CONTEXT - %build_address - // stack: addr, 32, kexit_info - MLOAD_32BYTES -sys_calldataload_after_mload_packing: - // stack: value, kexit_info - SWAP1 - EXIT_KERNEL - PANIC -calldataload_large_offset: - %stack (kexit_info, i) -> (kexit_info, 0) - EXIT_KERNEL - -// Macro for {CALLDATA, RETURNDATA}COPY (W_copy in Yellow Paper). -%macro wcopy(segment, context_metadata_size) - // stack: kexit_info, dest_offset, offset, size - %wcopy_charge_gas - - %stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, kexit_info, dest_offset, offset, size) - %add_or_fault - // stack: expanded_num_bytes, kexit_info, dest_offset, offset, size, kexit_info - DUP1 %ensure_reasonable_offset - %update_mem_bytes - - %mload_context_metadata($context_metadata_size) - // stack: total_size, kexit_info, dest_offset, offset, size - DUP4 - // stack: offset, total_size, kexit_info, dest_offset, offset, size - GT %jumpi(wcopy_large_offset) - - // stack: kexit_info, dest_offset, offset, size - GET_CONTEXT - PUSH $segment - // stack: segment, context, kexit_info, dest_offset, offset, size - %jump(wcopy_within_bounds) -%endmacro - -%macro wcopy_charge_gas - // stack: kexit_info, dest_offset, offset, size - PUSH @GAS_VERYLOW - DUP5 - // stack: size, Gverylow, kexit_info, dest_offset, offset, size - ISZERO %jumpi(wcopy_empty) - // stack: Gverylow, kexit_info, dest_offset, offset, size - DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD %charge_gas -%endmacro - - -codecopy_within_bounds: - // stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size - POP -wcopy_within_bounds: - // stack: segment, src_ctx, kexit_info, dest_offset, offset, size - GET_CONTEXT - %stack (context, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, size, wcopy_after, kexit_info) - %build_address - SWAP3 %build_address - // stack: DST, SRC, size, wcopy_after, kexit_info - %jump(memcpy_bytes) - -wcopy_empty: - // stack: Gverylow, kexit_info, dest_offset, offset, size - %charge_gas - %stack (kexit_info, dest_offset, offset, size) -> (kexit_info) - EXIT_KERNEL - - -codecopy_large_offset: - // stack: total_size, src_ctx, kexit_info, dest_offset, offset, size - %pop2 -wcopy_large_offset: - // offset is larger than the size of the {CALLDATA,CODE,RETURNDATA}. So we just have to write zeros. - // stack: kexit_info, dest_offset, offset, size - GET_CONTEXT - %stack (context, kexit_info, dest_offset, offset, size) -> - (context, @SEGMENT_MAIN_MEMORY, dest_offset, size, wcopy_after, kexit_info) - %build_address - %jump(memset) - -wcopy_after: - // stack: kexit_info - EXIT_KERNEL - -// Pre stack: kexit_info, dest_offset, offset, size -// Post stack: (empty) -global sys_calldatacopy: - %wcopy(@SEGMENT_CALLDATA, @CTX_METADATA_CALLDATA_SIZE) - -// Pre stack: kexit_info, dest_offset, offset, size -// Post stack: (empty) -global sys_returndatacopy: - DUP4 DUP4 %add_or_fault // Overflow check - %mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) LT %jumpi(fault_exception) // Data len check - - %wcopy(@SEGMENT_RETURNDATA, @CTX_METADATA_RETURNDATA_SIZE) - -// Pre stack: kexit_info, dest_offset, offset, size -// Post stack: (empty) -global sys_codecopy: - // stack: kexit_info, dest_offset, offset, size - %wcopy_charge_gas - - %stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, kexit_info, dest_offset, offset, size) - %add_or_fault - // stack: expanded_num_bytes, kexit_info, dest_offset, offset, size, kexit_info - DUP1 %ensure_reasonable_offset - %update_mem_bytes - - GET_CONTEXT - %mload_context_metadata(@CTX_METADATA_CODE_SIZE) - // stack: code_size, ctx, kexit_info, dest_offset, offset, size - %codecopy_after_checks(@SEGMENT_CODE) - - -// Pre stack: kexit_info, address, dest_offset, offset, size -// Post stack: (empty) -global sys_extcodecopy: - %stack (kexit_info, address, dest_offset, offset, size) - -> (address, dest_offset, offset, size, kexit_info) - %u256_to_addr DUP1 %insert_accessed_addresses - // stack: cold_access, address, dest_offset, offset, size, kexit_info - PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS - MUL - PUSH @GAS_WARMACCESS - ADD - // stack: Gaccess, address, dest_offset, offset, size, kexit_info - - DUP5 - // stack: size, Gaccess, address, dest_offset, offset, size, kexit_info - ISZERO %jumpi(sys_extcodecopy_empty) - - // stack: Gaccess, address, dest_offset, offset, size, kexit_info - DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD - %stack (gas, address, dest_offset, offset, size, kexit_info) -> (gas, kexit_info, address, dest_offset, offset, size) - %charge_gas - - %stack (kexit_info, address, dest_offset, offset, size) -> (dest_offset, size, kexit_info, address, dest_offset, offset, size) - %add_or_fault - // stack: expanded_num_bytes, kexit_info, address, dest_offset, offset, size - DUP1 %ensure_reasonable_offset - %update_mem_bytes - - %next_context_id - - %stack (ctx, kexit_info, address, dest_offset, offset, size) -> - (address, ctx, extcodecopy_contd, ctx, kexit_info, dest_offset, offset, size) - %jump(load_code) - -sys_extcodecopy_empty: - %stack (Gaccess, address, dest_offset, offset, size, kexit_info) -> (Gaccess, kexit_info) - %charge_gas - EXIT_KERNEL - -extcodecopy_contd: - // stack: code_size, ctx, kexit_info, dest_offset, offset, size - %codecopy_after_checks(@SEGMENT_CODE) - - -// The internal logic is similar to wcopy, but handles range overflow differently. -// It is used for both CODECOPY and EXTCODECOPY. -%macro codecopy_after_checks(segment) - // stack: total_size, src_ctx, kexit_info, dest_offset, offset, size - DUP1 DUP6 - // stack: offset, total_size, total_size, src_ctx, kexit_info, dest_offset, offset, size - GT %jumpi(codecopy_large_offset) - - PUSH $segment SWAP1 - // stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size - DUP1 DUP8 DUP8 ADD - // stack: offset + size, total_size, total_size, segment, src_ctx, kexit_info, dest_offset, offset, size - LT %jumpi(codecopy_within_bounds) - - // stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size - DUP7 DUP7 ADD - // stack: offset + size, total_size, segment, src_ctx, kexit_info, dest_offset, offset, size - SUB // extra_size = offset + size - total_size - // stack: extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size - DUP1 DUP8 SUB - // stack: copy_size = size - extra_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size - - // Compute the new dest_offset after actual copies, at which we will start padding with zeroes. - DUP1 DUP7 ADD - // stack: new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size - - GET_CONTEXT - %stack (context, new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size) - %build_address - SWAP3 %build_address - // stack: DST, SRC, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size - %jump(memcpy_bytes) -%endmacro diff --git a/evm/src/cpu/kernel/asm/memory/txn_fields.asm b/evm/src/cpu/kernel/asm/memory/txn_fields.asm deleted file mode 100644 index a8c1c0788f..0000000000 --- a/evm/src/cpu/kernel/asm/memory/txn_fields.asm +++ /dev/null @@ -1,39 +0,0 @@ -// Load the given normalized transaction field from memory. -%macro mload_txn_field(field) - // Transaction fields are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: (empty) - PUSH $field - // stack: addr - MLOAD_GENERAL - // stack: value -%endmacro - -// Store the given normalized transaction field to memory. -%macro mstore_txn_field(field) - // Transaction fields are already scaled by their corresponding segment, - // effectively making them the direct memory position to read from / - // write to. - - // stack: value - PUSH $field - // stack: addr, value - SWAP1 - MSTORE_GENERAL - // stack: (empty) -%endmacro - -%macro origin - %mload_txn_field(@TXN_FIELD_ORIGIN) -%endmacro - -global sys_origin: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %origin - // stack: origin, kexit_info - SWAP1 - EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/mpt/accounts.asm b/evm/src/cpu/kernel/asm/mpt/accounts.asm deleted file mode 100644 index 0ee987b4c1..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/accounts.asm +++ /dev/null @@ -1,21 +0,0 @@ -// Return a pointer to the current account's data in the state trie. -%macro current_account_data - %address %mpt_read_state_trie - // stack: account_ptr - // account_ptr should be non-null as long as the prover provided the proper - // Merkle data. But a bad prover may not have, and we don't want return a - // null pointer for security reasons. - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr -%endmacro - -// Returns a pointer to the root of the storage trie associated with the current account. -%macro current_storage_trie - // stack: (empty) - %current_account_data - // stack: account_ptr - %add_const(2) - // stack: storage_root_ptr_ptr - %mload_trie_data - // stack: storage_root_ptr -%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/delete/delete.asm b/evm/src/cpu/kernel/asm/mpt/delete/delete.asm deleted file mode 100644 index 913ba1fcfb..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/delete/delete.asm +++ /dev/null @@ -1,45 +0,0 @@ -// Return a copy of the given node with the given key deleted. -// Assumes that the key is in the trie. -// -// Pre stack: node_ptr, num_nibbles, key, retdest -// Post stack: updated_node_ptr -global mpt_delete: - // stack: node_ptr, num_nibbles, key, retdest - DUP1 %mload_trie_data - // stack: node_type, node_ptr, num_nibbles, key, retdest - // Increment node_ptr, so it points to the node payload instead of its type. - SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_delete_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_delete_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_delete_leaf) - %eq_const(@MPT_NODE_EMPTY) %jumpi(panic) // This should never happen. - PANIC - -mpt_delete_leaf: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - %pop4 - PUSH 0 // empty node ptr - SWAP1 JUMP - -global delete_account: - %stack (address, retdest) -> (address, delete_account_save, retdest) - %addr_to_state_key - // stack: key, delete_account_save, retdest - PUSH 64 - // stack: 64, key, delete_account_save, retdest - %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - // stack: state_root_prt, 64, key, delete_account_save, retdest - %jump(mpt_delete) -delete_account_save: - // stack: updated_state_root_ptr, retdest - %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - JUMP - -%macro delete_account - %stack (address) -> (address, %%after) - %jump(delete_account) -%%after: - // stack: (empty) -%endmacro \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm b/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm deleted file mode 100644 index 64187ac83a..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm +++ /dev/null @@ -1,130 +0,0 @@ -// Delete from a branch node. -// Algorithm is roughly: -// - Delete `(num_nibbles-1, key[1:])` from `branch[key[0]]`. -// - If the returned node is non-empty, update the branch node and return it. -// - Otherwise, count the number of non-empty children of the branch node. -// - If there are more than one, update the branch node and return it. -// - If there is exactly one, transform the branch node into an leaf/extension node and return it. -// Assumes that `num_nibbles>0` and that the value of the branch node is zero. -// TODO: May need to revisit these assumptions depending on how the receipt trie is implemented. -global mpt_delete_branch: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, retdest - DUP2 ISZERO %jumpi(panic) // This should never happen. - DUP3 DUP3 - // stack: num_nibbles, key, node_payload_ptr, num_nibbles, key, retdest - %split_first_nibble - %stack (first_nibble, num_nibbles, key, node_payload_ptr, old_num_nibbles, old_key) -> - (node_payload_ptr, first_nibble, num_nibbles, key, after_mpt_delete_branch, first_nibble, node_payload_ptr) - ADD - // stack: child_ptr_ptr, num_nibbles, key, after_mpt_delete_branch, first_nibble, node_payload_ptr, retdest - %mload_trie_data - %jump(mpt_delete) - -after_mpt_delete_branch: - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - // If the updated child is empty, check if we need to normalize the branch node. - DUP1 %mload_trie_data ISZERO %jumpi(maybe_normalize_branch) - -// Set `branch[first_nibble] = updated_child_ptr`. -update_branch: - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP3 DUP3 ADD - // stack: node_payload_ptr+first_nibble, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mstore_trie_data - %stack (first_nibble, node_payload_ptr, retdest) -> (node_payload_ptr, 1, retdest) - SUB - // stack: node_ptr, retdest - SWAP1 - JUMP - -// The updated child is empty. Count how many non-empty children the branch node has. -// If it's one, transform the branch node into an leaf/extension node and return it. -maybe_normalize_branch: - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - PUSH 0 - PUSH @SEGMENT_KERNEL_GENERAL - MSTORE_32BYTES_2 - POP - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - PUSH 0 -// Loop from i=0..16 excluding `first_nibble` and store the number of non-empty children in -// KernelGeneral[0]. Also store the last non-empty child in KernelGeneral[1]. -loop: - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP1 DUP4 EQ %jumpi(loop_eq_first_nibble) - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP1 %eq_const(16) %jumpi(loop_end) - DUP1 DUP5 ADD %mload_trie_data %mload_trie_data ISZERO ISZERO %jumpi(loop_non_empty) - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %increment %jump(loop) -loop_eq_first_nibble: - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %increment %jump(loop) -loop_non_empty: - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) %increment %mstore_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) - PUSH 1 PUSH @SEGMENT_KERNEL_GENERAL %build_kernel_address - DUP2 - MSTORE_GENERAL - %increment %jump(loop) -loop_end: - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - POP - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - // If there's more than one non-empty child, simply update the branch node. - %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) %gt_const(1) %jumpi(update_branch) - %mload_kernel_no_offset(@SEGMENT_KERNEL_GENERAL) ISZERO %jumpi(panic) // This should never happen. - // Otherwise, transform the branch node into a leaf/extension node. - // stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mload_kernel_general(1) - // stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP4 ADD %mload_trie_data - // stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP1 %mload_trie_data %eq_const(@MPT_NODE_BRANCH) %jumpi(maybe_normalize_branch_branchhash) - DUP1 %mload_trie_data %eq_const(@MPT_NODE_HASH) %jumpi(maybe_normalize_branch_branchhash) - DUP1 %mload_trie_data %eq_const(@MPT_NODE_EXTENSION) %jumpi(maybe_normalize_branch_leafext) - DUP1 %mload_trie_data %eq_const(@MPT_NODE_LEAF) %jumpi(maybe_normalize_branch_leafext) - PANIC // This should never happen. - -// The only child of the branch node is a branch node or a hash node. -// Transform the branch node into an extension node of length 1. -// This assumes that the hash node does not contain a leaf or an extension node (in which case this implementation is incorrect). -maybe_normalize_branch_branchhash: - // stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %get_trie_data_size // pointer to the extension node we're about to create - // stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - PUSH @MPT_NODE_EXTENSION %append_to_trie_data - // stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - PUSH 1 %append_to_trie_data // Append node_len to our node - // stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mload_kernel_general(1) %append_to_trie_data // Append node_key to our node - // stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - SWAP1 %append_to_trie_data // Append updated_child_node_ptr to our node - %stack (extension_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest) -> (retdest, extension_ptr) - JUMP - -// The only child of the branch node is a leaf/extension node. -// Transform the branch node into an leaf/extension node of length 1+len(child). -// For that, return the modified child as the new node. -maybe_normalize_branch_leafext: - // stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP1 %increment %mload_trie_data - // stack: child_len, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - DUP2 %add_const(2) %mload_trie_data - // stack: child_key, child_len, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest - %mload_kernel_general(1) - %stack (i, child_key, child_len, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr) -> - (1, i, child_len, child_key, only_child_ptr) - %merge_nibbles - // stack: len, key, only_child_ptr,retdest - DUP3 - // stack: node_ptr, len, key, only_child_ptr, retdest - SWAP1 DUP2 - // stack: node_ptr, len, node_ptr, key, only_child_ptr, retdest - %increment %mstore_trie_data // Change len in the child node - // stack: node_ptr, key, only_child_ptr, retdest - %add_const(2) %mstore_trie_data // Change key in the child node - // stack: node_ptr, retdest - SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm b/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm deleted file mode 100644 index 0627fcba6a..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm +++ /dev/null @@ -1,79 +0,0 @@ -// Delete from an extension node. -// Algorithm is roughly: -// - Let `k = length(node)` -// - Delete `(num_nibbles-k, key[k:])` from `node.child`. -// - If the returned child node is a branch node, the current node is replaced with an extension node with updated child. -// - If the returned child node is an extension node, we merge the two extension nodes into one extension node. -// - If the returned child node is a leaf node, we merge the two nodes into one leaf node. -global mpt_delete_extension: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, retdest - DUP1 %mload_trie_data - // stack: node_len, node_payload_ptr, num_nibbles, key, retdest - DUP2 %increment %mload_trie_data - %stack (node_key, node_len, node_payload_ptr, num_nibbles, key, retdest) -> - (node_len, num_nibbles, key, node_payload_ptr, node_len, node_key, retdest) - %truncate_nibbles - // stack: num_nibbles, key, node_payload_ptr, node_len, node_key, retdest - SWAP2 - // stack: node_payload_ptr, key, num_nibbles, node_len, node_key, retdest - DUP1 %add_const(2) %mload_trie_data - %stack (node_child_ptr, node_payload_ptr, key, num_nibbles, node_len, node_key, retdest) -> - (node_child_ptr, num_nibbles, key, after_mpt_delete_extension, node_payload_ptr, node_len, node_key, retdest) - %jump(mpt_delete) - -after_mpt_delete_extension: - // stack: updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - DUP1 %mload_trie_data - // stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(after_mpt_delete_extension_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(after_mpt_delete_extension_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(after_mpt_delete_extension_leaf) - %eq_const(@MPT_NODE_EMPTY) %jumpi(panic) // This should never happen. - PANIC - -after_mpt_delete_extension_branch: - // stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - POP - // stack: updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - DUP2 %add_const(2) %mstore_trie_data - // stack: node_payload_ptr, node_len, node_key, retdest - %decrement - %stack (extension_ptr, node_len, node_key, retdest) -> (retdest, extension_ptr) - JUMP - -after_mpt_delete_extension_extension: - // stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - POP SWAP1 POP - // stack: updated_child_node_ptr, node_len, node_key, retdest - DUP1 %increment %mload_trie_data - // stack: child_len, updated_child_node_ptr, node_len, node_key, retdest - DUP2 %add_const(2) %mload_trie_data - // stack: child_key, child_len, updated_child_node_ptr, node_len, node_key, retdest - %stack (child_key, child_len, updated_child_node_ptr, node_len, node_key) -> (node_len, node_key, child_len, child_key, updated_child_node_ptr) - %merge_nibbles - // stack: len, key, updated_child_node_ptr, retdest - DUP3 %increment %mstore_trie_data // Change len - // stack: key, updated_child_node_ptr, retdest - DUP2 %add_const(2) %mstore_trie_data // Change key - // stack: extension_ptr, retdest - SWAP1 JUMP - -// Essentially the same as `after_mpt_delete_extension_extension`. TODO: Could merge in a macro or common function. -after_mpt_delete_extension_leaf: - // stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest - POP SWAP1 POP - // stack: updated_child_node_ptr, node_len, node_key, retdest - DUP1 %increment %mload_trie_data - // stack: child_len, updated_child_node_ptr, node_len, node_key, retdest - DUP2 %add_const(2) %mload_trie_data - // stack: child_key, child_len, updated_child_node_ptr, node_len, node_key, retdest - %stack (child_key, child_len, updated_child_node_ptr, node_len, node_key) -> (node_len, node_key, child_len, child_key, updated_child_node_ptr) - %merge_nibbles - // stack: len, key, updated_child_node_ptr, retdest - DUP3 %increment %mstore_trie_data // Change len - // stack: key, updated_child_node_ptr, retdest - DUP2 %add_const(2) %mstore_trie_data // Change key - // stack: updated_child_node_ptr, retdest - SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm deleted file mode 100644 index 9acde9ce78..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ /dev/null @@ -1,288 +0,0 @@ -// Computes the Merkle root of the given trie node. -// -// encode_value is a function which should take as input -// - the position within @SEGMENT_RLP_RAW to write to, -// - the offset of a value within @SEGMENT_TRIE_DATA, -// - a return address, and -// - the current length of @SEGMENT_TRIE_DATA -// It should serialize the value, write it to @SEGMENT_RLP_RAW starting at the -// given position, and return an updated position (the next unused offset) as well -// as an updated length for @SEGMENT_TRIE_DATA. -// -// Given the initial length of the `TrieData` segment, it also updates the length -// for the current trie. -// -// Pre stack: node_ptr, encode_value, cur_len, retdest -// Post stack: hash, new_len -global mpt_hash: - // stack: node_ptr, encode_value, cur_len, retdest - %stack (node_ptr, encode_value, cur_len) -> (node_ptr, encode_value, cur_len, mpt_hash_hash_if_rlp) - %jump(encode_or_hash_node) -mpt_hash_hash_if_rlp: - // stack: result, result_len, new_len, retdest - // If result_len < 32, then we have an RLP blob, and we need to hash it. - DUP2 %lt_const(32) %jumpi(mpt_hash_hash_rlp) - // Otherwise, we already have a hash, so just return it. - // stack: result, result_len, new_len, retdest - %stack (result, result_len, new_len, retdest) -> (retdest, result, new_len) - JUMP -mpt_hash_hash_rlp: - // stack: result, result_len, new_len, retdest - %stack (result, result_len, new_len) - -> (@SEGMENT_RLP_RAW, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len) - // stack: addr, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len - %jump(mstore_unpacking) -mpt_hash_hash_rlp_after_unpacking: - // stack: result_addr, result_len, new_len, retdest - POP PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - // stack: result_addr, result_len, new_len, retdest - KECCAK_GENERAL - // stack: hash, new_len, retdest - %stack(hash, new_len, retdest) -> (retdest, hash, new_len) - JUMP - -// Given a trie node, return its RLP encoding if it is is less than 32 bytes, -// otherwise return the Keccak256 hash of its RLP encoding. -// -// The result is given as a (value, length) pair, where the length is given -// in bytes. -// -// Pre stack: node_ptr, encode_value, cur_len, retdest -// Post stack: result, result_len, cur_len -global encode_or_hash_node: - DUP1 %mload_trie_data - - // Check if we're dealing with a concrete node, i.e. not a hash node. - // stack: node_type, node_ptr, encode_value, cur_len, retdest - DUP1 - PUSH @MPT_NODE_HASH - SUB - %jumpi(encode_or_hash_concrete_node) - - // If we got here, node_type == @MPT_NODE_HASH. - // Load the hash and return (hash, 32). - // stack: node_type, node_ptr, encode_value, cur_len, retdest - POP - - // stack: node_ptr, encode_value, cur_len, retdest - %increment // Skip over node type prefix - // stack: hash_ptr, encode_value, cur_len, retdest - %mload_trie_data - // stack: hash, encode_value, cur_len, retdest - // Update the length of the `TrieData` segment: there are only two - // elements in a hash node. - SWAP2 %add_const(2) - %stack (cur_len, encode_value, hash, retdest) -> (retdest, hash, 32, cur_len) - JUMP -encode_or_hash_concrete_node: - %stack (node_type, node_ptr, encode_value, cur_len) -> (node_type, node_ptr, encode_value, cur_len, maybe_hash_node) - %jump(encode_node) -maybe_hash_node: - // stack: result_addr, result_len, cur_len, retdest - DUP2 %lt_const(32) - %jumpi(pack_small_rlp) - - // result_len >= 32, so we hash the result. - // stack: result_addr, result_len, cur_len, retdest - KECCAK_GENERAL - %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) - JUMP -pack_small_rlp: - // stack: result_ptr, result_len, cur_len, retdest - %stack (result_ptr, result_len, cur_len) - -> (result_ptr, result_len, result_len, cur_len) - MLOAD_32BYTES -after_packed_small_rlp: - %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) - JUMP - -// RLP encode the given trie node, and return an (pointer, length) pair -// indicating where the data lives within @SEGMENT_RLP_RAW. -// -// Pre stack: node_type, node_ptr, encode_value, cur_len, retdest -// Post stack: result_ptr, result_len, cur_len -encode_node: - // stack: node_type, node_ptr, encode_value, cur_len, retdest - // Increment node_ptr, so it points to the node payload instead of its type. - SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - - DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(encode_node_empty) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(encode_node_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(encode_node_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(encode_node_leaf) - - // If we got here, node_type is either @MPT_NODE_HASH, which should have - // been handled earlier in encode_or_hash_node, or something invalid. - PANIC - -global encode_node_empty: - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - %pop3 - %stack (cur_len, retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1, cur_len) - JUMP - -global encode_node_branch: - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - POP - - // `TrieData` stores the node type, 16 children pointers, and a value pointer. - SWAP2 %add_const(18) SWAP2 - // stack: node_payload_ptr, encode_value, cur_len, retdest - - // Allocate a block of RLP memory - %alloc_rlp_block DUP1 - // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len retdest - - // Call encode_or_hash_node on each child - %encode_child(0) %encode_child(1) %encode_child(2) %encode_child(3) - %encode_child(4) %encode_child(5) %encode_child(6) %encode_child(7) - %encode_child(8) %encode_child(9) %encode_child(10) %encode_child(11) - %encode_child(12) %encode_child(13) %encode_child(14) %encode_child(15) - - // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest - - %stack (rlp_pos, rlp_start, node_payload_ptr) - -> (node_payload_ptr, rlp_pos, rlp_start) - %add_const(16) - // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest - %mload_trie_data - // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest - DUP1 %jumpi(encode_node_branch_with_value) - - // No value; append the empty string (0x80). - // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (0x80, rlp_pos, rlp_pos, rlp_start) - MSTORE_GENERAL - // stack: rlp_pos', rlp_start, cur_len, retdest - %increment - // stack: rlp_pos'', rlp_start, cur_len, retdest - %jump(encode_node_branch_prepend_prefix) -encode_node_branch_with_value: - // stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest - %stack (value_ptr, rlp_pos, rlp_start, encode_value, cur_len) - -> (encode_value, rlp_pos, value_ptr, cur_len, encode_node_branch_after_value, rlp_start) - JUMP // call encode_value -encode_node_branch_after_value: - // stack: rlp_pos'', cur_len, rlp_start, retdest - %stack(rlp_pos, cur_len, rlp_start, retdest) -> (rlp_pos, rlp_start, cur_len, retdest) -encode_node_branch_prepend_prefix: - // stack: rlp_pos'', rlp_start, cur_len, retdest - %prepend_rlp_list_prefix - // stack: rlp_prefix_start, rlp_len, cur_len, retdest - %stack (rlp_prefix_start, rlp_len, cur_len, retdest) - -> (retdest, rlp_prefix_start, rlp_len, cur_len) - JUMP - - -// Part of the encode_node_branch function. Encodes the i'th child. -%macro encode_child(i) - // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - PUSH %%after_encode - DUP6 DUP6 DUP6 - // stack: node_payload_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %add_const($i) %mload_trie_data - // stack: child_i_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %jump(encode_or_hash_node) -%%after_encode: - // stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest - // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - SWAP1 - PUSH 32 DUP2 SUB - %jumpi(%%unpack) - // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest - DUP4 // rlp_pos - PUSH 160 - MSTORE_GENERAL - SWAP3 %increment SWAP3 // rlp_pos += 1 -%%unpack: - %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, old_len, retdest) - -> (rlp_pos, result, result_len, %%after_unpacking, - rlp_start, node_payload_ptr, encode_value, cur_len, retdest) - %jump(mstore_unpacking) -%%after_unpacking: - // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest -%endmacro - -global encode_node_extension: - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - SWAP3 %add_const(4) SWAP3 - %stack (node_type, node_payload_ptr, encode_value, cur_len) - -> (node_payload_ptr, encode_value, cur_len, encode_node_extension_after_encode_child, node_payload_ptr) - %add_const(2) %mload_trie_data - // stack: child_ptr, encode_value, cur_len, encode_node_extension_after_encode_child, node_payload_ptr, retdest - %jump(encode_or_hash_node) -encode_node_extension_after_encode_child: - // stack: result, result_len, cur_len, node_payload_ptr, retdest - %stack (result, result_len, cur_len, node_payload_ptr) -> (result, result_len, node_payload_ptr, cur_len) - %alloc_rlp_block - // stack: rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - PUSH encode_node_extension_after_hex_prefix // retdest - PUSH 0 // terminated - // stack: terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - DUP6 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - DUP7 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - DUP5 - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - %jump(hex_prefix_rlp) -encode_node_extension_after_hex_prefix: - // stack: rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len, retdest - // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - PUSH 32 DUP5 SUB - %jumpi(encode_node_extension_unpack) - // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - DUP1 // rlp_pos - PUSH 160 - MSTORE_GENERAL - %increment // rlp_pos += 1 -encode_node_extension_unpack: - %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len) - -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start, cur_len) - %jump(mstore_unpacking) -encode_node_extension_after_unpacking: - // stack: rlp_pos, rlp_start, cur_len, retdest - %prepend_rlp_list_prefix - %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) - -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) - JUMP - -global encode_node_leaf: - // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - POP - // stack: node_payload_ptr, encode_value, cur_len, retdest - %alloc_rlp_block - PUSH encode_node_leaf_after_hex_prefix // retdest - PUSH 1 // terminated - // stack: terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - DUP4 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - DUP5 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - DUP5 - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %jump(hex_prefix_rlp) -encode_node_leaf_after_hex_prefix: - // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest - SWAP2 - %add_const(2) // The value pointer starts at index 3, after num_nibbles and packed_nibbles. - // stack: value_ptr_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest - %mload_trie_data - // stack: value_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest - %stack (value_ptr, rlp_start, rlp_pos, encode_value, cur_len, retdest) - -> (encode_value, rlp_pos, value_ptr, cur_len, encode_node_leaf_after_encode_value, rlp_start, retdest) - JUMP -encode_node_leaf_after_encode_value: - // stack: rlp_end_pos, cur_len, rlp_start, retdest - // `TrieData` holds the node type, the number of nibbles, the nibbles, - // the pointer to the value and the value. - // We add 4 for the node type, the number of nibbles, the nibbles - // and the pointer to the value. - SWAP1 %add_const(4) - %stack(cur_len, rlp_end_pos, rlp_start, retdest) -> (rlp_end_pos, rlp_start, cur_len, retdest) - %prepend_rlp_list_prefix - %stack (rlp_prefix_start_pos, rlp_len, cur_len, retdest) - -> (retdest, rlp_prefix_start_pos, rlp_len, cur_len) - JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm deleted file mode 100644 index cd07c01fdc..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ /dev/null @@ -1,355 +0,0 @@ -// Hashing logic specific to a particular trie. - -global mpt_hash_state_trie: - // stack: cur_len, retdest - PUSH encode_account - %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - // stack: node_ptr, encode_account, cur_len, retdest - %jump(mpt_hash) - -%macro mpt_hash_state_trie - // stack: cur_len - PUSH %%after - SWAP1 - %jump(mpt_hash_state_trie) -%%after: -%endmacro - -global mpt_hash_storage_trie: - // stack: node_ptr, cur_len, retdest - %stack (node_ptr, cur_len) -> (node_ptr, encode_storage_value, cur_len) - %jump(mpt_hash) - -%macro mpt_hash_storage_trie - %stack (node_ptr, cur_len) -> (node_ptr, cur_len, %%after) - %jump(mpt_hash_storage_trie) -%%after: -%endmacro - -global mpt_hash_txn_trie: - // stack: cur_len, retdest - PUSH encode_txn - %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - // stack: node_ptr, encode_txn, cur_len, retdest - %jump(mpt_hash) - -%macro mpt_hash_txn_trie - // stack: cur_len - PUSH %%after - SWAP1 - %jump(mpt_hash_txn_trie) -%%after: -%endmacro - -global mpt_hash_receipt_trie: - // stack: cur_len, retdest - PUSH encode_receipt - %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - // stack: node_ptr, encode_receipt, cur_len, retdest - %jump(mpt_hash) - -%macro mpt_hash_receipt_trie - // stack: cur_len - PUSH %%after - SWAP1 - %jump(mpt_hash_receipt_trie) -%%after: -%endmacro - -global encode_account: - // stack: rlp_addr, value_ptr, cur_len, retdest - // First, we compute the length of the RLP data we're about to write. - // We also update the length of the trie data segment. - // The nonce and balance fields are variable-length, so we need to load them - // to determine their contribution, while the other two fields are fixed - // 32-bytes integers. - - // First, we add 4 to the trie data length, for the nonce, - // the balance, the storage pointer and the code hash. - SWAP2 %add_const(4) SWAP2 - - // Now, we start the encoding. - // stack: rlp_addr, value_ptr, cur_len, retdest - DUP2 %mload_trie_data // nonce = value[0] - %rlp_scalar_len - // stack: nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest - DUP3 %increment %mload_trie_data // balance = value[1] - %rlp_scalar_len - // stack: balance_rlp_len, nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest - PUSH 66 // storage_root and code_hash fields each take 1 + 32 bytes - ADD ADD - // stack: payload_len, rlp_addr, value_ptr, cur_len, retdest - SWAP1 - // stack: rlp_addr, payload_len, value_ptr, cur_len, retdest - DUP2 %rlp_list_len - // stack: list_len, rlp_addr, payload_len, value_ptr, cur_len, retdest - SWAP1 - // stack: rlp_addr, list_len, payload_len, value_ptr, cur_len, retdest - %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos_2, payload_len, value_ptr, cur_len, retdest - %encode_rlp_list_prefix - // stack: rlp_pos_3, value_ptr, cur_len, retdest - DUP2 %mload_trie_data // nonce = value[0] - // stack: nonce, rlp_pos_3, value_ptr, cur_len, retdest - SWAP1 %encode_rlp_scalar - // stack: rlp_pos_4, value_ptr, cur_len, retdest - DUP2 %increment %mload_trie_data // balance = value[1] - // stack: balance, rlp_pos_4, value_ptr, cur_len, retdest - SWAP1 %encode_rlp_scalar - // stack: rlp_pos_5, value_ptr, cur_len, retdest - DUP3 - DUP3 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] - // stack: storage_root_ptr, cur_len, rlp_pos_5, value_ptr, cur_len, retdest - - - PUSH debug_after_hash_storage_trie - POP - - // Hash storage trie. - %mpt_hash_storage_trie - // stack: storage_root_digest, new_len, rlp_pos_5, value_ptr, cur_len, retdest - %stack(storage_root_digest, new_len, rlp_pos_five, value_ptr, cur_len) -> (rlp_pos_five, storage_root_digest, value_ptr, new_len) - %encode_rlp_256 - // stack: rlp_pos_6, value_ptr, new_len, retdest - SWAP1 %add_const(3) %mload_trie_data // code_hash = value[3] - // stack: code_hash, rlp_pos_6, new_len, retdest - SWAP1 %encode_rlp_256 - // stack: rlp_pos_7, new_len, retdest - %stack(rlp_pos_7, new_len, retdest) -> (retdest, rlp_pos_7, new_len) - JUMP - -global encode_txn: - // stack: rlp_addr, value_ptr, cur_len, retdest - - // Load the txn_rlp_len which is at the beginning of value_ptr - DUP2 %mload_trie_data - // stack: txn_rlp_len, rlp_addr, value_ptr, cur_len, retdest - // We need to add 1+txn_rlp_len to the length of the trie data. - SWAP3 DUP4 %increment ADD - // stack: new_len, rlp_addr, value_ptr, txn_rlp_len, retdest - SWAP3 - SWAP2 %increment - // stack: txn_rlp_ptr=value_ptr+1, rlp_addr, txn_rlp_len, new_len, retdest - - %stack (txn_rlp_ptr, rlp_addr, txn_rlp_len) -> (rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) - // Encode the txn rlp prefix - // stack: rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest - %encode_rlp_multi_byte_string_prefix - // copy txn_rlp to the new block - // stack: rlp_addr, txn_rlp_len, txn_rlp_ptr, new_len, retdest - %stack (rlp_addr, txn_rlp_len, txn_rlp_ptr) -> ( - @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 - rlp_addr, // dest addr - txn_rlp_len, // mcpy len - txn_rlp_len, rlp_addr) - %build_kernel_address - SWAP1 - // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_addr, new_len, retdest - %memcpy_bytes - ADD - // stack new_rlp_addr, new_len, retdest - %stack(new_rlp_addr, new_len, retdest) -> (retdest, new_rlp_addr, new_len) - JUMP - -// We assume a receipt in memory is stored as: -// [payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]]. -// A log is [payload_len, address, num_topics, [topics], data_len, [data]]. -global encode_receipt: - // stack: rlp_addr, value_ptr, cur_len, retdest - // First, we add 261 to the trie data length for all values before the logs besides the type. - // These are: the payload length, the status, cum_gas_used, the bloom filter (256 elements), - // the length of the logs payload and the length of the logs. - SWAP2 %add_const(261) SWAP2 - // There is a double encoding! - // What we compute is: - // - either RLP(RLP(receipt)) for Legacy transactions - // - or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. - // First encode the wrapper prefix. - DUP2 %mload_trie_data - // stack: first_value, rlp_addr, value_ptr, cur_len, retdest - // The first value is either the transaction type or the payload length. - // Since the receipt contains at least the 256-bytes long bloom filter, payload_len > 3. - DUP1 %lt_const(3) %jumpi(encode_nonzero_receipt_type) - // If we are here, then the first byte is the payload length. - %rlp_list_len - // stack: rlp_receipt_len, rlp_addr, value_ptr, cur_len, retdest - SWAP1 %encode_rlp_multi_byte_string_prefix - // stack: rlp_addr, value_ptr, cur_len, retdest - -encode_receipt_after_type: - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - // Then encode the receipt prefix. - // `payload_ptr` is either `value_ptr` or `value_ptr+1`, depending on the transaction type. - DUP2 %mload_trie_data - // stack: payload_len, rlp_addr, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_list_prefix - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - // Encode status. - DUP2 %increment %mload_trie_data - // stack: status, rlp_addr, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_scalar - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - // Encode cum_gas_used. - DUP2 %add_const(2) %mload_trie_data - // stack: cum_gas_used, rlp_addr, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_scalar - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - // Encode bloom. - PUSH 256 // Bloom length. - DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA %build_kernel_address // MPT src address. - DUP3 - // stack: rlp_addr, SRC, 256, rlp_addr, payload_len_ptr, cur_len, retdest - %encode_rlp_string - // stack: rlp_addr, old_rlp_pos, payload_len_ptr, cur_len, retdest - SWAP1 POP - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - // Encode logs prefix. - DUP2 %add_const(259) %mload_trie_data - // stack: logs_payload_len, rlp_addr, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_list_prefix - // stack: rlp_addr, payload_len_ptr, cur_len, retdest - DUP2 %add_const(261) - // stack: logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest - DUP3 %add_const(260) %mload_trie_data - // stack: num_logs, logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest - PUSH 0 - -encode_receipt_logs_loop: - // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest - DUP2 DUP2 EQ - // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest - %jumpi(encode_receipt_end) - // We add 4 to the trie data length for the fixed size elements in the current log. - SWAP5 %add_const(4) SWAP5 - // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest - DUP3 DUP5 - // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - // Encode log prefix. - DUP2 %mload_trie_data - // stack: payload_len, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_list_prefix - // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - // Encode address. - DUP2 %increment %mload_trie_data - // stack: address, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - SWAP1 %encode_rlp_160 - // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - DUP2 %add_const(2) %mload_trie_data - // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - // Encode topics prefix. - DUP1 %mul_const(33) - // stack: topics_payload_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - DUP3 %encode_rlp_list_prefix - // stack: new_rlp_pos, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - SWAP2 POP - // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest - - // Add `num_topics` to the length of the trie data segment. - DUP1 SWAP9 - // stack: cur_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest - ADD SWAP8 - - // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - SWAP2 %add_const(3) - // stack: topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - PUSH 0 - -encode_receipt_topics_loop: - // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - DUP4 DUP2 EQ - // stack: j == num_topics, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - %jumpi(encode_receipt_topics_end) - // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - DUP2 DUP2 ADD - %mload_trie_data - // stack: current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - DUP4 - // stack: rlp_addr, current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - %encode_rlp_256 - // stack: new_rlp_pos, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - SWAP3 POP - // stack: j, topics_ptr, new_rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - %increment - %jump(encode_receipt_topics_loop) - -encode_receipt_topics_end: - // stack: num_topics, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - ADD - // stack: data_len_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - SWAP5 POP - // stack: rlp_addr, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest - SWAP5 POP - // stack: num_topics, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest - POP - // stack: i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest - // Encode data prefix. - DUP3 %mload_trie_data - // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest - - // Add `data_len` to the length of the trie data. - DUP1 SWAP7 ADD SWAP6 - - // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - DUP4 %increment DUP2 ADD - // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - SWAP4 %increment - // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - PUSH @SEGMENT_TRIE_DATA %build_kernel_address - // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - DUP6 - // stack: rlp_addr, SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - %encode_rlp_string - // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - SWAP4 POP - // stack: i, num_logs, next_log_ptr, new_rlp_pos, payload_len_ptr, cur_len'', retdest - %increment - %jump(encode_receipt_logs_loop) - -encode_receipt_end: - // stack: num_logs, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest - %pop3 - // stack: rlp_addr, payload_len_ptr, cur_len'', retdest - SWAP1 POP - // stack: rlp_addr, cur_len'', retdest - %stack(rlp_addr, new_len, retdest) -> (retdest, rlp_addr, new_len) - JUMP - -encode_nonzero_receipt_type: - // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest - // We have a nonlegacy receipt, so the type is also stored in the trie data segment. - SWAP3 %increment SWAP3 - // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest - DUP3 %increment %mload_trie_data - // stack: payload_len, txn_type, rlp_addr, value_ptr, retdest - // The transaction type is encoded in 1 byte - %increment %rlp_list_len - // stack: rlp_receipt_len, txn_type, rlp_addr, value_ptr, retdest - DUP3 %encode_rlp_multi_byte_string_prefix - // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest - DUP1 DUP3 - MSTORE_GENERAL - %increment - // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest - %stack (rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest) -> (rlp_addr, value_ptr, retdest) - // We replace `value_ptr` with `paylaod_len_ptr` so we can encode the rest of the data more easily - SWAP1 %increment SWAP1 - // stack: rlp_addr, payload_len_ptr, retdest - %jump(encode_receipt_after_type) - -global encode_storage_value: - // stack: rlp_addr, value_ptr, cur_len, retdest - SWAP1 %mload_trie_data SWAP1 - - // A storage value is a scalar, so we only need to add 1 to the trie data length. - SWAP2 %increment SWAP2 - - // stack: rlp_addr, value, cur_len, retdest - // The YP says storage trie is a map "... to the RLP-encoded 256-bit integer values" - // which seems to imply that this should be %encode_rlp_256. But %encode_rlp_scalar - // causes the tests to pass, so it seems storage values should be treated as variable- - // length after all. - %doubly_encode_rlp_scalar - // stack: rlp_addr', cur_len, retdest - %stack (rlp_addr, cur_len, retdest) -> (retdest, rlp_addr, cur_len) - JUMP - diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm deleted file mode 100644 index 0ca2458f0c..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ /dev/null @@ -1,131 +0,0 @@ -// Computes the RLP encoding of the hex-prefix encoding of the given nibble list -// and termination flag. Writes the result to @SEGMENT_RLP_RAW starting at the -// given position, and returns the updated position, i.e. a pointer to the next -// unused offset. -// -// Pre stack: rlp_start_addr, num_nibbles, packed_nibbles, terminated, retdest -// Post stack: rlp_end_addr -global hex_prefix_rlp: - DUP2 %assert_lt_const(65) - - PUSH 2 DUP3 DIV - // Compute the length of the hex-prefix string, in bytes: - // hp_len = num_nibbles / 2 + 1 = i + 1 - %increment - // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - - // Write the RLP header. - DUP1 %gt_const(55) %jumpi(rlp_header_large) - DUP1 %gt_const(1) %jumpi(rlp_header_medium) - - // The hex-prefix is a single byte. It must be <= 127, since its first - // nibble only has two bits. So this is the "small" RLP string case, where - // the byte is its own RLP encoding. - // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - POP -first_byte: - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - // get the first nibble, if num_nibbles is odd, or zero otherwise - SWAP2 - // stack: packed_nibbles, num_nibbles, rlp_addr, terminated, retdest - DUP2 - PUSH 2 DUP2 MOD - // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_addr, terminated, retdest - SWAP1 SUB - %mul_const(4) - SHR - // stack: first_nibble_or_zero, num_nibbles, rlp_addr, terminated, retdest - SWAP2 - // stack: rlp_addr, num_nibbles, first_nibble_or_zero, terminated, retdest - SWAP3 - // stack: terminated, num_nibbles, first_nibble_or_zero, rlp_addr, retdest - %mul_const(2) - // stack: terminated * 2, num_nibbles, first_nibble_or_zero, rlp_addr, retdest - SWAP1 - // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_addr, retdest - %mod_const(2) // parity - ADD - // stack: parity + terminated * 2, first_nibble_or_zero, rlp_addr, retdest - %mul_const(16) - ADD - // stack: first_byte, rlp_addr, retdest - DUP2 - %swap_mstore - %increment - // stack: rlp_addr', retdest - SWAP1 - JUMP - -remaining_bytes: - // stack: rlp_addr, num_nibbles, packed_nibbles, retdest - SWAP2 - PUSH @U256_MAX - // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_addr, ret_dest - SWAP1 SWAP2 - PUSH 2 DUP2 MOD - // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_addr, ret_dest - SWAP1 SUB DUP1 - // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest - %div2 - // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest - SWAP2 SWAP1 - // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest - %mul_const(4) - // stack: 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest - PUSH 256 SUB - // stack: 256 - 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest - SHR - // stack: mask, rem_bytes, packed_nibbles, rlp_addr, ret_dest - SWAP1 SWAP2 - AND - %stack(remaining_nibbles, rem_bytes, rlp_addr) -> (rlp_addr, remaining_nibbles, rem_bytes) - %mstore_unpacking - SWAP1 - JUMP - - -rlp_header_medium: - // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - %add_const(0x80) // value = 0x80 + hp_len - DUP2 - %swap_mstore - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - // rlp_addr += 1 - %increment - - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest - PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest - SWAP4 SWAP5 SWAP6 - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest - - %jump(first_byte) - -rlp_header_large: - // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - // In practice hex-prefix length will never exceed 256, so the length of the - // length will always be 1 byte in this case. - - DUP2 // rlp_addr - PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8 - MSTORE_GENERAL - - // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - DUP2 %increment - %swap_mstore - - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - // rlp_addr += 2 - %add_const(2) - - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest - SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest - PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest - SWAP4 SWAP5 SWAP6 - // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest - - %jump(first_byte) diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert.asm deleted file mode 100644 index 34889a33f8..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert.asm +++ /dev/null @@ -1,89 +0,0 @@ -// Return a copy of the given node, with the given key set to the given value. -// -// Pre stack: node_ptr, num_nibbles, key, value_ptr, retdest -// Post stack: updated_node_ptr -global mpt_insert: - // stack: node_ptr, num_nibbles, key, value_ptr, retdest - DUP1 %mload_trie_data - // stack: node_type, node_ptr, num_nibbles, key, value_ptr, retdest - // Increment node_ptr, so it points to the node payload instead of its type. - SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - - DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_insert_empty) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_insert_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_insert_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_insert_leaf) - - // There's still the MPT_NODE_HASH case, but if we hit a hash node, - // it means the prover failed to provide necessary Merkle data, so panic. -global mpt_insert_hash_node: - PANIC - -mpt_insert_empty: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - %pop2 - // stack: num_nibbles, key, value_ptr, retdest - // We will append a new leaf node to our MPT tape and return a pointer to it. - %get_trie_data_size - // stack: leaf_ptr, num_nibbles, key, value_ptr, retdest - PUSH @MPT_NODE_LEAF %append_to_trie_data - // stack: leaf_ptr, num_nibbles, key, value_ptr, retdest - SWAP1 %append_to_trie_data - // stack: leaf_ptr, key, value_ptr, retdest - SWAP1 %append_to_trie_data - // stack: leaf_ptr, value_ptr, retdest - SWAP1 %append_to_trie_data - // stack: leaf_ptr, retdest - SWAP1 - JUMP - -mpt_insert_branch: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - POP - - //stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - - // At this point, we branch based on whether the key terminates with this branch node. - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - DUP2 %jumpi(mpt_insert_branch_nonterminal) - - // The key terminates here, so the value will be placed right in our (updated) branch node. - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - SWAP3 - // stack: value_ptr, num_nibbles, key, node_payload_ptr, retdest - DUP4 %add_const(16) - // stack: branch_value_ptr_ptr, value_ptr, num_nibbles, key, node_payload_ptr, retdest - %mstore_trie_data - // stack: num_nibbles, key, node_payload_ptr, retdest - %pop2 - // stack: node_payload_ptr, retdest - PUSH 1 SWAP1 SUB - // stack: branch_ptr, retdest - SWAP1 - JUMP - -mpt_insert_branch_nonterminal: - // The key continues, so we split off the first (most significant) nibble, - // and recursively insert into the child associated with that nibble. - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - %stack (node_payload_ptr, num_nibbles, key) -> (num_nibbles, key, node_payload_ptr) - %split_first_nibble - // stack: first_nibble, num_nibbles, key, node_payload_ptr, value_ptr, retdest - DUP4 ADD - // stack: child_ptr_ptr, num_nibbles, key, node_payload_ptr, value_ptr, retdest - // Replace node_payload_ptr with branch pointer - SWAP3 PUSH 1 SWAP1 SUB SWAP3 - %stack (child_ptr_ptr, num_nibbles, key, updated_branch_ptr, value_ptr) - -> (child_ptr_ptr, num_nibbles, key, value_ptr, - mpt_insert_branch_nonterminal_after_recursion, - child_ptr_ptr, updated_branch_ptr) - %mload_trie_data // Deref child_ptr_ptr, giving child_ptr - %jump(mpt_insert) - -mpt_insert_branch_nonterminal_after_recursion: - // stack: updated_child_ptr, child_ptr_ptr, updated_branch_ptr, retdest - SWAP1 %mstore_trie_data // Store the pointer to the updated child. - // stack: updated_branch_ptr, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm deleted file mode 100644 index 21a4b7558b..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_extension.asm +++ /dev/null @@ -1,213 +0,0 @@ -/* -Insert into an extension node. -The high-level logic can be expressed with the following pseudocode: - -common_len, common_key, node_len, node_key, insert_len, insert_key = - split_common_prefix(node_len, node_key, insert_len, insert_key) - -if node_len == 0: - new_node = insert(node_child, insert_len, insert_key, insert_value) -else: - new_node = [MPT_TYPE_BRANCH] + [0] * 17 - - // Process the node's child. - if node_len > 1: - // The node key continues with multiple nibbles left, so we can't place - // node_child directly in the branch, but need an extension for it. - node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) - new_node[node_key_first + 1] = [MPT_TYPE_EXTENSION, node_len, node_key, node_child] - else: - // The remaining node_key is a single nibble, so we can place node_child directly in the branch. - new_node[node_key + 1] = node_child - - // Process the inserted entry. - if insert_len > 0: - // The insert key continues. Add a leaf node for it. - insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) - new_node[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] - else: - new_node[17] = insert_value - -if common_len > 0: - return [MPT_TYPE_EXTENSION, common_len, common_key, new_node] -else: - return new_node -*/ - -global mpt_insert_extension: - // stack: node_type, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest - POP - // stack: node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest - - // We start by loading the extension node's three fields: node_len, node_key, node_child_ptr - DUP1 %add_const(2) %mload_trie_data - // stack: node_child_ptr, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest - %stack (node_child_ptr, node_payload_ptr, insert_len, insert_key) - -> (node_payload_ptr, insert_len, insert_key, node_child_ptr) - // stack: node_payload_ptr, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - DUP1 %increment %mload_trie_data - // stack: node_key, node_payload_ptr, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - SWAP1 %mload_trie_data - // stack: node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - - // Next, we split off any key prefix which is common to the node's key and the inserted key. - %split_common_prefix - // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - - // Now we branch based on whether the node key continues beyond the common prefix. - DUP3 %jumpi(node_key_continues) - - // The node key does not continue. In this case we recurse. Pseudocode: - // new_node = insert(node_child, insert_len, insert_key, insert_value) - // and then proceed to maybe_add_extension_for_common_key. - // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - PUSH maybe_add_extension_for_common_key - DUP9 // insert_value_ptr - DUP8 // insert_key - DUP8 // insert_len - DUP11 // node_child_ptr - %jump(mpt_insert) - -node_key_continues: - // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // Allocate new_node, a branch node which is initially empty - // Pseudocode: new_node = [MPT_TYPE_BRANCH] + [0] * 17 - %get_trie_data_size // pointer to the branch node we're about to create - PUSH @MPT_NODE_BRANCH %append_to_trie_data - - PUSH 0 - // Increment trie data size by 17 - %get_trie_data_size - // stack: trie_data_size, 0 - DUP1 - %add_const(17) - %set_trie_data_size - - // stack: trie_data_size, 0 - - // Write 17 consecutive 0s at once - PUSH @SEGMENT_TRIE_DATA %build_kernel_address - MSTORE_32BYTES_17 - POP - -process_node_child: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // We want to check if node_len > 1. We already know node_len > 0 since we're in node_key_continues, - // so it suffices to check 1 - node_len != 0 - DUP4 // node_len - PUSH 1 SUB - %jumpi(node_key_continues_multiple_nibbles) - - // If we got here, node_len = 1. - // Pseudocode: new_node[node_key + 1] = node_child - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - DUP8 // node_child_ptr - DUP2 // new_node_ptr - %increment - DUP7 // node_key - ADD - %mstore_trie_data - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - %jump(process_inserted_entry) - -node_key_continues_multiple_nibbles: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // Pseudocode: node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) - // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. - // Instead we will duplicate them, and leave the old ones alone; they won't be used. - DUP5 DUP5 - // stack: node_len, node_key, new_node_ptr, ... - %split_first_nibble - // stack: node_key_first, node_len, node_key, new_node_ptr, ... - - // Pseudocode: new_node[node_key_first + 1] = [MPT_TYPE_EXTENSION, node_len, node_key, node_child] - %get_trie_data_size // pointer to the extension node we're about to create - // stack: ext_node_ptr, node_key_first, node_len, node_key, new_node_ptr, ... - PUSH @MPT_NODE_EXTENSION %append_to_trie_data - // stack: ext_node_ptr, node_key_first, node_len, node_key, new_node_ptr, ... - SWAP2 %append_to_trie_data // Append node_len - // stack: node_key_first, ext_node_ptr, node_key, new_node_ptr, ... - SWAP2 %append_to_trie_data // Append node_key - // stack: ext_node_ptr, node_key_first, new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - DUP10 %append_to_trie_data // Append node_child_ptr - - SWAP1 - // stack: node_key_first, ext_node_ptr, new_node_ptr, ... - DUP3 // new_node_ptr - ADD - %increment - // stack: new_node_ptr + node_key_first + 1, ext_node_ptr, new_node_ptr, ... - %mstore_trie_data - %jump(process_inserted_entry) - -process_inserted_entry: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - DUP6 // insert_len - %jumpi(insert_key_continues) - - // If we got here, insert_len = 0, so we store the inserted value directly in our new branch node. - // Pseudocode: new_node[17] = insert_value - DUP9 // insert_value_ptr - DUP2 // new_node_ptr - %add_const(17) - %mstore_trie_data - %jump(maybe_add_extension_for_common_key) - -insert_key_continues: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // Pseudocode: insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) - // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. - // Instead we will duplicate them, and leave the old ones alone; they won't be used. - DUP7 DUP7 - // stack: insert_len, insert_key, new_node_ptr, ... - %split_first_nibble - // stack: insert_key_first, insert_len, insert_key, new_node_ptr, ... - - // Pseudocode: new_node[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] - %get_trie_data_size // pointer to the leaf node we're about to create - // stack: leaf_node_ptr, insert_key_first, insert_len, insert_key, new_node_ptr, ... - PUSH @MPT_NODE_LEAF %append_to_trie_data - // stack: leaf_node_ptr, insert_key_first, insert_len, insert_key, new_node_ptr, ... - SWAP2 %append_to_trie_data // Append insert_len - // stack: insert_key_first, leaf_node_ptr, insert_key, new_node_ptr, ... - SWAP2 %append_to_trie_data // Append insert_key - // stack: leaf_node_ptr, insert_key_first, new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - DUP11 %append_to_trie_data // Append insert_value_ptr - - SWAP1 - // stack: insert_key_first, leaf_node_ptr, new_node_ptr, ... - DUP3 // new_node_ptr - ADD - %increment - // stack: new_node_ptr + insert_key_first + 1, leaf_node_ptr, new_node_ptr, ... - %mstore_trie_data - %jump(maybe_add_extension_for_common_key) - -maybe_add_extension_for_common_key: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // If common_len > 0, we need to add an extension node. - DUP2 %jumpi(add_extension_for_common_key) - // Otherwise, we simply return new_node_ptr. - SWAP8 - %pop8 - // stack: new_node_ptr, retdest - SWAP1 - JUMP - -add_extension_for_common_key: - // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - // Pseudocode: return [MPT_TYPE_EXTENSION, common_len, common_key, new_node] - %get_trie_data_size // pointer to the extension node we're about to create - // stack: extension_ptr, new_node_ptr, common_len, common_key, ... - PUSH @MPT_NODE_EXTENSION %append_to_trie_data - SWAP2 %append_to_trie_data // Append common_len to our node - // stack: new_node_ptr, extension_ptr, common_key, ... - SWAP2 %append_to_trie_data // Append common_key to our node - // stack: extension_ptr, new_node_ptr, ... - SWAP1 %append_to_trie_data // Append new_node_ptr to our node - // stack: extension_ptr, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest - SWAP6 - %pop6 - // stack: extension_ptr, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm deleted file mode 100644 index 806fc0ddbd..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_leaf.asm +++ /dev/null @@ -1,205 +0,0 @@ -/* -Insert into a leaf node. -The high-level logic can be expressed with the following pseudocode: - -if node_len == insert_len && node_key == insert_key: - return Leaf[node_key, insert_value] - -common_len, common_key, node_len, node_key, insert_len, insert_key = - split_common_prefix(node_len, node_key, insert_len, insert_key) - -branch = [MPT_TYPE_BRANCH] + [0] * 17 - -// Process the node's entry. -if node_len > 0: - node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) - branch[node_key_first + 1] = [MPT_TYPE_LEAF, node_len, node_key, node_value] -else: - branch[17] = node_value - -// Process the inserted entry. -if insert_len > 0: - insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) - branch[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] -else: - branch[17] = insert_value - -// Add an extension node if there is a common prefix. -if common_len > 0: - return [MPT_TYPE_EXTENSION, common_len, common_key, branch] -else: - return branch -*/ - -global mpt_insert_leaf: - // stack: node_type, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest - POP - // stack: node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest - %stack (node_payload_ptr, insert_len, insert_key) -> (insert_len, insert_key, node_payload_ptr) - // stack: insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - DUP3 %increment %mload_trie_data - // stack: node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - DUP4 %mload_trie_data - // stack: node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - - // If the keys match, i.e. node_len == insert_len && node_key == insert_key, - // then we're simply replacing the leaf node's value. Since this is a common - // case, it's best to detect it early. Calling %split_common_prefix could be - // expensive as leaf keys tend to be long. - DUP1 DUP4 EQ // node_len == insert_len - DUP3 DUP6 EQ // node_key == insert_key - MUL // Cheaper than AND - // stack: keys_match, node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - %jumpi(keys_match) - - // Replace node_payload_ptr with node_value, which is node_payload[2]. - // stack: node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - SWAP4 - %add_const(2) - %mload_trie_data - SWAP4 - // stack: node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - - // Split off any common prefix between the node key and the inserted key. - %split_common_prefix - // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - - // For the remaining cases, we will need a new branch node since the two keys diverge. - // We may also need an extension node above it (if common_len > 0); we will handle that later. - // For now, we allocate the branch node, initially with no children or value. - %get_trie_data_size // pointer to the branch node we're about to create - PUSH @MPT_NODE_BRANCH %append_to_trie_data - - PUSH 0 - // Increment trie data size by 17 - %get_trie_data_size - // stack: trie_data_size, 0 - DUP1 - %add_const(17) - %set_trie_data_size - - // stack: trie_data_size, 0 - - // Write 17 consecutive 0s at once - PUSH @SEGMENT_TRIE_DATA %build_kernel_address - MSTORE_32BYTES_17 - POP - - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - - // Now, we branch based on whether each key continues beyond the common - // prefix, starting with the node key. - -process_node_entry: - DUP4 // node_len - %jumpi(node_key_continues) - - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // branch[17] = node_value_ptr - DUP8 // node_value_ptr - DUP2 // branch_ptr - %add_const(17) - %mstore_trie_data - -process_inserted_entry: - DUP6 // insert_len - %jumpi(insert_key_continues) - - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // branch[17] = insert_value_ptr - DUP9 // insert_value_ptr - DUP2 // branch_ptr - %add_const(17) - %mstore_trie_data - -maybe_add_extension_for_common_key: - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // If common_len > 0, we need to add an extension node. - DUP2 %jumpi(add_extension_for_common_key) - // Otherwise, we simply return branch_ptr. - SWAP8 - %pop8 - // stack: branch_ptr, retdest - SWAP1 - JUMP - -add_extension_for_common_key: - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // Pseudocode: return [MPT_TYPE_EXTENSION, common_len, common_key, branch] - %get_trie_data_size // pointer to the extension node we're about to create - // stack: extension_ptr, branch_ptr, common_len, common_key, ... - PUSH @MPT_NODE_EXTENSION %append_to_trie_data - SWAP2 %append_to_trie_data // Append common_len to our node - // stack: branch_ptr, extension_ptr, common_key, ... - SWAP2 %append_to_trie_data // Append common_key to our node - // stack: extension_ptr, branch_ptr, ... - SWAP1 %append_to_trie_data // Append branch_ptr to our node - // stack: extension_ptr, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - SWAP6 - %pop6 - // stack: extension_ptr, retdest - SWAP1 - JUMP - -node_key_continues: - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // branch[node_key_first + 1] = Leaf[node_len, node_key, node_value] - // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. - // Instead we will duplicate them, and leave the old ones alone; they won't be used. - DUP5 DUP5 - // stack: node_len, node_key, branch_ptr, ... - %split_first_nibble - // stack: node_key_first, node_len, node_key, branch_ptr, ... - %get_trie_data_size // pointer to the leaf node we're about to create - // stack: leaf_ptr, node_key_first, node_len, node_key, branch_ptr, ... - SWAP1 - DUP5 // branch_ptr - %increment // Skip over node type field - ADD // Add node_key_first - %mstore_trie_data - // stack: node_len, node_key, branch_ptr, ... - PUSH @MPT_NODE_LEAF %append_to_trie_data - %append_to_trie_data // Append node_len to our leaf node - %append_to_trie_data // Append node_key to our leaf node - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - DUP8 %append_to_trie_data // Append node_value_ptr to our leaf node - %jump(process_inserted_entry) - -insert_key_continues: - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // branch[insert_key_first + 1] = Leaf[insert_len, insert_key, insert_value] - // To minimize stack manipulation, we won't actually mutate the insert_len, insert_key variables in our stack. - // Instead we will duplicate them, and leave the old ones alone; they won't be used. - DUP7 DUP7 - // stack: insert_len, insert_key, branch_ptr, ... - %split_first_nibble - // stack: insert_key_first, insert_len, insert_key, branch_ptr, ... - %get_trie_data_size // pointer to the leaf node we're about to create - // stack: leaf_ptr, insert_key_first, insert_len, insert_key, branch_ptr, ... - SWAP1 - DUP5 // branch_ptr - %increment // Skip over node type field - ADD // Add insert_key_first - %mstore_trie_data - // stack: insert_len, insert_key, branch_ptr, ... - PUSH @MPT_NODE_LEAF %append_to_trie_data - %append_to_trie_data // Append insert_len to our leaf node - %append_to_trie_data // Append insert_key to our leaf node - // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - DUP9 %append_to_trie_data // Append insert_value_ptr to our leaf node - %jump(maybe_add_extension_for_common_key) - -keys_match: - // The keys match exactly, so we simply replace the leaf value with the new value. - // stack: node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr, retdest - %stack (node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr) - -> (node_payload_ptr, node_len, node_key, insert_value_ptr) - // stack: node_payload_ptr, common_len, common_key, insert_value_ptr, retdest - DUP4 DUP2 - %add_const(2) - %mstore_trie_data - %stack (node_payload_ptr, common_len, common_key, insert_value_ptr, retdest) -> (node_payload_ptr, retdest) - PUSH 1 SWAP1 SUB - // stack: leaf_ptr, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm deleted file mode 100644 index 71f78ec5bd..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ /dev/null @@ -1,95 +0,0 @@ -// Insertion logic specific to a particular trie. - -// Mutate the state trie, inserting the given key-value pair. -// Pre stack: key, value_ptr, retdest -// Post stack: (empty) -// TODO: Have this take an address and do %mpt_insert_state_trie? To match mpt_read_state_trie. -global mpt_insert_state_trie: - // stack: key, value_ptr, retdest - %stack (key, value_ptr) - -> (key, value_ptr, mpt_insert_state_trie_save) - PUSH 64 // num_nibbles - %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - // stack: state_root_ptr, num_nibbles, key, value_ptr, mpt_insert_state_trie_save, retdest - %jump(mpt_insert) -mpt_insert_state_trie_save: - // stack: updated_node_ptr, retdest - %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - JUMP - -%macro mpt_insert_state_trie - %stack (key, value_ptr) -> (key, value_ptr, %%after) - %jump(mpt_insert_state_trie) -%%after: -%endmacro - -// Insert a node in the transaction trie. The payload -// must be pointing to the rlp encoded txn -// Pre stack: key, txn_rlp_ptr, redest -// Post stack: (empty) -global mpt_insert_txn_trie: - // stack: key=rlp(key), num_nibbles, txn_rlp_ptr, retdest - %stack (key, num_nibbles, txn_rlp_ptr) - -> (num_nibbles, key, txn_rlp_ptr, mpt_insert_txn_trie_save) - %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - // stack: txn_trie_root_ptr, num_nibbles, key, txn_rlp_ptr, mpt_insert_state_trie_save, retdest - %jump(mpt_insert) - -mpt_insert_txn_trie_save: - // stack: updated_node_ptr, retdest - %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - JUMP - -%macro mpt_insert_txn_trie - %stack (key, txn_rpl_ptr) -> (key, txn_rlp_ptr, %%after) - %jump(mpt_insert_txn_trie) -%%after: -%endmacro - -global mpt_insert_receipt_trie: - // stack: num_nibbles, scalar, value_ptr, retdest - %stack (num_nibbles, scalar, value_ptr) - -> (num_nibbles, scalar, value_ptr, mpt_insert_receipt_trie_save) - // The key is the scalar, which is an RLP encoding of the transaction number - // stack: num_nibbles, key, value_ptr, mpt_insert_receipt_trie_save, retdest - %mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - // stack: receipt_root_ptr, num_nibbles, key, value_ptr, mpt_insert_receipt_trie_save, retdest - %jump(mpt_insert) -mpt_insert_receipt_trie_save: - // stack: updated_node_ptr, retdest - %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) - JUMP - -%macro mpt_insert_receipt_trie - %stack (num_nibbles, key, value_ptr) -> (num_nibbles, key, value_ptr, %%after) - %jump(mpt_insert_receipt_trie) -%%after: -%endmacro - -// Pre stack: scalar, retdest -// Post stack: rlp_scalar -global scalar_to_rlp: - // stack: scalar, retdest - %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - // stack: init_addr, scalar, retdest - SWAP1 DUP2 - %encode_rlp_scalar - // stack: addr', init_addr, retdest - // Now our rlp_encoding is in RlpRaw. - // Set new RlpRaw data size - DUP1 %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - DUP2 DUP2 SUB // len of the key - // stack: len, addr', init_addr, retdest - DUP3 - MLOAD_32BYTES - // stack: packed_key, addr', init_addr, retdest - SWAP2 %pop2 - // stack: key, retdest - SWAP1 - JUMP - -%macro scalar_to_rlp - %stack (scalar) -> (scalar, %%after) - %jump(scalar_to_rlp) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/read.asm b/evm/src/cpu/kernel/asm/mpt/read.asm deleted file mode 100644 index 4a57dd4db2..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/read.asm +++ /dev/null @@ -1,152 +0,0 @@ -// Given an address, return a pointer to the associated account data, which -// consists of four words (nonce, balance, storage_root, code_hash), in the -// state trie. Returns null if the address is not found. -global mpt_read_state_trie: - // stack: addr, retdest - %addr_to_state_key - // stack: key, retdest - PUSH 64 // num_nibbles - %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // node_ptr - // stack: node_ptr, num_nibbles, key, retdest - %jump(mpt_read) - -// Convenience macro to call mpt_read_state_trie and return where we left off. -%macro mpt_read_state_trie - %stack (addr) -> (addr, %%after) - %jump(mpt_read_state_trie) -%%after: -%endmacro - -// Read a value from a MPT. -// -// Arguments: -// - the virtual address of the trie to search in -// - the number of nibbles in the key (should start at 64) -// - the key, as a U256 -// - return destination -// -// This function returns a pointer to the value, or 0 if the key is not found. -global mpt_read: - // stack: node_ptr, num_nibbles, key, retdest - DUP1 - %mload_trie_data - // stack: node_type, node_ptr, num_nibbles, key, retdest - // Increment node_ptr, so it points to the node payload instead of its type. - SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - - DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_read_empty) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_read_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_read_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_read_leaf) - - // There's still the MPT_NODE_HASH case, but if we hit a hash node, - // it means the prover failed to provide necessary Merkle data, so panic. -global mpt_read_hash_node: - PANIC - -global mpt_read_empty: - // Return 0 to indicate that the value was not found. - %stack (node_type, node_payload_ptr, num_nibbles, key, retdest) - -> (retdest, 0) - JUMP - -global mpt_read_branch: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, retdest - DUP2 // num_nibbles - ISZERO - // stack: num_nibbles == 0, node_payload_ptr, num_nibbles, key, retdest - %jumpi(mpt_read_branch_end_of_key) - - // We have not reached the end of the key, so we descend to one of our children. - // stack: node_payload_ptr, num_nibbles, key, retdest - %stack (node_payload_ptr, num_nibbles, key) - -> (num_nibbles, key, node_payload_ptr) - // stack: num_nibbles, key, node_payload_ptr, retdest - %split_first_nibble - %stack (first_nibble, num_nibbles, key, node_payload_ptr) - -> (node_payload_ptr, first_nibble, num_nibbles, key) - // child_ptr = load(node_payload_ptr + first_nibble) - ADD %mload_trie_data - // stack: child_ptr, num_nibbles, key, retdest - %jump(mpt_read) // recurse - -global mpt_read_branch_end_of_key: - %stack (node_payload_ptr, num_nibbles, key, retdest) -> (node_payload_ptr, retdest) - // stack: node_payload_ptr, retdest - %add_const(16) // skip over the 16 child nodes - // stack: value_ptr_ptr, retdest - %mload_trie_data - // stack: value_ptr, retdest - SWAP1 - JUMP - -global mpt_read_extension: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - %stack (node_type, node_payload_ptr, num_nibbles, key) - -> (num_nibbles, key, node_payload_ptr) - // stack: num_nibbles, key, node_payload_ptr, retdest - DUP3 %mload_trie_data - // stack: node_num_nibbles, num_nibbles, key, node_payload_ptr, retdest - SWAP1 - SUB - // stack: future_nibbles, key, node_payload_ptr, retdest - DUP2 DUP2 - // stack: future_nibbles, key, future_nibbles, key, node_payload_ptr, retdest - %mul_const(4) SHR // key_part = key >> (future_nibbles * 4) - DUP1 - // stack: key_part, key_part, future_nibbles, key, node_payload_ptr, retdest - DUP5 %increment %mload_trie_data - // stack: node_key, key_part, key_part, future_nibbles, key, node_payload_ptr, retdest - EQ // does the first part of our key match the node's key? - %jumpi(mpt_read_extension_found) -global mpt_read_extension_not_found: - // Not found; return 0. - %stack (key_part, future_nibbles, key, node_payload_ptr, retdest) -> (retdest, 0) - JUMP -mpt_read_extension_found: - // stack: key_part, future_nibbles, key, node_payload_ptr, retdest - DUP2 %mul_const(4) SHL // key_part_shifted = (key_part << (future_nibbles * 4)) - // stack: key_part_shifted, future_nibbles, key, node_payload_ptr, retdest - %stack (key_part_shifted, future_nibbles, key) - -> (key, key_part_shifted, future_nibbles) - SUB // key -= key_part_shifted - // stack: key, future_nibbles, node_payload_ptr, retdest - SWAP2 - // stack: node_payload_ptr, future_nibbles, key, retdest - %add_const(2) // child pointer is third field of extension node - %mload_trie_data - // stack: child_ptr, future_nibbles, key, retdest - %jump(mpt_read) // recurse - -mpt_read_leaf: - // stack: node_type, node_payload_ptr, num_nibbles, key, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, retdest - DUP1 %mload_trie_data - // stack: node_num_nibbles, node_payload_ptr, num_nibbles, key, retdest - DUP2 %increment %mload_trie_data - // stack: node_key, node_num_nibbles, node_payload_ptr, num_nibbles, key, retdest - SWAP3 - // stack: num_nibbles, node_num_nibbles, node_payload_ptr, node_key, key, retdest - EQ - %stack (num_nibbles_match, node_payload_ptr, node_key, key) - -> (key, node_key, num_nibbles_match, node_payload_ptr) - EQ - AND - // stack: keys_match && num_nibbles_match, node_payload_ptr, retdest - %jumpi(mpt_read_leaf_found) -global mpt_read_leaf_not_found: - // Not found; return 0. - %stack (node_payload_ptr, retdest) -> (retdest, 0) - JUMP -mpt_read_leaf_found: - // stack: node_payload_ptr, retdest - %add_const(2) // The value pointer is located after num_nibbles and the key. - // stack: value_ptr_ptr, retdest - %mload_trie_data - // stack: value_ptr, retdest - SWAP1 - JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm deleted file mode 100644 index 84d8d0efc9..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_read.asm +++ /dev/null @@ -1,56 +0,0 @@ -%macro sload_current - %stack (slot) -> (slot, %%after) - %jump(sload_current) -%%after: -%endmacro - -global sload_current: - %stack (slot) -> (slot, after_storage_read) - %slot_to_storage_key - // stack: storage_key, after_storage_read - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, after_storage_read - %jump(mpt_read) - -global after_storage_read: - // stack: value_ptr, retdest - DUP1 %jumpi(storage_key_exists) - - // Storage key not found. Return default value_ptr = 0, - // which derefs to 0 since @SEGMENT_TRIE_DATA[0] = 0. - %stack (value_ptr, retdest) -> (retdest, 0) - JUMP - -global storage_key_exists: - // stack: value_ptr, retdest - %mload_trie_data - // stack: value, retdest - SWAP1 - JUMP - -// Read a word from the current account's storage trie. -// -// Pre stack: kexit_info, slot -// Post stack: value - -global sys_sload: - // stack: kexit_info, slot - SWAP1 - DUP1 - // stack: slot, slot, kexit_info - %sload_current - - %stack (value, slot, kexit_info) -> (slot, value, kexit_info, value) - %address - // stack: addr, slot, value, kexit_info, value - %insert_accessed_storage_keys - // stack: cold_access, old_value, kexit_info, value - SWAP1 POP - // stack: cold_access, kexit_info, value - %mul_const(@GAS_COLDSLOAD_MINUS_WARMACCESS) - %add_const(@GAS_WARMACCESS) - %charge_gas - // stack: kexit_info, value - EXIT_KERNEL - diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm deleted file mode 100644 index 08270dfa9e..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ /dev/null @@ -1,144 +0,0 @@ -// Write a word to the current account's storage trie. -// -// Pre stack: kexit_info, slot, value -// Post stack: (empty) - -global sys_sstore: - %check_static - DUP1 %leftover_gas %le_const(@GAS_CALLSTIPEND) %jumpi(fault_exception) - %stack (kexit_info, slot, value) -> (slot, kexit_info, slot, value) - %sload_current - %address - %stack (addr, current_value, kexit_info, slot, value) -> (addr, slot, current_value, current_value, kexit_info, slot, value) - %insert_accessed_storage_keys - // stack: cold_access, original_value, current_value, kexit_info, slot, value - %mul_const(@GAS_COLDSLOAD) - - // Check for warm access. - %stack (gas, original_value, current_value, kexit_info, slot, value) -> - (value, current_value, current_value, original_value, gas, original_value, current_value, kexit_info, slot, value) - EQ SWAP2 EQ ISZERO - // stack: current_value==original_value, value==current_value, gas, original_value, current_value, kexit_info, slot, value) - ADD // OR - %jumpi(sstore_warm) - - // Check for sset (set a zero storage slot to a non-zero value). - // stack: gas, original_value, current_value, kexit_info, slot, value - DUP2 ISZERO %mul_const(@GAS_SSET) ADD - - // Check for sreset (set a non-zero storage slot to a non-zero value). - // stack: gas, original_value, current_value, kexit_info, slot, value - DUP2 ISZERO ISZERO %mul_const(@GAS_SRESET) ADD - %jump(sstore_charge_gas) - -sstore_warm: - // stack: gas, original_value, current_value, kexit_info, slot, value) - %add_const(@GAS_WARMACCESS) - -sstore_charge_gas: - %stack (gas, original_value, current_value, kexit_info, slot, value) -> (gas, kexit_info, current_value, value, original_value, slot) - %charge_gas - -sstore_refund: - %stack (kexit_info, current_value, value, original_value, slot) -> (current_value, value, current_value, value, original_value, slot, kexit_info) - EQ %jumpi(sstore_no_refund) - %stack (current_value, value, original_value, slot, kexit_info) -> (current_value, original_value, current_value, value, original_value, slot, kexit_info) - EQ %jumpi(sstore_refund_original) - %stack (current_value, value, original_value, slot, kexit_info) -> (original_value, current_value, value, original_value, slot, kexit_info) - ISZERO %jumpi(sstore_dirty_reset) - %stack (current_value, value, original_value, slot, kexit_info) -> (current_value, current_value, value, original_value, slot, kexit_info) - ISZERO %jumpi(sstore_dirty_clear1) - %stack (current_value, value, original_value, slot, kexit_info) -> (value, current_value, value, original_value, slot, kexit_info) - ISZERO %jumpi(sstore_dirty_clear2) - %jump(sstore_dirty_reset) - -sstore_dirty_clear1: - PUSH @REFUND_SCLEAR PUSH 0 SUB %refund_gas - %jump(sstore_dirty_reset) - -sstore_dirty_clear2: - PUSH @REFUND_SCLEAR %refund_gas - -sstore_dirty_reset: - %stack (current_value, value, original_value, slot, kexit_info) -> (original_value, value, current_value, value, original_value, slot, kexit_info) - EQ %jumpi(sstore_dirty_reset2) - %jump(sstore_no_refund) -sstore_dirty_reset2: - %stack (current_value, value, original_value, slot, kexit_info) -> (original_value, current_value, value, original_value, slot, kexit_info) - ISZERO %jumpi(sstore_dirty_reset_sset) - PUSH @GAS_WARMACCESS PUSH @GAS_SRESET SUB %refund_gas - %jump(sstore_no_refund) -sstore_dirty_reset_sset: - PUSH @GAS_WARMACCESS PUSH @GAS_SSET SUB %refund_gas - %jump(sstore_no_refund) - -sstore_refund_original: - %stack (current_value, value, original_value, slot, kexit_info) -> (value, current_value, value, original_value, slot, kexit_info) - ISZERO %jumpi(sstore_sclear) - %jump(sstore_no_refund) -sstore_sclear: - PUSH @REFUND_SCLEAR %refund_gas - %jump(sstore_no_refund) - -sstore_no_refund: - %stack (current_value, value, original_value, slot, kexit_info) -> (kexit_info, current_value, slot, value) -sstore_after_refund: - // stack: kexit_info, current_value, slot, value - // Check if `value` is equal to `current_value`, and if so exit the kernel early. - %stack (kexit_info, current_value, slot, value) -> (value, current_value, current_value, slot, value, kexit_info) - EQ %jumpi(sstore_noop) - - // stack: current_value, slot, value, kexit_info - DUP2 %address %journal_add_storage_change - // stack: slot, value, kexit_info - - // If the value is zero, delete the slot from the storage trie. - // stack: slot, value, kexit_info - DUP2 ISZERO %jumpi(sstore_delete) - - // First we write the value to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: value_ptr, slot, value, kexit_info - SWAP2 - // stack: value, slot, value_ptr, kexit_info - %append_to_trie_data - // stack: slot, value_ptr, kexit_info - - // Next, call mpt_insert on the current account's storage root. - %stack (slot, value_ptr) -> (slot, value_ptr, after_storage_insert) - %slot_to_storage_key - // stack: storage_key, value_ptr, after_storage_insert, kexit_info - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_storage_insert, kexit_info - %jump(mpt_insert) - -after_storage_insert: - // stack: new_storage_root_ptr, kexit_info - %current_account_data - // stack: account_ptr, new_storage_root_ptr, kexit_info - - // Update the copied account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, kexit_info - %mstore_trie_data - // stack: kexit_info - EXIT_KERNEL - -sstore_noop: - // stack: current_value, slot, value, kexit_info - %pop3 - EXIT_KERNEL - -// Delete the slot from the storage trie. -sstore_delete: - // stack: slot, value, kexit_info - SWAP1 POP - PUSH after_storage_insert SWAP1 - // stack: slot, after_storage_insert, kexit_info - %slot_to_storage_key - // stack: storage_key, after_storage_insert, kexit_info - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, after_storage_insert, kexit_info - %jump(mpt_delete) diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm deleted file mode 100644 index 9829494c2f..0000000000 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ /dev/null @@ -1,232 +0,0 @@ -%macro mload_trie_data - // stack: virtual - %mload_kernel(@SEGMENT_TRIE_DATA) - // stack: value -%endmacro - -%macro mstore_trie_data - // stack: virtual, value - %mstore_kernel(@SEGMENT_TRIE_DATA) - // stack: (empty) -%endmacro - -%macro initialize_rlp_segment - PUSH @ENCODED_EMPTY_NODE_POS - PUSH 0x80 - MSTORE_GENERAL -%endmacro - -%macro alloc_rlp_block - // stack: (empty) - %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - // stack: block_start - // In our model it's fine to use memory in a sparse way, as long as the gaps aren't larger than - // 2^16 or so. So instead of the caller specifying the size of the block they need, we'll just - // allocate 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. - DUP1 %add_const(@MAX_RLP_BLOB_SIZE) - // stack: block_end, block_start - %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - // stack: block_start - // We leave an extra 9 bytes, so that callers can later prepend a prefix before block_start. - // (9 is the length of the longest possible RLP list prefix.) - %add_const(9) - // stack: block_start -%endmacro - -%macro get_trie_data_size - // stack: (empty) - %mload_global_metadata(@GLOBAL_METADATA_TRIE_DATA_SIZE) - // stack: trie_data_size -%endmacro - -%macro set_trie_data_size - // stack: trie_data_size - %mstore_global_metadata(@GLOBAL_METADATA_TRIE_DATA_SIZE) - // stack: (empty) -%endmacro - -// Equivalent to: trie_data[trie_data_size++] = value -%macro append_to_trie_data - // stack: value - %get_trie_data_size - // stack: trie_data_size, value - DUP1 - %increment - // stack: trie_data_size', trie_data_size, value - %set_trie_data_size - // stack: trie_data_size, value - %mstore_trie_data - // stack: (empty) -%endmacro - -// Split off the first nibble from a key part. Roughly equivalent to -// def split_first_nibble(num_nibbles, key): -// num_nibbles -= 1 -// num_nibbles_x4 = num_nibbles * 4 -// first_nibble = (key >> num_nibbles_x4) & 0xF -// key -= (first_nibble << num_nibbles_x4) -// return (first_nibble, num_nibbles, key) -%macro split_first_nibble - // stack: num_nibbles, key - %decrement // num_nibbles -= 1 - // stack: num_nibbles, key - DUP2 - // stack: key, num_nibbles, key - DUP2 %mul_const(4) - // stack: num_nibbles_x4, key, num_nibbles, key - SHR - // stack: key >> num_nibbles_x4, num_nibbles, key - %and_const(0xF) - // stack: first_nibble, num_nibbles, key - DUP1 - // stack: first_nibble, first_nibble, num_nibbles, key - DUP3 %mul_const(4) - // stack: num_nibbles_x4, first_nibble, first_nibble, num_nibbles, key - SHL - // stack: first_nibble << num_nibbles_x4, first_nibble, num_nibbles, key - DUP1 - // stack: junk, first_nibble << num_nibbles_x4, first_nibble, num_nibbles, key - SWAP4 - // stack: key, first_nibble << num_nibbles_x4, first_nibble, num_nibbles, junk - SUB - // stack: key, first_nibble, num_nibbles, junk - SWAP3 - // stack: junk, first_nibble, num_nibbles, key - POP - // stack: first_nibble, num_nibbles, key -%endmacro - -// Remove the first `k` nibbles from a key part. -// def truncate_nibbles(k, num_nibbles, key): -// num_nibbles -= k -// num_nibbles_x4 = num_nibbles * 4 -// lead_nibbles = key >> num_nibbles_x4 -// key -= (lead_nibbles << num_nibbles_x4) -// return (num_nibbles, key) -%macro truncate_nibbles - // stack: k, num_nibbles, key - SWAP1 SUB - // stack: num_nibbles, key - DUP1 %mul_const(4) - %stack (num_nibbles_x4, num_nibbles, key) -> (num_nibbles_x4, key, num_nibbles_x4, num_nibbles, key) - SHR - %stack (lead_nibbles, num_nibbles_x4, num_nibbles, key) -> (num_nibbles_x4, lead_nibbles, key, num_nibbles) - SHL SWAP1 SUB - // stack: key, num_nibbles - SWAP1 -%endmacro - -// Split off the common prefix among two key parts. -// -// Pre stack: len_1, key_1, len_2, key_2 -// Post stack: len_common, key_common, len_1, key_1, len_2, key_2 -// -// Roughly equivalent to -// def split_common_prefix(len_1, key_1, len_2, key_2): -// bits_1 = len_1 * 4 -// bits_2 = len_2 * 4 -// len_common = 0 -// key_common = 0 -// while True: -// if bits_1 * bits_2 == 0: -// break -// first_nib_1 = (key_1 >> (bits_1 - 4)) & 0xF -// first_nib_2 = (key_2 >> (bits_2 - 4)) & 0xF -// if first_nib_1 != first_nib_2: -// break -// len_common += 1 -// key_common = key_common * 16 + first_nib_1 -// bits_1 -= 4 -// bits_2 -= 4 -// key_1 -= (first_nib_1 << bits_1) -// key_2 -= (first_nib_2 << bits_2) -// len_1 = bits_1 // 4 -// len_2 = bits_2 // 4 -// return (len_common, key_common, len_1, key_1, len_2, key_2) -%macro split_common_prefix - // stack: len_1, key_1, len_2, key_2 - %mul_const(4) - SWAP2 %mul_const(4) SWAP2 - // stack: bits_1, key_1, bits_2, key_2 - PUSH 0 - PUSH 0 - -%%loop: - // stack: len_common, key_common, bits_1, key_1, bits_2, key_2 - - // if bits_1 * bits_2 == 0: break - DUP3 DUP6 MUL ISZERO %jumpi(%%return) - - // first_nib_2 = (key_2 >> (bits_2 - 4)) & 0xF - DUP6 PUSH 4 DUP7 SUB SHR %and_const(0xF) - // first_nib_1 = (key_1 >> (bits_1 - 4)) & 0xF - DUP5 PUSH 4 DUP6 SUB SHR %and_const(0xF) - // stack: first_nib_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - - // if first_nib_1 != first_nib_2: break - DUP2 DUP2 SUB %jumpi(%%return_with_first_nibs) - - // len_common += 1 - SWAP2 %increment SWAP2 - - // key_common = key_common * 16 + first_nib_1 - SWAP3 - %mul_const(16) - DUP4 ADD - SWAP3 - // stack: first_nib_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - - // bits_1 -= 4 - SWAP4 %sub_const(4) SWAP4 - // bits_2 -= 4 - SWAP6 %sub_const(4) SWAP6 - // stack: first_nib_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - - // key_1 -= (first_nib_1 << bits_1) - DUP5 SHL - // stack: first_nib_1 << bits_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - DUP6 SUB - // stack: key_1, first_nib_2, len_common, key_common, bits_1, key_1_old, bits_2, key_2 - SWAP5 POP - // stack: first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - - // key_2 -= (first_nib_2 << bits_2) - DUP6 SHL - // stack: first_nib_2 << bits_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - DUP7 SUB - // stack: key_2, len_common, key_common, bits_1, key_1, bits_2, key_2_old - SWAP6 POP - // stack: len_common, key_common, bits_1, key_1, bits_2, key_2 - - %jump(%%loop) -%%return_with_first_nibs: - // stack: first_nib_1, first_nib_2, len_common, key_common, bits_1, key_1, bits_2, key_2 - %pop2 -%%return: - // stack: len_common, key_common, bits_1, key_1, bits_2, key_2 - SWAP2 %shr_const(2) SWAP2 // bits_1 -> len_1 (in nibbles) - SWAP4 %shr_const(2) SWAP4 // bits_2 -> len_2 (in nibbles) - // stack: len_common, key_common, len_1, key_1, len_2, key_2 -%endmacro - -// Remove the first `k` nibbles from a key part. -// def merge_nibbles(front_len, front_key, back_len, back_key): -// return (front_len + back_len, (front_key<<(back_len*4)) + back_key) -%macro merge_nibbles - // stack: front_len, front_key, back_len, back_key - %stack (front_len, front_key, back_len, back_key) -> (back_len, front_key, back_key, back_len, front_len) - %mul_const(4) SHL ADD - // stack: new_key, back_len, front_len - SWAP2 ADD -%endmacro - -// Computes state_key = Keccak256(addr). Clobbers @SEGMENT_KERNEL_GENERAL. -%macro addr_to_state_key - %keccak256_word(20) -%endmacro - -// Given a storage slot (a 256-bit integer), computes storage_key = Keccak256(slot). -// Clobbers @SEGMENT_KERNEL_GENERAL. -%macro slot_to_storage_key - %keccak256_word(32) -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/decode.asm b/evm/src/cpu/kernel/asm/rlp/decode.asm deleted file mode 100644 index 43c6627d6c..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/decode.asm +++ /dev/null @@ -1,147 +0,0 @@ -// Note: currently, these methods do not check that RLP input is in canonical -// form; for example a single byte could be encoded with the length-of-length -// form. Technically an EVM must perform these checks, but we aren't really -// concerned with it in our setting. An attacker who corrupted consensus could -// prove a non-canonical state, but this would just temporarily stall the bridge -// until a fix was deployed. We are more concerned with preventing any theft of -// assets. - -// Parse the length of a bytestring from RLP memory. The next len bytes after -// rlp_addr' will contain the string. -// -// Pre stack: rlp_addr, retdest -// Post stack: rlp_addr', len -global decode_rlp_string_len: - // stack: rlp_addr, retdest - DUP1 - MLOAD_GENERAL - // stack: first_byte, rlp_addr, retdest - DUP1 - %gt_const(0xb7) - // stack: first_byte >= 0xb8, first_byte, rlp_addr, retdest - %jumpi(decode_rlp_string_len_large) - // stack: first_byte, rlp_addr, retdest - DUP1 - %gt_const(0x7f) - // stack: first_byte >= 0x80, first_byte, rlp_addr, retdest - %jumpi(decode_rlp_string_len_medium) - - // String is a single byte in the range [0x00, 0x7f]. - %stack (first_byte, rlp_addr, retdest) -> (retdest, rlp_addr, 1) - JUMP - -decode_rlp_string_len_medium: - // String is 0-55 bytes long. First byte contains the len. - // stack: first_byte, rlp_addr, retdest - %sub_const(0x80) - // stack: len, rlp_addr, retdest - SWAP1 - %increment - // stack: rlp_addr', len, retdest - %stack (rlp_addr, len, retdest) -> (retdest, rlp_addr, len) - JUMP - -decode_rlp_string_len_large: - // String is >55 bytes long. First byte contains the len of the len. - // stack: first_byte, rlp_addr, retdest - %sub_const(0xb7) - // stack: len_of_len, rlp_addr, retdest - SWAP1 - %increment - // stack: rlp_addr', len_of_len, retdest - %jump(decode_int_given_len) - -// Convenience macro to call decode_rlp_string_len and return where we left off. -%macro decode_rlp_string_len - %stack (rlp_addr) -> (rlp_addr, %%after) - %jump(decode_rlp_string_len) -%%after: -%endmacro - -// Parse a scalar from RLP memory. -// Pre stack: rlp_addr, retdest -// Post stack: rlp_addr', scalar -// -// Scalars are variable-length, but this method assumes a max length of 32 -// bytes, so that the result can be returned as a single word on the stack. -// As per the spec, scalars must not have leading zeros. -global decode_rlp_scalar: - // stack: rlp_addr, retdest - PUSH decode_int_given_len - // stack: decode_int_given_len, rlp_addr, retdest - SWAP1 - // stack: rlp_addr, decode_int_given_len, retdest - // decode_rlp_string_len will return to decode_int_given_len, at which point - // the stack will contain (rlp_addr', len, retdest), which are the proper args - // to decode_int_given_len. - %jump(decode_rlp_string_len) - -// Convenience macro to call decode_rlp_scalar and return where we left off. -%macro decode_rlp_scalar - %stack (rlp_addr) -> (rlp_addr, %%after) - %jump(decode_rlp_scalar) -%%after: -%endmacro - -// Parse the length of an RLP list from memory. -// Pre stack: rlp_addr, retdest -// Post stack: rlp_addr', len -global decode_rlp_list_len: - // stack: rlp_addr, retdest - DUP1 - MLOAD_GENERAL - // stack: first_byte, rlp_addr, retdest - SWAP1 - %increment // increment rlp_addr - SWAP1 - // stack: first_byte, rlp_addr', retdest - // If first_byte is >= 0xf8, it's a > 55 byte list, and - // first_byte - 0xf7 is the length of the length. - DUP1 - %gt_const(0xf7) // GT is native while GE is not, so compare to 0xf6 instead - // stack: first_byte >= 0xf7, first_byte, rlp_addr', retdest - %jumpi(decode_rlp_list_len_big) - - // This is the "small list" case. - // The list length is first_byte - 0xc0. - // stack: first_byte, rlp_addr', retdest - %sub_const(0xc0) - // stack: len, rlp_addr', retdest - %stack (len, rlp_addr, retdest) -> (retdest, rlp_addr, len) - JUMP - -decode_rlp_list_len_big: - // The length of the length is first_byte - 0xf7. - // stack: first_byte, rlp_addr', retdest - %sub_const(0xf7) - // stack: len_of_len, rlp_addr', retdest - SWAP1 - // stack: rlp_addr', len_of_len, retdest - %jump(decode_int_given_len) - -// Convenience macro to call decode_rlp_list_len and return where we left off. -%macro decode_rlp_list_len - %stack (rlp_addr) -> (rlp_addr, %%after) - %jump(decode_rlp_list_len) -%%after: -%endmacro - -// Parse an integer of the given length. It is assumed that the integer will -// fit in a single (256-bit) word on the stack. -// Pre stack: rlp_addr, len, retdest -// Post stack: rlp_addr', int -global decode_int_given_len: - DUP2 ISZERO %jumpi(empty_int) - %stack (rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr, len, retdest) - ADD - %stack(rlp_addr_two, rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr_two, retdest) - MLOAD_32BYTES - // stack: int, rlp_addr', retdest - %stack(int, rlp_addr, retdest) -> (retdest, rlp_addr, int) - JUMP - -empty_int: - // stack: rlp_addr, len, retdest - %stack(rlp_addr, len, retdest) -> (retdest, rlp_addr, 0) - JUMP - diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm deleted file mode 100644 index 9f6813ab18..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ /dev/null @@ -1,265 +0,0 @@ -// Convenience macro to RLP-encode a fixed-length 160 bit (20 byte) string -// and return where we left off. Assumes string < 2^160. -// Pre stack: rlp_addr, string, retdest -// Post stack: rlp_addr -%macro encode_rlp_160 - %stack (rlp_addr, string) -> (20, rlp_addr, string, %%after) - %jump(encode_rlp_fixed) -%%after: -%endmacro - -// Convenience macro to RLP-encode a fixed-length 256 bit (32 byte) string -// and return where we left off. -// Pre stack: rlp_addr, string, retdest -// Post stack: rlp_addr -%macro encode_rlp_256 - %stack (rlp_addr, string) -> (32, rlp_addr, string, %%after) - %jump(encode_rlp_fixed) -%%after: -%endmacro - -// RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). -global encode_rlp_fixed: - // stack: len, rlp_addr, string, retdest - DUP2 - DUP2 - %add_const(0x80) - // stack: first_byte, rlp_addr, len, rlp_addr, string, retdest - MSTORE_GENERAL - // stack: len, rlp_addr, string, retdest - SWAP1 - %increment // increment rlp_addr - // stack: rlp_addr, len, string, retdest - %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) - // stack: rlp_addr, string, len, encode_rlp_fixed_finish, retdest - %jump(mstore_unpacking) -encode_rlp_fixed_finish: - // stack: rlp_addr', retdest - SWAP1 - JUMP - -// Doubly-RLP-encode a fixed-length string with the given byte length. -// I.e. writes encode(encode(string). Assumes string < 2^(8 * len). -global doubly_encode_rlp_fixed: - // stack: len, rlp_addr, string, retdest - DUP2 - DUP2 - %add_const(0x81) - // stack: first_byte, rlp_addr, len, rlp_addr, string, retdest - MSTORE_GENERAL - // stack: len, rlp_addr, string, retdest - DUP2 %increment - DUP2 - %add_const(0x80) - // stack: second_byte, rlp_addr', len, original_rlp_addr, string, retdest - MSTORE_GENERAL - // stack: len, rlp_addr, string, retdest - SWAP1 - %add_const(2) // advance past the two prefix bytes - // stack: rlp_addr'', len, string, retdest - %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) - // stack: context, segment, rlp_addr'', string, len, encode_rlp_fixed_finish, retdest - %jump(mstore_unpacking) - -// Writes the RLP prefix for a string of the given length. This does not handle -// the trivial encoding of certain single-byte strings, as handling that would -// require access to the actual string, while this method only accesses its -// length. This method should generally be used only when we know a string -// contains at least two bytes. -// -// Pre stack: rlp_addr, str_len, retdest -// Post stack: rlp_addr' -global encode_rlp_multi_byte_string_prefix: - // stack: rlp_addr, str_len, retdest - DUP2 %gt_const(55) - // stack: str_len > 55, rlp_addr, str_len, retdest - %jumpi(encode_rlp_multi_byte_string_prefix_large) - // Medium case; prefix is 0x80 + str_len. - // stack: rlp_addr, str_len, retdest - PUSH 0x80 - DUP2 - // stack: rlp_addr, 0x80, rlp_addr, str_len, retdest - SWAP3 ADD - // stack: prefix, rlp_addr, rlp_addr, retdest - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - // stack: rlp_addr', retdest - SWAP1 - JUMP -encode_rlp_multi_byte_string_prefix_large: - // Large case; prefix is 0xb7 + len_of_len, followed by str_len. - // stack: rlp_addr, str_len, retdest - DUP2 - %num_bytes - // stack: len_of_len, rlp_addr, str_len, retdest - SWAP1 - DUP1 // rlp_addr - DUP3 // len_of_len - %add_const(0xb7) - // stack: first_byte, rlp_addr, rlp_addr, len_of_len, str_len, retdest - MSTORE_GENERAL - // stack: rlp_addr, len_of_len, str_len, retdest - %increment - // stack: rlp_addr', len_of_len, str_len, retdest - %stack (rlp_addr, len_of_len, str_len) -> (rlp_addr, str_len, len_of_len) - %jump(mstore_unpacking) - -%macro encode_rlp_multi_byte_string_prefix - %stack (rlp_addr, str_len) -> (rlp_addr, str_len, %%after) - %jump(encode_rlp_multi_byte_string_prefix) -%%after: -%endmacro - -// Writes the RLP prefix for a list with the given payload length. -// -// Pre stack: rlp_addr, payload_len, retdest -// Post stack: rlp_addr' -global encode_rlp_list_prefix: - // stack: rlp_addr, payload_len, retdest - DUP2 %gt_const(55) - %jumpi(encode_rlp_list_prefix_large) - // Small case: prefix is just 0xc0 + length. - // stack: rlp_addr, payload_len, retdest - DUP1 - SWAP2 - %add_const(0xc0) - // stack: prefix, rlp_addr, rlp_addr, retdest - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - SWAP1 - JUMP -encode_rlp_list_prefix_large: - // Write 0xf7 + len_of_len. - // stack: rlp_addr, payload_len, retdest - DUP2 %num_bytes - // stack: len_of_len, rlp_addr, payload_len, retdest - DUP2 - DUP2 %add_const(0xf7) - // stack: first_byte, rlp_addr, len_of_len, rlp_addr, payload_len, retdest - MSTORE_GENERAL - // stack: len_of_len, rlp_addr, payload_len, retdest - SWAP1 %increment - // stack: rlp_addr', len_of_len, payload_len, retdest - %stack (rlp_addr, len_of_len, payload_len) - -> (rlp_addr, payload_len, len_of_len, - encode_rlp_list_prefix_large_done_writing_len) - %jump(mstore_unpacking) -encode_rlp_list_prefix_large_done_writing_len: - // stack: rlp_addr'', retdest - SWAP1 - JUMP - -%macro encode_rlp_list_prefix - %stack (rlp_addr, payload_len) -> (rlp_addr, payload_len, %%after) - %jump(encode_rlp_list_prefix) -%%after: -%endmacro - -// Given an RLP list payload which starts and ends at the given rlp_address, -// prepend the appropriate RLP list prefix. Returns the updated start rlp_address, -// as well as the length of the RLP data (including the newly-added prefix). -// -// Pre stack: end_rlp_addr, start_rlp_addr, retdest -// Post stack: prefix_start_rlp_addr, rlp_len -global prepend_rlp_list_prefix: - // stack: end_rlp_addr, start_rlp_addr, retdest - DUP2 DUP2 SUB // end_rlp_addr - start_rlp_addr - // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 %gt_const(55) - %jumpi(prepend_rlp_list_prefix_big) - - // If we got here, we have a small list, so we prepend 0xc0 + len at rlp_address 8. - // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - PUSH 1 DUP4 SUB // offset of prefix - DUP2 %add_const(0xc0) - // stack: prefix_byte, start_rlp_addr-1, payload_len, end_rlp_addr, start_rlp_addr, retdest - MSTORE_GENERAL - // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - %increment - // stack: rlp_len, end_rlp_addr, start_rlp_addr, retdest - SWAP2 %decrement - // stack: prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest - %stack (prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) - JUMP - -prepend_rlp_list_prefix_big: - // We have a large list, so we prepend 0xf7 + len_of_len at rlp_address - // prefix_start_rlp_addr = start_rlp_addr - 1 - len_of_len - // followed by the length itself. - // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 %num_bytes - // stack: len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 - PUSH 1 DUP6 SUB // start_rlp_addr - 1 - SUB - // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 - DUP3 %add_const(0xf7) MSTORE_GENERAL // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len - // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest - DUP1 %increment // start_len_rlp_addr = prefix_start_rlp_addr + 1 - %stack (start_len_rlp_addr, prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest) - -> (start_len_rlp_addr, payload_len, len_of_len, - prepend_rlp_list_prefix_big_done_writing_len, - prefix_start_rlp_addr, end_rlp_addr, retdest) - %jump(mstore_unpacking) -prepend_rlp_list_prefix_big_done_writing_len: - // stack: start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr, retdest - %stack (start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr) - -> (end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr) - // stack: end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr, retdest - SUB - // stack: rlp_len, prefix_start_rlp_addr, retdest - %stack (rlp_len, prefix_start_rlp_addr, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) - JUMP - -// Convenience macro to call prepend_rlp_list_prefix and return where we left off. -%macro prepend_rlp_list_prefix - %stack (end_rlp_addr, start_rlp_addr) -> (end_rlp_addr, start_rlp_addr, %%after) - %jump(prepend_rlp_list_prefix) -%%after: -%endmacro - -// Given some scalar, compute the number of bytes used in its RLP encoding, -// including any length prefix. -%macro rlp_scalar_len - // stack: scalar - // Since the scalar fits in a word, we can't hit the large (>55 byte) - // case, so we just check for small vs medium. - DUP1 %gt_const(0x7f) - // stack: is_medium, scalar - %jumpi(%%medium) - // Small case; result is 1. - %stack (scalar) -> (1) - %jump(%%finish) -%%medium: - // stack: scalar - %num_bytes - // stack: scalar_bytes - %increment // Account for the length prefix. - // stack: rlp_len -%%finish: -%endmacro - -// Given some list with the given payload length, compute the number of bytes -// used in its RLP encoding, including the list prefix. -%macro rlp_list_len - // stack: payload_len - DUP1 %gt_const(55) - // stack: is_large, payload_len - %jumpi(%%large) - // Small case; prefix is a single byte. - %increment - // stack: 1 + payload_len - %jump(%%finish) -%%large: - // Prefix is 1 byte containing len_of_len, followed by len_of_len bytes containing len. - // stack: payload_len - DUP1 %num_bytes - // stack: len_of_len, payload_len - %increment - // stack: prefix_len, payload_len - ADD -%%finish: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm deleted file mode 100644 index d311a57ebc..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm +++ /dev/null @@ -1,108 +0,0 @@ -// RLP-encode a scalar, i.e. a variable-length integer. -// Pre stack: rlp_addr, scalar, retdest -// Post stack: rlp_addr -global encode_rlp_scalar: - // stack: rlp_addr, scalar, retdest - // If scalar > 0x7f, this is the "medium" case. - DUP2 - %gt_const(0x7f) - %jumpi(encode_rlp_scalar_medium) - - // Else, if scalar != 0, this is the "small" case, where the value is its own encoding. - DUP2 %jumpi(encode_rlp_scalar_small) - - // scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80. - // stack: rlp_addr, scalar, retdest - %stack (rlp_addr, scalar) -> (0x80, rlp_addr, rlp_addr) - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - // stack: rlp_addr', retdest - SWAP1 - JUMP - -encode_rlp_scalar_medium: - // This is the "medium" case, where we write 0x80 + len followed by the - // (big-endian) scalar bytes. We first compute the minimal number of bytes - // needed to represent this scalar, then treat it as if it was a fixed- - // length string with that length. - // stack: rlp_addr, scalar, retdest - DUP2 - %num_bytes - // stack: scalar_bytes, rlp_addr, scalar, retdest - %jump(encode_rlp_fixed) - -// Doubly-RLP-encode a scalar, i.e. return encode(encode(scalar)). -// Pre stack: rlp_addr, scalar, retdest -// Post stack: rlp_addr -global doubly_encode_rlp_scalar: - // stack: rlp_addr, scalar, retdest - // If scalar > 0x7f, this is the "medium" case. - DUP2 - %gt_const(0x7f) - %jumpi(doubly_encode_rlp_scalar_medium) - - // Else, if scalar != 0, this is the "small" case, where the value is its own encoding. - DUP2 %jumpi(encode_rlp_scalar_small) - - // scalar = 0, so BE(scalar) is the empty string, encode(scalar) = 0x80, and encode(encode(scalar)) = 0x8180. - // stack: rlp_addr, scalar, retdest - %stack (rlp_addr, scalar) -> (0x81, rlp_addr, rlp_addr) - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - DUP1 PUSH 0x80 - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - // stack: rlp_addr, retdest - SWAP1 - JUMP - -doubly_encode_rlp_scalar_medium: - // This is the "medium" case, where - // encode(scalar) = [0x80 + len] || BE(scalar) - // and so - // encode(encode(scalar)) = [0x80 + len + 1] || [0x80 + len] || BE(scalar) - // We first compute the length of the scalar with %num_bytes, then treat the scalar as if it was a - // fixed-length string with that length. - // stack: rlp_addr, scalar, retdest - DUP2 - %num_bytes - // stack: scalar_bytes, rlp_addr, scalar, retdest - %jump(doubly_encode_rlp_fixed) - -// The "small" case of RLP-encoding a scalar, where the value is its own encoding. -// This can be used for both for singly encoding or doubly encoding, since encode(encode(x)) = encode(x) = x. -encode_rlp_scalar_small: - // stack: rlp_addr, scalar, retdest - %stack (rlp_addr, scalar) -> (scalar, rlp_addr, rlp_addr) - // stack: scalar, rlp_addr, rlp_addr, retdest - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - // stack: rlp_addr', retdest - SWAP1 - JUMP - -// Convenience macro to call encode_rlp_scalar and return where we left off. -// It takes swapped inputs, i.e. `scalar, rlp_addr` instead of `rlp_addr, scalar`. -%macro encode_rlp_scalar_swapped_inputs - %stack (scalar, rlp_addr) -> (rlp_addr, scalar, %%after) - %jump(encode_rlp_scalar) -%%after: -%endmacro - -// Convenience macro to call encode_rlp_scalar and return where we left off. -%macro encode_rlp_scalar - %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) - %jump(encode_rlp_scalar) -%%after: -%endmacro - -// Convenience macro to call doubly_encode_rlp_scalar and return where we left off. -%macro doubly_encode_rlp_scalar - %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) - %jump(doubly_encode_rlp_scalar) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm deleted file mode 100644 index 60174a9436..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm +++ /dev/null @@ -1,79 +0,0 @@ -// Encodes an arbitrary string, given a pointer and length. -// Pre stack: rlp_addr, ADDR, len, retdest -// Post stack: rlp_addr' -global encode_rlp_string: - // stack: rlp_addr, ADDR, len, retdest - DUP3 %eq_const(1) - // stack: len == 1, rlp_addr, ADDR, len, retdest - DUP3 - MLOAD_GENERAL - // stack: first_byte, len == 1, rlp_addr, ADDR, len, retdest - %lt_const(128) - MUL // cheaper than AND - // stack: single_small_byte, rlp_addr, ADDR, len, retdest - %jumpi(encode_rlp_string_small_single_byte) - - // stack: rlp_addr, ADDR, len, retdest - DUP3 %gt_const(55) - // stack: len > 55, rlp_addr, ADDR, len, retdest - %jumpi(encode_rlp_string_large) - -global encode_rlp_string_small: - // stack: rlp_addr, ADDR, len, retdest - DUP1 - DUP4 // len - %add_const(0x80) - // stack: first_byte, rlp_addr, rlp_addr, ADDR, len, retdest - MSTORE_GENERAL - // stack: rlp_addr, ADDR, len, retdest - %increment - // stack: rlp_addr', ADDR, len, retdest - DUP3 DUP2 ADD // rlp_addr'' = rlp_addr' + len - // stack: rlp_addr'', rlp_addr', ADDR, len, retdest - %stack (rlp_addr2, rlp_addr1, ADDR, len, retdest) - -> (rlp_addr1, ADDR, len, retdest, rlp_addr2) - %jump(memcpy_bytes) - -global encode_rlp_string_small_single_byte: - // stack: rlp_addr, ADDR, len, retdest - %stack (rlp_addr, ADDR, len) -> (ADDR, rlp_addr) - MLOAD_GENERAL - // stack: byte, rlp_addr, retdest - DUP2 SWAP1 - MSTORE_GENERAL - // stack: rlp_addr, retdest - %increment - SWAP1 - // stack: retdest, rlp_addr' - JUMP - -global encode_rlp_string_large: - // stack: rlp_addr, ADDR, len, retdest - DUP3 %num_bytes - // stack: len_of_len, rlp_addr, ADDR, len, retdest - SWAP1 - DUP1 - // stack: rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest - DUP3 // len_of_len - %add_const(0xb7) - // stack: first_byte, rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest - MSTORE_GENERAL - // stack: rlp_addr, len_of_len, ADDR, len, retdest - %increment - // stack: rlp_addr', len_of_len, ADDR, len, retdest - %stack (rlp_addr, len_of_len, ADDR, len) - -> (rlp_addr, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len) - %jump(mstore_unpacking) -global encode_rlp_string_large_after_writing_len: - // stack: rlp_addr'', ADDR, len, retdest - DUP3 DUP2 ADD // rlp_addr''' = rlp_addr'' + len - // stack: rlp_addr''', rlp_addr'', ADDR, len, retdest - %stack (rlp_addr3, rlp_addr2, ADDR, len, retdest) - -> (rlp_addr2, ADDR, len, retdest, rlp_addr3) - %jump(memcpy_bytes) - -%macro encode_rlp_string - %stack (rlp_addr, ADDR, len) -> (rlp_addr, ADDR, len, %%after) - %jump(encode_rlp_string) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm b/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm deleted file mode 100644 index 6958cff9f8..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/increment_bounded_rlp.asm +++ /dev/null @@ -1,38 +0,0 @@ -// Increment by 1 the rlp encoded index and increment -// its number of nibbles when required. Shouldn't be -// called with rlp_index > 0x82 ff ff -global increment_bounded_rlp: - // stack: num_nibbles, rlp_index, retdest - DUP2 - %eq_const(0x80) - %jumpi(case_0x80) - DUP1 - %eq_const(0x7f) - %jumpi(case_0x7f) - DUP1 - %eq_const(0x81ff) - %jumpi(case_0x81ff) - // If rlp_index != 0x80 and rlp_index != 0x7f and rlp_index != 0x81ff - // we only need to add one and keep the number of nibbles - DUP2 %increment DUP2 - %stack (next_num_nibbles, next_rlp_index, num_nibbles, rlp_index, retdest) -> (retdest, rlp_index, num_nibbles, next_rlp_index, next_num_nibbles) - JUMP - -case_0x80: - %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x80, 2, 0x01, 2) - JUMP -case_0x7f: - %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x7f, 2, 0x8180, 4) - JUMP - -case_0x81ff: - %stack (num_nibbles, rlp_index, retdest) -> (retdest, 0x81ff, 4, 0x820100, 6) - JUMP - - - -%macro increment_bounded_rlp - %stack (rlp_index, num_nibbles) -> (rlp_index, num_nibbles, %%after) - %jump(increment_bounded_rlp) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/num_bytes.asm b/evm/src/cpu/kernel/asm/rlp/num_bytes.asm deleted file mode 100644 index de0a7ca966..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/num_bytes.asm +++ /dev/null @@ -1,30 +0,0 @@ -// Get the number of bytes required to represent the given scalar. -// Note that we define num_bytes(0) to be 1. -global num_bytes: - // stack: x, retdest - DUP1 ISZERO %jumpi(return_1) - // Non-deterministically guess the number of bits - PROVER_INPUT(num_bits) - %stack(num_bits, x) -> (num_bits, 1, x, num_bits) - SUB - SHR - // stack: 1, num_bits - %assert_eq_const(1) - // convert number of bits to number of bytes - %add_const(7) - %shr_const(3) - - SWAP1 - JUMP - -return_1: - // stack: x, retdest - %stack(x, retdest) -> (retdest, 1) - JUMP - -// Convenience macro to call num_bytes and return where we left off. -%macro num_bytes - %stack (x) -> (x, %%after) - %jump(num_bytes) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm deleted file mode 100644 index 8070fd0beb..0000000000 --- a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm +++ /dev/null @@ -1,38 +0,0 @@ -// Read RLP data from the prover's tape, and save it to the SEGMENT_RLP_RAW -// segment of memory. - -// Pre stack: retdest -// Post stack: txn_rlp_len - -global read_rlp_to_memory: - // stack: retdest - PROVER_INPUT(rlp) // Read the RLP blob length from the prover tape. - // stack: len, retdest - PUSH @SEGMENT_RLP_RAW - %build_kernel_address - - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - // stack: addr, final_addr, retdest -read_rlp_to_memory_loop: - // stack: addr, final_addr, retdest - DUP2 - DUP2 - LT - ISZERO - // stack: addr >= final_addr, addr, final_addr, retdest - %jumpi(read_rlp_to_memory_finish) - // stack: addr, final_addr, retdest - PROVER_INPUT(rlp) - SWAP1 - MSTORE_32BYTES_32 - // stack: addr', final_addr, retdest - %jump(read_rlp_to_memory_loop) - -read_rlp_to_memory_finish: - // stack: addr, final_addr, retdest - // we recover the offset here - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - DUP3 SUB - // stack: pos, addr, final_addr, retdest - %stack(pos, addr, final_addr, retdest) -> (retdest, pos) - JUMP \ No newline at end of file diff --git a/evm/src/cpu/kernel/asm/shift.asm b/evm/src/cpu/kernel/asm/shift.asm deleted file mode 100644 index ee9ccbfaea..0000000000 --- a/evm/src/cpu/kernel/asm/shift.asm +++ /dev/null @@ -1,20 +0,0 @@ -/// Initialise the lookup table of binary powers for doing left/right shifts -/// -/// Specifically, set SHIFT_TABLE_SEGMENT[i] = 2^i for i = 0..255. -%macro shift_table_init - push @SEGMENT_SHIFT_TABLE // segment, ctx == virt == 0 - push 1 // 2^0 - %rep 255 - // stack: 2^i, addr_i - dup2 - %increment - // stack: addr_(i+1), 2^i, addr_i - dup2 - dup1 - add - // stack: 2^(i+1), addr_(i+1), 2^i, addr_i - %endrep - %rep 256 - mstore_general - %endrep -%endmacro diff --git a/evm/src/cpu/kernel/asm/signed.asm b/evm/src/cpu/kernel/asm/signed.asm deleted file mode 100644 index 566d7d5aeb..0000000000 --- a/evm/src/cpu/kernel/asm/signed.asm +++ /dev/null @@ -1,216 +0,0 @@ -// SDIV(a, b): signed division operation. -// -// If b = 0, then SDIV(a, b) = 0, -// else if a = -2^255 and b = -1, then SDIV(a, b) = -2^255 -// else SDIV(a, b) = sgn(a/b) * floor(|a/b|). -global _sys_sdiv: - // stack: num, denom, return_info - DUP1 - PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 - GT - // stack: num_is_nonneg := sign_bit > num, num, denom, return_info - DUP1 - %jumpi(sys_sdiv_nonneg_num) - // stack: num_is_nonneg, num, denom, return_info - SWAP1 - PUSH 0 - SUB - SWAP1 - // stack: num_is_nonneg, num := -num, denom, return_info -sys_sdiv_nonneg_num: - SWAP2 - DUP1 - PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 - GT - // stack: denom_is_nonneg := sign_bit > denom, denom, num, num_is_nonneg, return_info - DUP1 - %jumpi(sys_sdiv_nonneg_denom) - // stack: denom_is_nonneg, denom, num, num_is_nonneg, return_info - SWAP1 - PUSH 0 - SUB - // stack: denom := -denom, denom_is_nonneg, num, num_is_nonneg, return_info - SWAP1 -sys_sdiv_nonneg_denom: - // stack: denom_is_nonneg, denom, num, num_is_nonneg, return_info - SWAP2 - DIV - // stack: num / denom, denom_is_nonneg, num_is_nonneg, return_info - SWAP2 - EQ - // stack: denom_is_nonneg == num_is_nonneg, num / denom, return_info - %jumpi(sys_sdiv_same_sign) - PUSH 0 - SUB -sys_sdiv_same_sign: - SWAP1 - JUMP - - -// SMOD(a, b): signed "modulo remainder" operation. -// -// If b != 0, then SMOD(a, b) = sgn(a) * MOD(|a|, |b|), -// else SMOD(a, 0) = 0. -global _sys_smod: - // stack: x, mod, return_info - PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 - // stack: sign_bit, x, mod, return_info - DUP1 - DUP4 - LT - // stack: mod < sign_bit, sign_bit, x, mod, return_info - %jumpi(sys_smod_pos_mod) - // mod is negative, so we negate it - // sign_bit, x, mod, return_info - SWAP2 - PUSH 0 - SUB - SWAP2 - // sign_bit, x, mod := 0 - mod, return_info -sys_smod_pos_mod: - // At this point, we know that mod is non-negative. - DUP2 - LT - // stack: x < sign_bit, x, mod, return_info - %jumpi(sys_smod_pos_x) - // x is negative, so let's negate it - // stack: x, mod, return_info - PUSH 0 - SUB - // stack: x := 0 - x, mod, return_info - MOD - // negate the result - PUSH 0 - SUB - SWAP1 - JUMP -sys_smod_pos_x: - // Both x and mod are non-negative - // stack: x, mod, return_info - MOD - SWAP1 - JUMP - - -// SIGNEXTEND from the Nth byte of value, where the bytes of value are -// considered in LITTLE-endian order. Just a SHL followed by a SAR. -global _sys_signextend: - // Stack: N, value, return_info - // Handle N >= 31, which is a no-op. - PUSH 31 - %min - // Stack: min(31, N), value, return_info - %increment - %mul_const(8) - // Stack: 8*(N + 1), value, return_info - PUSH 256 - SUB - // Stack: 256 - 8*(N + 1), value, return_info - %stack(bits, value, return_info) -> (bits, value, bits, return_info) - SHL - SWAP1 - // Stack: bits, value << bits, return_info - // fall through to sys_sar - - -// SAR, i.e. shift arithmetic right, shifts `value` `shift` bits to -// the right, preserving sign by filling with the most significant bit. -// -// Trick: x >>s i = (x + sign_bit >>u i) - (sign_bit >>u i), -// where >>s is arithmetic shift and >>u is logical shift. -// Reference: Hacker's Delight, 2013, 2nd edition, §2-7. -global _sys_sar: - // SAR(shift, value) is the same for all shift >= 255, so we - // replace shift with min(shift, 255) - - // Stack: shift, value, return_info - PUSH 255 - %min - // Stack: min(shift, 255), value, return_info - - // Now assume shift < 256. - // Stack: shift, value, return_info - PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 - DUP2 - SHR - // Stack: 2^255 >> shift, shift, value, return_info - SWAP2 - %add_const(0x8000000000000000000000000000000000000000000000000000000000000000) - // Stack: 2^255 + value, shift, 2^255 >> shift, return_info - SWAP1 - SHR - SUB - // Stack: ((2^255 + value) >> shift) - (2^255 >> shift), return_info - SWAP1 - JUMP - - -// SGT, i.e. signed greater than, returns 1 if lhs > rhs as signed -// integers, 0 otherwise. -// -// Just swap argument order and fall through to signed less than. -global _sys_sgt: - SWAP1 - - -// SLT, i.e. signed less than, returns 1 if lhs < rhs as signed -// integers, 0 otherwise. -// -// Trick: x (_sys_sdiv, x, y, _syscall_return, kernel_return) - JUMP - -global sys_smod: - %charge_gas_const(@GAS_LOW) - %stack(kernel_return, x, y) -> (_sys_smod, x, y, _syscall_return, kernel_return) - JUMP - -global sys_signextend: - %charge_gas_const(@GAS_LOW) - %stack(kernel_return, x, y) -> (_sys_signextend, x, y, _syscall_return, kernel_return) - JUMP - -global sys_sar: - %charge_gas_const(@GAS_VERYLOW) - %stack(kernel_return, x, y) -> (_sys_sar, x, y, _syscall_return, kernel_return) - JUMP - -global sys_slt: - %charge_gas_const(@GAS_VERYLOW) - %stack(kernel_return, x, y) -> (_sys_slt, x, y, _syscall_return, kernel_return) - JUMP - -global sys_sgt: - %charge_gas_const(@GAS_VERYLOW) - %stack(kernel_return, x, y) -> (_sys_sgt, x, y, _syscall_return, kernel_return) - JUMP - -_syscall_return: - SWAP1 - EXIT_KERNEL diff --git a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm deleted file mode 100644 index 4a8feccaa3..0000000000 --- a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm +++ /dev/null @@ -1,252 +0,0 @@ -// Store chain ID = 1. Used for non-legacy txns which always have a chain ID. -%macro store_chain_id_present_true - PUSH 1 - %mstore_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) -%endmacro - -// Decode the chain ID and store it. -%macro decode_and_store_chain_id - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, chain_id) -> (chain_id, rlp_addr) - %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: rlp_addr -%endmacro - -// Decode the nonce and store it. -%macro decode_and_store_nonce - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, nonce) -> (nonce, rlp_addr) - %mstore_txn_field(@TXN_FIELD_NONCE) - // stack: rlp_addr -%endmacro - -// Decode the gas price and, since this is for legacy txns, store it as both -// TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS and TXN_FIELD_MAX_FEE_PER_GAS. -%macro decode_and_store_gas_price_legacy - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, gas_price) -> (gas_price, gas_price, rlp_addr) - %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: rlp_addr -%endmacro - -// Decode the max priority fee and store it. -%macro decode_and_store_max_priority_fee - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) - %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - // stack: rlp_addr -%endmacro - -// Decode the max fee and store it. -%macro decode_and_store_max_fee - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) - %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: rlp_addr -%endmacro - -// Decode the gas limit and store it. -%macro decode_and_store_gas_limit - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, gas_limit) -> (gas_limit, rlp_addr) - %mstore_txn_field(@TXN_FIELD_GAS_LIMIT) - // stack: rlp_addr -%endmacro - -// Decode the "to" field and store it. -// This field is either 160-bit or empty in the case of a contract creation txn. -%macro decode_and_store_to - // stack: rlp_addr - %decode_rlp_string_len - // stack: rlp_addr, len - SWAP1 - // stack: len, rlp_addr - DUP1 ISZERO %jumpi(%%contract_creation) - // stack: len, rlp_addr - DUP1 %eq_const(20) ISZERO %jumpi(invalid_txn) // Address is 160-bit - %stack (len, rlp_addr) -> (rlp_addr, len, %%with_scalar) - %jump(decode_int_given_len) -%%with_scalar: - // stack: rlp_addr, int - SWAP1 - %mstore_txn_field(@TXN_FIELD_TO) - // stack: rlp_addr - %jump(%%end) -%%contract_creation: - // stack: len, rlp_addr - POP - PUSH 1 %mstore_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) - // stack: rlp_addr -%%end: -%endmacro - -// Decode the "value" field and store it. -%macro decode_and_store_value - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, value) -> (value, rlp_addr) - %mstore_txn_field(@TXN_FIELD_VALUE) - // stack: rlp_addr -%endmacro - -// Decode the calldata field, store its length in @TXN_FIELD_DATA_LEN, and copy it to @SEGMENT_TXN_DATA. -%macro decode_and_store_data - // stack: rlp_addr - // Decode the data length, store it, and compute new_rlp_addr after any data. - %decode_rlp_string_len - %stack (rlp_addr, data_len) -> (data_len, rlp_addr, data_len, rlp_addr, data_len) - %mstore_txn_field(@TXN_FIELD_DATA_LEN) - // stack: rlp_addr, data_len, rlp_addr, data_len - ADD - // stack: new_rlp_addr, old_rlp_addr, data_len - - // Memcpy the txn data from @SEGMENT_RLP_RAW to @SEGMENT_TXN_DATA. - %stack (new_rlp_addr, old_rlp_addr, data_len) -> (old_rlp_addr, data_len, %%after, new_rlp_addr) - // old_rlp_addr has context 0. We will call GET_CONTEXT and update it. - GET_CONTEXT ADD - PUSH @SEGMENT_TXN_DATA - GET_CONTEXT ADD - // stack: DST, SRC, data_len, %%after, new_rlp_addr - %jump(memcpy_bytes) - -%%after: - // stack: new_rlp_addr -%endmacro - -%macro decode_and_store_access_list - // stack: rlp_addr - DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) - %decode_rlp_list_len - %stack (rlp_addr, len) -> (len, len, rlp_addr, %%after) - %jumpi(decode_and_store_access_list) - // stack: len, rlp_addr, %%after - POP SWAP1 POP - // stack: rlp_addr - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) -%%after: -%endmacro - -%macro decode_and_store_y_parity - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, y_parity) -> (y_parity, rlp_addr) - %mstore_txn_field(@TXN_FIELD_Y_PARITY) - // stack: rlp_addr -%endmacro - -%macro decode_and_store_r - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, r) -> (r, rlp_addr) - %mstore_txn_field(@TXN_FIELD_R) - // stack: rlp_addr -%endmacro - -%macro decode_and_store_s - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, s) -> (s, rlp_addr) - %mstore_txn_field(@TXN_FIELD_S) - // stack: rlp_addr -%endmacro - - -// The access list is of the form `[[{20 bytes}, [{32 bytes}...]]...]`. -global decode_and_store_access_list: - // stack: len, rlp_addr - DUP2 ADD - // stack: end_rlp_addr, rlp_addr - // Store the RLP length. - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - SWAP1 -decode_and_store_access_list_loop: - // stack: rlp_addr, end_rlp_addr - DUP2 DUP2 EQ %jumpi(decode_and_store_access_list_finish) - // stack: rlp_addr, end_rlp_addr - %decode_rlp_list_len // Should be a list `[{20 bytes}, [{32 bytes}...]]` - // stack: rlp_addr, internal_len, end_rlp_addr - SWAP1 POP // We don't need the length of this list. - // stack: rlp_addr, end_rlp_addr - %decode_rlp_scalar // Address // TODO: Should panic when address is not 20 bytes? - // stack: rlp_addr, addr, end_rlp_addr - SWAP1 - // stack: addr, rlp_addr, end_rlp_addr - DUP1 %insert_accessed_addresses_no_return - // stack: addr, rlp_addr, end_rlp_addr - %add_address_cost - // stack: addr, rlp_addr, end_rlp_addr - SWAP1 - // stack: rlp_addr, addr, end_rlp_addr - %decode_rlp_list_len // Should be a list of storage keys `[{32 bytes}...]` - // stack: rlp_addr, sk_len, addr, end_rlp_addr - SWAP1 DUP2 ADD - // stack: sk_end_rlp_addr, rlp_addr, addr, end_rlp_addr - SWAP1 - // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr -sk_loop: - DUP2 DUP2 EQ %jumpi(end_sk) - // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr - %decode_rlp_scalar // Storage key // TODO: Should panic when key is not 32 bytes? - %stack (rlp_addr, key, sk_end_rlp_addr, addr, end_rlp_addr) -> - (addr, key, sk_loop_contd, rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) - %jump(insert_accessed_storage_keys_with_original_value) -sk_loop_contd: - // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr - %add_storage_key_cost - %jump(sk_loop) -end_sk: - %stack (rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) -> (rlp_addr, end_rlp_addr) - %jump(decode_and_store_access_list_loop) -decode_and_store_access_list_finish: - %stack (rlp_addr, end_rlp_addr, retdest) -> (retdest, rlp_addr) - JUMP - -%macro add_address_cost - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) - %add_const(@GAS_ACCESSLISTADDRESS) - %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) -%endmacro - -%macro add_storage_key_cost - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) - %add_const(@GAS_ACCESSLISTSTORAGE) - %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_DATA_COST) -%endmacro - -insert_accessed_storage_keys_with_original_value: - %stack (addr, key, retdest) -> (key, addr, after_read, addr, key, retdest) - %jump(sload_with_addr) -after_read: - %stack (value, addr, key, retdest) -> ( addr, key, value, retdest) - %insert_accessed_storage_keys - %pop2 - JUMP - - -sload_with_addr: - %stack (slot, addr) -> (slot, addr, after_storage_read) - %slot_to_storage_key - // stack: storage_key, addr, after_storage_read - PUSH 64 // storage_key has 64 nibbles - %stack (n64, storage_key, addr, after_storage_read) -> (addr, n64, storage_key, after_storage_read) - %mpt_read_state_trie - // stack: account_ptr, 64, storage_key, after_storage_read - DUP1 ISZERO %jumpi(ret_zero) // TODO: Fix this. This should never happen. - // stack: account_ptr, 64, storage_key, after_storage_read - %add_const(2) - // stack: storage_root_ptr_ptr - %mload_trie_data - // stack: storage_root_ptr, 64, storage_key, after_storage_read - %jump(mpt_read) - -ret_zero: - // stack: account_ptr, 64, storage_key, after_storage_read, retdest - %pop4 - PUSH 0 SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/transactions/router.asm b/evm/src/cpu/kernel/asm/transactions/router.asm deleted file mode 100644 index edabfbc43a..0000000000 --- a/evm/src/cpu/kernel/asm/transactions/router.asm +++ /dev/null @@ -1,64 +0,0 @@ -// This is the entry point of transaction processing. We load the transaction -// RLP data into memory, check the transaction type, then based on the type we -// jump to the appropriate transaction parsing method. - -global route_txn: - // stack: txn_counter, num_nibbles, retdest - // First load transaction data into memory, where it will be parsed. - %stack(txn_counter, num_nibbles) -> (update_txn_trie, txn_counter, num_nibbles, read_txn_from_memory) - // stack: update_txn_trie, txn_counter, num_nibbles, read_txn_from_memory, retdest - %jump(read_rlp_to_memory) - -// At this point, the raw txn data is in memory. -read_txn_from_memory: - // stack: retdest - - // We will peak at the first byte to determine what type of transaction this is. - // Note that type 1 and 2 transactions have a first byte of 1 and 2, respectively. - // Type 0 (legacy) transactions have no such prefix, but their RLP will have a - // first byte >= 0xc0, so there is no overlap. - - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - MLOAD_GENERAL - %eq_const(1) - // stack: first_byte == 1, retdest - %jumpi(process_type_1_txn) - // stack: retdest - - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - MLOAD_GENERAL - %eq_const(2) - // stack: first_byte == 2, retdest - %jumpi(process_type_2_txn) - // stack: retdest - - // At this point, since it's not a type 1 or 2 transaction, - // it must be a legacy (aka type 0) transaction. - %jump(process_type_0_txn) - -global update_txn_trie: - // stack: txn_rlp_len, txn_counter, num_nibbles, retdest - // Copy the transaction rlp to the trie data segment. - %get_trie_data_size - // stack: value_ptr, txn_rlp_len, txn_counter, num_nibbles, retdest - SWAP1 - // First we write txn rlp length - DUP1 %append_to_trie_data - // stack: txn_rlp_len, value_ptr, txn_counter, num_nibbles, ret_dest - DUP2 %increment - // stack: rlp_start=value_ptr+1, txn_rlp_len, value_ptr, txn_counter, num_nibbles, retdest - - - // and now copy txn_rlp to the new block - %stack (rlp_start, txn_rlp_len, value_ptr, txn_counter, num_nibbles) -> ( - @SEGMENT_RLP_RAW, // src addr. ctx == virt == 0 - rlp_start, @SEGMENT_TRIE_DATA, // swapped dest addr, ctx == 0 - txn_rlp_len, // mcpy len - txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr) - SWAP2 %build_kernel_address - // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr - %memcpy_bytes - ADD - %set_trie_data_size - // stack: txn_counter, num_nibbles, value_ptr, retdest - %jump(mpt_insert_txn_trie) diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm deleted file mode 100644 index a3f3bb0d25..0000000000 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ /dev/null @@ -1,173 +0,0 @@ -// Type 0 transactions, aka legacy transaction, have the format -// rlp([nonce, gas_price, gas_limit, to, value, data, v, r, s]) -// -// The field v was originally encoded as -// 27 + y_parity -// but as of EIP 155 it can also be encoded as -// 35 + 2 * chain_id + y_parity -// -// If a chain_id is present in v, the signed data is -// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0])) -// otherwise, it is -// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data])) - -global process_type_0_txn: - // stack: retdest - PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 - // stack: rlp_addr, retdest - %decode_rlp_list_len - // We don't actually need the length. - %stack (rlp_addr, len) -> (rlp_addr) - - // stack: rlp_addr, retdest - %decode_and_store_nonce - %decode_and_store_gas_price_legacy - %decode_and_store_gas_limit - %decode_and_store_to - %decode_and_store_value - %decode_and_store_data - // stack: rlp_addr, retdest - - // Parse the "v" field. - // stack: rlp_addr, retdest - %decode_rlp_scalar - // stack: rlp_addr, v, retdest - SWAP1 - // stack: v, rlp_addr, retdest - DUP1 - %gt_const(28) - // stack: v > 28, v, rlp_addr, retdest - %jumpi(process_v_new_style) - - // We have an old style v, so y_parity = v - 27. - // No chain ID is present, so we can leave TXN_FIELD_CHAIN_ID_PRESENT and - // TXN_FIELD_CHAIN_ID with their default values of zero. - // stack: v, rlp_addr, retdest - %sub_const(27) - %stack (y_parity, rlp_addr) -> (y_parity, rlp_addr) - %mstore_txn_field(@TXN_FIELD_Y_PARITY) - - // stack: rlp_addr, retdest - %jump(decode_r_and_s) - -process_v_new_style: - // stack: v, rlp_addr, retdest - // We have a new style v, so chain_id_present = 1, - // chain_id = (v - 35) / 2, and y_parity = (v - 35) % 2. - %stack (v, rlp_addr) -> (1, v, rlp_addr) - %mstore_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) - - // stack: v, rlp_addr, retdest - %sub_const(35) - DUP1 - // stack: v - 35, v - 35, rlp_addr, retdest - %div2 - // stack: chain_id, v - 35, rlp_addr, retdest - %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - - // stack: v - 35, rlp_addr, retdest - %mod_const(2) - // stack: y_parity, rlp_addr, retdest - %mstore_txn_field(@TXN_FIELD_Y_PARITY) - -decode_r_and_s: - // stack: rlp_addr, retdest - %decode_and_store_r - %decode_and_store_s - // stack: rlp_addr, retdest - POP - // stack: retdest - -type_0_compute_signed_data: - // If a chain_id is present in v, the signed data is - // keccak256(rlp([nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0])) - // otherwise, it is - // keccak256(rlp([nonce, gas_price, gas_limit, to, value, data])) - - %alloc_rlp_block - // stack: rlp_addr_start, retdest - %mload_txn_field(@TXN_FIELD_NONCE) - // stack: nonce, rlp_addr_start, retdest - DUP2 - // stack: rlp_addr, nonce, rlp_addr_start, retdest - %encode_rlp_scalar - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_TO) - %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_addr, rlp_addr_start, retdest - SWAP1 %encode_rlp_160 - %jump(after_to) -zero_to: - // stack: to, rlp_addr, rlp_addr_start, retdest - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - -after_to: - %mload_txn_field(@TXN_FIELD_VALUE) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - // Encode txn data. - %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH @SEGMENT_TXN_DATA - // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest - PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest - SWAP3 - // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest - %jump(encode_rlp_string) - -after_serializing_txn_data: - // stack: rlp_addr, rlp_addr_start, retdest - %mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) - ISZERO %jumpi(finish_rlp_list) - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_CHAIN_ID) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - PUSH 0 - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - PUSH 0 - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - -finish_rlp_list: - %prepend_rlp_list_prefix - // stack: ADDR, rlp_len, retdest - KECCAK_GENERAL - // stack: hash, retdest - - %mload_txn_field(@TXN_FIELD_S) - %mload_txn_field(@TXN_FIELD_R) - %mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27 - - PUSH store_origin - // stack: store_origin, v, r, s, hash, retdest - SWAP4 - // stack: hash, v, r, s, store_origin, retdest - %jump(ecrecover) - -store_origin: - // stack: address, retdest - // If ecrecover returned u256::MAX, that indicates failure. - DUP1 - %eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - %jumpi(panic) - - // stack: address, retdest - %mstore_txn_field(@TXN_FIELD_ORIGIN) - // stack: retdest - %jump(process_normalized_txn) diff --git a/evm/src/cpu/kernel/asm/transactions/type_1.asm b/evm/src/cpu/kernel/asm/transactions/type_1.asm deleted file mode 100644 index e64a4aee03..0000000000 --- a/evm/src/cpu/kernel/asm/transactions/type_1.asm +++ /dev/null @@ -1,138 +0,0 @@ -// Type 1 transactions, introduced by EIP 2930, have the format -// 0x01 || rlp([chain_id, nonce, gas_price, gas_limit, to, value, data, -// access_list, y_parity, r, s]) -// -// The signed data is -// keccak256(0x01 || rlp([chain_id, nonce, gas_price, gas_limit, to, value, -// data, access_list])) - -global process_type_1_txn: - // stack: retdest - // Initial rlp address offset of 1 (skipping over the 0x01 byte) - PUSH 1 - PUSH @SEGMENT_RLP_RAW - %build_kernel_address - // stack: rlp_addr, retdest - %decode_rlp_list_len - // We don't actually need the length. - %stack (rlp_addr, len) -> (rlp_addr) - - %store_chain_id_present_true - %decode_and_store_chain_id - %decode_and_store_nonce - %decode_and_store_gas_price_legacy - %decode_and_store_gas_limit - %decode_and_store_to - %decode_and_store_value - %decode_and_store_data - %decode_and_store_access_list - %decode_and_store_y_parity - %decode_and_store_r - %decode_and_store_s - - // stack: rlp_addr, retdest - POP - // stack: retdest - -// From EIP-2930: -// The signatureYParity, signatureR, signatureS elements of this transaction represent a secp256k1 signature -// over keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])). -type_1_compute_signed_data: - %alloc_rlp_block - // stack: rlp_addr_start, retdest - %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: chain_id, rlp_addr_start, retdest - DUP2 - // stack: rlp_addr, chain_id, rlp_addr_start, retdest - %encode_rlp_scalar - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_NONCE) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - %mload_txn_field(@TXN_FIELD_TO) - %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_addr, rlp_addr_start, retdest - SWAP1 %encode_rlp_160 - %jump(after_to) -zero_to: - // stack: to, rlp_addr, rlp_addr_start, retdest - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - -after_to: - %mload_txn_field(@TXN_FIELD_VALUE) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_addr_start, retdest - - // Encode txn data. - %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 - // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest - PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest - SWAP3 - // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest - %jump(encode_rlp_string) - -after_serializing_txn_data: - // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_addr, rlp_addr_start, retdest) -> - ( - rlp_addr, - al_start, - al_len, - after_serializing_access_list, - rlp_addr, rlp_addr_start, retdest) - %jump(memcpy_bytes) -after_serializing_access_list: - // stack: rlp_addr, rlp_addr_start, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_addr, rlp_addr_start, retdest - %prepend_rlp_list_prefix - // stack: prefix_start_rlp_addr, rlp_len, retdest - - // Store a `1` in front of the RLP - %decrement - %stack (rlp_addr) -> (1, rlp_addr, rlp_addr) - MSTORE_GENERAL - // stack: rlp_addr, rlp_len, retdest - - // Hash the RLP + the leading `1` - SWAP1 %increment SWAP1 - // stack: ADDR, len, retdest - KECCAK_GENERAL - // stack: hash, retdest - - %mload_txn_field(@TXN_FIELD_S) - %mload_txn_field(@TXN_FIELD_R) - %mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27 - - PUSH store_origin - // stack: store_origin, v, r, s, hash, retdest - SWAP4 - // stack: hash, v, r, s, store_origin, retdest - %jump(ecrecover) - -store_origin: - // stack: address, retdest - // If ecrecover returned u256::MAX, that indicates failure. - DUP1 - %eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - %jumpi(panic) - - // stack: address, retdest - %mstore_txn_field(@TXN_FIELD_ORIGIN) - // stack: retdest - %jump(process_normalized_txn) diff --git a/evm/src/cpu/kernel/asm/transactions/type_2.asm b/evm/src/cpu/kernel/asm/transactions/type_2.asm deleted file mode 100644 index 5074c57950..0000000000 --- a/evm/src/cpu/kernel/asm/transactions/type_2.asm +++ /dev/null @@ -1,145 +0,0 @@ -// Type 2 transactions, introduced by EIP 1559, have the format -// 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, -// gas_limit, to, value, data, access_list, y_parity, r, s]) -// -// The signed data is -// keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, -// max_fee_per_gas, gas_limit, to, value, data, -// access_list])) - -global process_type_2_txn: - // stack: retdest - // Initial rlp address offset of 1 (skipping over the 0x02 byte) - PUSH 1 - PUSH @SEGMENT_RLP_RAW - %build_kernel_address - // stack: rlp_addr, retdest - %decode_rlp_list_len - // We don't actually need the length. - %stack (rlp_addr, len) -> (rlp_addr) - - // stack: rlp_addr, retdest - %store_chain_id_present_true - %decode_and_store_chain_id - %decode_and_store_nonce - %decode_and_store_max_priority_fee - %decode_and_store_max_fee - %decode_and_store_gas_limit - %decode_and_store_to - %decode_and_store_value - %decode_and_store_data - %decode_and_store_access_list - %decode_and_store_y_parity - %decode_and_store_r - %decode_and_store_s - - // stack: rlp_addr, retdest - POP - // stack: retdest - -// From EIP-1559: -// The signature_y_parity, signature_r, signature_s elements of this transaction represent a secp256k1 signature over -// keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list])) -type_2_compute_signed_data: - %alloc_rlp_block - // stack: rlp_addr_start, retdest - %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: chain_id, rlp_start, retdest - DUP2 - // stack: rlp_addr, chain_id, rlp_start, retdest - %encode_rlp_scalar - // stack: rlp_addr, rlp_start, retdest - - %mload_txn_field(@TXN_FIELD_NONCE) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - - %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - - %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - - %mload_txn_field(@TXN_FIELD_TO) - %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_addr, rlp_start, retdest - SWAP1 %encode_rlp_160 - %jump(after_to) -zero_to: - // stack: to, rlp_addr, rlp_start, retdest - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - -after_to: - %mload_txn_field(@TXN_FIELD_VALUE) - %encode_rlp_scalar_swapped_inputs - // stack: rlp_addr, rlp_start, retdest - - // Encode txn data. - %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 - // stack: ADDR, len, rlp_addr, rlp_start, retdest - PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_start, retdest - SWAP3 - // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_start, retdest - %jump(encode_rlp_string) - -after_serializing_txn_data: - // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_addr, rlp_start, retdest) -> - ( - rlp_addr, - al_start, - al_len, - after_serializing_access_list, - rlp_addr, rlp_start, retdest) - %jump(memcpy_bytes) -after_serializing_access_list: - // stack: rlp_addr, rlp_start, retdest - %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_addr, rlp_start, retdest - %prepend_rlp_list_prefix - // stack: prefix_start_pos, rlp_len, retdest - - // Store a `2` in front of the RLP - %decrement - %stack (rlp_addr) -> (2, rlp_addr, rlp_addr) - MSTORE_GENERAL - // stack: rlp_addr, rlp_len, retdest - - // Hash the RLP + the leading `2` - SWAP1 %increment SWAP1 - // stack: ADDR, len, retdest - KECCAK_GENERAL - // stack: hash, retdest - - %mload_txn_field(@TXN_FIELD_S) - %mload_txn_field(@TXN_FIELD_R) - %mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27 - - PUSH store_origin - // stack: store_origin, v, r, s, hash, retdest - SWAP4 - // stack: hash, v, r, s, store_origin, retdest - %jump(ecrecover) - -store_origin: - // stack: address, retdest - // If ecrecover returned u256::MAX, that indicates failure. - DUP1 - %eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - %jumpi(panic) - - // stack: address, retdest - %mstore_txn_field(@TXN_FIELD_ORIGIN) - // stack: retdest - %jump(process_normalized_txn) diff --git a/evm/src/cpu/kernel/asm/util/assertions.asm b/evm/src/cpu/kernel/asm/util/assertions.asm deleted file mode 100644 index 6c517407b1..0000000000 --- a/evm/src/cpu/kernel/asm/util/assertions.asm +++ /dev/null @@ -1,116 +0,0 @@ -// It is convenient to have a single panic routine, which we can jump to from -// anywhere. -global panic: - PANIC - -// Consumes the top element and asserts that it is zero. -%macro assert_zero - %jumpi(panic) -%endmacro - -%macro assert_zero(ret) - %jumpi($ret) -%endmacro - -// Consumes the top element and asserts that it is nonzero. -%macro assert_nonzero - ISZERO - %jumpi(panic) -%endmacro - -%macro assert_nonzero(ret) - ISZERO - %jumpi($ret) -%endmacro - -%macro assert_eq - SUB - %jumpi(panic) -%endmacro - -%macro assert_eq(ret) - SUB - %jumpi($ret) -%endmacro - -%macro assert_lt - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x < y) == !(x >= y). - GE - %assert_zero -%endmacro - -%macro assert_lt(ret) - GE - %assert_zero($ret) -%endmacro - -%macro assert_le - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x <= y) == !(x > y). - GT - %assert_zero -%endmacro - -%macro assert_le(ret) - GT - %assert_zero($ret) -%endmacro - -%macro assert_gt - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x > y) == !(x <= y). - LE - %assert_zero -%endmacro - -%macro assert_gt(ret) - LE - %assert_zero($ret) -%endmacro - -%macro assert_ge - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x >= y) == !(x < y). - LT - %assert_zero -%endmacro - -%macro assert_ge(ret) - LT - %assert_zero($ret) -%endmacro - -%macro assert_eq_const(c) - PUSH $c - SUB - %jumpi(panic) -%endmacro - -%macro assert_lt_const(c) - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x < c) == !(x >= c). - %ge_const($c) - %assert_zero -%endmacro - -%macro assert_le_const(c) - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x <= c) == !(x > c). - %gt_const($c) - %assert_zero -%endmacro - -%macro assert_gt_const(c) - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x > c) == !(x <= c). - %le_const($c) - %assert_zero -%endmacro - -%macro assert_ge_const(c) - // %assert_zero is cheaper than %assert_nonzero, so we will leverage the - // fact that (x >= c) == !(x < c). - %lt_const($c) - %assert_zero -%endmacro diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm deleted file mode 100644 index 78fd34fc1c..0000000000 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ /dev/null @@ -1,485 +0,0 @@ -%macro jump(dst) - PUSH $dst - jump -%endmacro - -%macro jumpi(dst) - PUSH $dst - jumpi -%endmacro - -// Jump to `jumpdest` if the top of the stack is != c -%macro jump_neq_const(c, jumpdest) - PUSH $c - SUB - %jumpi($jumpdest) -%endmacro - -// Jump to `jumpdest` if the top of the stack is < c -%macro jumpi_lt_const(c, jumpdest) - %ge_const($c) - %jumpi($jumpdest) -%endmacro - -%macro pop2 - %rep 2 - POP - %endrep -%endmacro - -%macro pop3 - %rep 3 - POP - %endrep -%endmacro - -%macro pop4 - %rep 4 - POP - %endrep -%endmacro - -%macro pop5 - %rep 5 - POP - %endrep -%endmacro - -%macro pop6 - %rep 6 - POP - %endrep -%endmacro - -%macro pop7 - %rep 7 - POP - %endrep -%endmacro - -%macro pop8 - %rep 8 - POP - %endrep -%endmacro - -%macro pop9 - %rep 9 - POP - %endrep -%endmacro - -%macro pop10 - %rep 10 - POP - %endrep -%endmacro - -%macro and_const(c) - // stack: input, ... - PUSH $c - AND - // stack: input & c, ... -%endmacro - -%macro add_const(c) - // stack: input, ... - PUSH $c - ADD - // stack: input + c, ... -%endmacro - -// Slightly inefficient as we need to swap the inputs. -// Consider avoiding this in performance-critical code. -%macro sub_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - SWAP1 - // stack: input, c, ... - SUB - // stack: input - c, ... -%endmacro - -%macro mul_const(c) - // stack: input, ... - PUSH $c - MUL - // stack: input * c, ... -%endmacro - -// Slightly inefficient as we need to swap the inputs. -// Consider avoiding this in performance-critical code. -%macro div_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - SWAP1 - // stack: input, c, ... - DIV - // stack: input / c, ... -%endmacro - -// Slightly inefficient as we need to swap the inputs. -// Consider avoiding this in performance-critical code. -%macro mod_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - SWAP1 - // stack: input, c, ... - MOD - // stack: input % c, ... -%endmacro - -%macro shl_const(c) - // stack: input, ... - PUSH $c - SHL - // stack: input << c, ... -%endmacro - -%macro shr_const(c) - // stack: input, ... - PUSH $c - SHR - // stack: input >> c, ... -%endmacro - -%macro eq_const(c) - // stack: input, ... - PUSH $c - EQ - // stack: input == c, ... -%endmacro - -%macro lt_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - GT // Check it backwards: (input < c) == (c > input) - // stack: input < c, ... -%endmacro - -%macro le_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - LT ISZERO // Check it backwards: (input <= c) == !(c < input) - // stack: input <= c, ... -%endmacro - -%macro gt_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - LT // Check it backwards: (input > c) == (c < input) - // stack: input >= c, ... -%endmacro - -%macro ge_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - GT ISZERO // Check it backwards: (input >= c) == !(c > input) - // stack: input >= c, ... -%endmacro - -// If pred is zero, yields z; otherwise, yields nz -%macro select - // stack: pred, nz, z - ISZERO - // stack: pred == 0, nz, z - DUP1 - // stack: pred == 0, pred == 0, nz, z - ISZERO - // stack: pred != 0, pred == 0, nz, z - SWAP3 - // stack: z, pred == 0, nz, pred != 0 - MUL - // stack: (pred == 0) * z, nz, pred != 0 - SWAP2 - // stack: pred != 0, nz, (pred == 0) * z - MUL - // stack: (pred != 0) * nz, (pred == 0) * z - ADD - // stack: (pred != 0) * nz + (pred == 0) * z -%endmacro - -// If pred, yields x; otherwise, yields y -// Assumes pred is boolean (either 0 or 1). -%macro select_bool - // stack: pred, y, x - DUP1 - // stack: pred, pred, y, x - ISZERO - // stack: notpred, pred, y, x - SWAP3 - // stack: x, pred, y, notpred - MUL - // stack: pred * x, y, notpred - SWAP2 - // stack: notpred, y, pred * x - MUL - // stack: notpred * y, pred * x - ADD - // stack: notpred * y + pred * x -%endmacro - -%macro square - // stack: x - DUP1 - // stack: x, x - MUL - // stack: x^2 -%endmacro - -%macro min - // stack: x, y - DUP2 - DUP2 - // stack: x, y, x, y - GT - // stack: x > y, x, y - %select_bool - // stack: min -%endmacro - -%macro max - // stack: x, y - DUP2 - DUP2 - // stack: x, y, x, y - LT - // stack: x < y, x, y - %select_bool - // stack: max -%endmacro - -%macro max_3 - // stack: x, y, z - %max - // stack: max(x, y), z - SWAP1 - // stack: z, max(x, y) - %max - // stack: max(x, y, z) -%endmacro - -%macro max_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - %max - // stack: max(input, c), ... -%endmacro - -%macro min_const(c) - // stack: input, ... - PUSH $c - // stack: c, input, ... - %min - // stack: min(input, c), ... -%endmacro - -%macro ceil_div - // stack: x, y - PUSH 1 - DUP3 - SUB // y - 1 - // stack: y - 1, x, y - ADD - DIV - // stack: ceil(x / y) -%endmacro - -%macro ceil_div_const(c) - // stack: x, ... - PUSH $c - // stack: c, x, ... - SWAP1 - // stack: x, c, ... - %ceil_div - // stack: ceil(x / c), ... -%endmacro - -%macro as_u32 - %and_const(0xffffffff) -%endmacro - -%macro as_u64 - %and_const(0xffffffffffffffff) -%endmacro - -%macro not_u32 - // stack: x - PUSH 0xffffffff - // stack: 0xffffffff, x - SUB - // stack: 0xffffffff - x -%endmacro - -// u32 addition (discarding 2^32 bit) -%macro add_u32 - // stack: x, y - ADD - // stack: x + y - %as_u32 - // stack: (x + y) & u32::MAX -%endmacro - -%macro add3_u32 - // stack: x , y , z - ADD - // stack: x + y , z - ADD - // stack: x + y + z - %as_u32 -%endmacro - -%macro increment - %add_const(1) -%endmacro - -%macro decrement - %sub_const(1) -%endmacro - -%macro div2 - // stack: x - PUSH 1 - SHR - // stack: x >> 1 -%endmacro - -%macro iseven - %mod_const(2) - ISZERO -%endmacro - -// given u32 bytestring abcd return dcba -%macro reverse_bytes_u32 - // stack: abcd - DUP1 - PUSH 28 - BYTE - // stack: a, abcd - DUP2 - PUSH 29 - BYTE - %shl_const(8) - // stack: b0, a, abcd - DUP3 - PUSH 30 - BYTE - %shl_const(16) - // stack: c00, b0, a, abcd - SWAP3 - PUSH 31 - BYTE - %shl_const(24) - // stack: d000, b0, a, c00 - ADD // OR - ADD // OR - ADD // OR - // stack: dcba -%endmacro - -%macro reverse_bytes_u64 - // stack: word - DUP1 - // stack: word, word - %and_const(0xffffffff) - // stack: word_lo, word - SWAP1 - // stack: word, word_lo - %shr_const(32) - // stack: word_hi, word_lo - %reverse_bytes_u32 - // stack: word_hi_inverted, word_lo - SWAP1 - // stack: word_lo, word_hi_inverted - %reverse_bytes_u32 - // stack: word_lo_inverted, word_hi_inverted - %shl_const(32) - ADD // OR - // stack: word_inverted -%endmacro - -// Combine four big-endian u64s into a u256. -%macro u64s_to_u256 - // stack: a, b, c, d - %rep 3 - %shl_const(64) - ADD // OR - %endrep - // stack: a || b || c || d -%endmacro - -%macro u256_to_addr - // stack: x - %mod_const(0x10000000000000000000000000000000000000000) // 2^160 -%endmacro - -%macro not_bit - // stack: b - ISZERO - // stack: not b -%endmacro - -%macro build_address - // stack: ctx, seg, off - ADD - ADD - // stack: addr -%endmacro - -%macro build_address_no_offset - // stack: ctx, seg - ADD - // stack: addr -%endmacro - -%macro build_current_general_address - // stack: offset - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address - // stack: addr -%endmacro - -%macro build_current_general_address_no_offset - // stack: - PUSH @SEGMENT_KERNEL_GENERAL - GET_CONTEXT - %build_address_no_offset - // stack: addr (offset == 0) -%endmacro - -%macro build_kernel_address - // stack: seg, off - ADD - // stack: addr (ctx == 0) -%endmacro - -%macro build_address_with_ctx(seg, off) - // stack: ctx - PUSH $seg - PUSH $off - %build_address - // stack: addr -%endmacro - -%macro build_address_with_ctx_no_offset(seg) - // stack: ctx - PUSH $seg - ADD - // stack: addr -%endmacro - -%macro build_address_with_ctx_no_segment(off) - // stack: ctx - PUSH $off - ADD - // stack: addr -%endmacro diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm deleted file mode 100644 index dceb7b195b..0000000000 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ /dev/null @@ -1,64 +0,0 @@ -global sys_keccak256: - // stack: kexit_info, offset, len - PUSH @GAS_KECCAK256 - DUP4 - // stack: len, static_gas, kexit_info, offset, len - ISZERO %jumpi(sys_keccak256_empty) - // stack: static_gas, kexit_info, offset, len - DUP4 %num_bytes_to_num_words %mul_const(@GAS_KECCAK256WORD) - ADD - %charge_gas - // stack: kexit_info, offset, len - - %stack (kexit_info, offset, len) -> (offset, len, kexit_info, offset, len) - %add_or_fault - DUP1 %ensure_reasonable_offset - %update_mem_bytes - - %stack (kexit_info, offset, len) -> (offset, len, kexit_info) - PUSH @SEGMENT_MAIN_MEMORY - GET_CONTEXT - %build_address - // stack: ADDR, len, kexit_info - KECCAK_GENERAL - // stack: hash, kexit_info - SWAP1 - EXIT_KERNEL - -sys_keccak256_empty: - // stack: static_gas, kexit_info, offset, len - %charge_gas - %stack (kexit_info, offset, len) -> (kexit_info, @EMPTY_STRING_HASH) - EXIT_KERNEL - -// Computes Keccak256(input_word). Clobbers @SEGMENT_KERNEL_GENERAL. -// -// Pre stack: input_word -// Post stack: hash -%macro keccak256_word(num_bytes) - // Since KECCAK_GENERAL takes its input from memory, we will first write - // input_word's bytes to @SEGMENT_KERNEL_GENERAL[0..$num_bytes]. - %stack (word) -> (@SEGMENT_KERNEL_GENERAL, word, $num_bytes, %%after_mstore, $num_bytes, $num_bytes) - %jump(mstore_unpacking) -%%after_mstore: - // stack: addr, $num_bytes, $num_bytes - SUB - KECCAK_GENERAL -%endmacro - -// Computes Keccak256(a || b). Clobbers @SEGMENT_KERNEL_GENERAL. -// -// Pre stack: a, b -// Post stack: hash -%macro keccak256_u256_pair - // Since KECCAK_GENERAL takes its input from memory, we will first write - // a's bytes to @SEGMENT_KERNEL_GENERAL[0..32], then b's bytes to - // @SEGMENT_KERNEL_GENERAL[32..64]. - %stack (a) -> (@SEGMENT_KERNEL_GENERAL, a) - MSTORE_32BYTES_32 - // stack: addr, b - MSTORE_32BYTES_32 - %stack (addr) -> (addr, 64, 64) // reset the address offset - SUB - KECCAK_GENERAL -%endmacro diff --git a/evm/src/cpu/kernel/asm/util/math.asm b/evm/src/cpu/kernel/asm/util/math.asm deleted file mode 100644 index 4bdf690238..0000000000 --- a/evm/src/cpu/kernel/asm/util/math.asm +++ /dev/null @@ -1,37 +0,0 @@ -log2_floor_helper: - // stack: val, counter, retdest - DUP1 - // stack: val, val, counter, retdest - ISZERO - %jumpi(end) - // stack: val, counter, retdest - %div2 - // stack: val/2, counter, retdest - SWAP1 - %increment - SWAP1 - // stack: val/2, counter + 1, retdest - %jump(log2_floor_helper) -end: - // stack: val, counter, retdest - POP - // stack: counter, retdest - SWAP1 - // stack: retdest, counter - JUMP - -global log2_floor: - // stack: val, retdest - %div2 - // stack: val/2, retdest - PUSH 0 - // stack: 0, val/2, retdest - SWAP1 - // stack: val/2, 0, retdest - %jump(log2_floor_helper) - -%macro log2_floor - %stack (val) -> (val, %%after) - %jump(log2_floor) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs deleted file mode 100644 index 2dc79d6111..0000000000 --- a/evm/src/cpu/kernel/assembler.rs +++ /dev/null @@ -1,731 +0,0 @@ -use std::collections::HashMap; -use std::fs; -use std::time::Instant; - -use ethereum_types::{H256, U256}; -use itertools::{izip, Itertools}; -use keccak_hash::keccak; -use log::debug; -use serde::{Deserialize, Serialize}; - -use super::ast::{BytesTarget, PushTarget}; -use crate::cpu::kernel::ast::Item::LocalLabelDeclaration; -use crate::cpu::kernel::ast::{File, Item, StackReplacement}; -use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -use crate::cpu::kernel::optimizer::optimize_asm; -use crate::cpu::kernel::stack::stack_manipulation::expand_stack_manipulation; -use crate::cpu::kernel::utils::u256_to_trimmed_be_bytes; -use crate::generation::prover_input::ProverInputFn; - -/// The number of bytes to push when pushing an offset within the code (i.e. when assembling jumps). -/// Ideally we would automatically use the minimal number of bytes required, but that would be -/// nontrivial given the circular dependency between an offset and its size. -pub(crate) const BYTES_PER_OFFSET: u8 = 3; - -#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)] -pub struct Kernel { - pub(crate) code: Vec, - - /// Computed using `hash_kernel`. - pub(crate) code_hash: H256, - - pub(crate) global_labels: HashMap, - pub(crate) ordered_labels: Vec, - - /// Map from `PROVER_INPUT` offsets to their corresponding `ProverInputFn`. - pub(crate) prover_inputs: HashMap, -} - -impl Kernel { - fn new( - code: Vec, - global_labels: HashMap, - prover_inputs: HashMap, - ) -> Self { - let code_hash = keccak(&code); - let ordered_labels = global_labels - .keys() - .cloned() - .sorted_by_key(|label| global_labels[label]) - .inspect(|key| debug!("Global label: {} => {:?}", key, global_labels[key])) - .collect(); - Self { - code, - code_hash, - global_labels, - ordered_labels, - prover_inputs, - } - } - - pub fn to_file(&self, path: &str) { - let kernel_serialized = serde_json::to_string(self).unwrap(); - fs::write(path, kernel_serialized).expect("Unable to write kernel to file"); - } - - pub fn from_file(path: &str) -> Self { - let bytes = fs::read(path).expect("Unable to read kernel file"); - serde_json::from_slice(&bytes).unwrap() - } - - /// Get a string representation of the current offset for debugging purposes. - pub(crate) fn offset_name(&self, offset: usize) -> String { - match self - .ordered_labels - .binary_search_by_key(&offset, |label| self.global_labels[label]) - { - Ok(idx) => self.ordered_labels[idx].clone(), - Err(0) => offset.to_string(), - Err(idx) => format!("{}, below {}", offset, self.ordered_labels[idx - 1]), - } - } - - pub(crate) fn offset_label(&self, offset: usize) -> Option { - self.global_labels - .iter() - .find_map(|(k, v)| (*v == offset).then(|| k.clone())) - } -} - -#[derive(Eq, PartialEq, Hash, Clone, Debug)] -struct MacroSignature { - name: String, - num_params: usize, -} - -struct Macro { - params: Vec, - items: Vec, -} - -impl Macro { - fn get_param_index(&self, param: &str) -> usize { - self.params - .iter() - .position(|p| p == param) - .unwrap_or_else(|| panic!("No such param: {param} {:?}", &self.params)) - } -} - -pub(crate) fn assemble( - files: Vec, - constants: HashMap, - optimize: bool, -) -> Kernel { - let macros = find_macros(&files); - let mut global_labels = HashMap::new(); - let mut prover_inputs = HashMap::new(); - let mut offset = 0; - let mut expanded_files = Vec::with_capacity(files.len()); - let mut local_labels = Vec::with_capacity(files.len()); - let mut macro_counter = 0; - for file in files { - let start = Instant::now(); - let mut file = file.body; - file = expand_macros(file, ¯os, &mut macro_counter); - file = inline_constants(file, &constants); - file = expand_stack_manipulation(file); - if optimize { - optimize_asm(&mut file); - } - local_labels.push(find_labels( - &file, - &mut offset, - &mut global_labels, - &mut prover_inputs, - )); - expanded_files.push(file); - debug!("Expanding file took {:?}", start.elapsed()); - } - let mut code = vec![]; - for (file, locals) in izip!(expanded_files, local_labels) { - let prev_len = code.len(); - assemble_file(file, &mut code, locals, &global_labels); - let file_len = code.len() - prev_len; - debug!("Assembled file size: {} bytes", file_len); - } - assert_eq!(code.len(), offset, "Code length doesn't match offset."); - debug!("Total kernel size: {} bytes", code.len()); - Kernel::new(code, global_labels, prover_inputs) -} - -fn find_macros(files: &[File]) -> HashMap { - let mut macros = HashMap::new(); - for file in files { - for item in &file.body { - if let Item::MacroDef(name, params, items) = item { - let signature = MacroSignature { - name: name.clone(), - num_params: params.len(), - }; - let macro_ = Macro { - params: params.clone(), - items: items.clone(), - }; - let old = macros.insert(signature.clone(), macro_); - assert!(old.is_none(), "Duplicate macro signature: {signature:?}"); - } - } - } - macros -} - -fn expand_macros( - body: Vec, - macros: &HashMap, - macro_counter: &mut u32, -) -> Vec { - let mut expanded = vec![]; - for item in body { - match item { - Item::MacroDef(_, _, _) => { - // At this phase, we no longer need macro definitions. - } - Item::MacroCall(m, args) => { - expanded.extend(expand_macro_call(m, args, macros, macro_counter)); - } - Item::Repeat(count, body) => { - for _ in 0..count.as_usize() { - expanded.extend(expand_macros(body.clone(), macros, macro_counter)); - } - } - item => { - expanded.push(item); - } - } - } - expanded -} - -fn expand_macro_call( - name: String, - args: Vec, - macros: &HashMap, - macro_counter: &mut u32, -) -> Vec { - let signature = MacroSignature { - name, - num_params: args.len(), - }; - let macro_ = macros - .get(&signature) - .unwrap_or_else(|| panic!("No such macro: {signature:?}")); - - let get_actual_label = |macro_label| format!("@{macro_counter}.{macro_label}"); - - let get_arg = |var| { - let param_index = macro_.get_param_index(var); - args[param_index].clone() - }; - - let expanded_item = macro_ - .items - .iter() - .map(|item| match item { - Item::MacroLabelDeclaration(label) => LocalLabelDeclaration(get_actual_label(label)), - Item::Push(PushTarget::MacroLabel(label)) => { - Item::Push(PushTarget::Label(get_actual_label(label))) - } - Item::Push(PushTarget::MacroVar(var)) => Item::Push(get_arg(var)), - Item::MacroCall(name, args) => { - let expanded_args = args - .iter() - .map(|arg| match arg { - PushTarget::MacroVar(var) => get_arg(var), - PushTarget::MacroLabel(l) => PushTarget::Label(get_actual_label(l)), - _ => arg.clone(), - }) - .collect(); - Item::MacroCall(name.clone(), expanded_args) - } - Item::StackManipulation(before, after) => { - let after = after - .iter() - .map(|replacement| match replacement { - StackReplacement::MacroLabel(label) => { - StackReplacement::Identifier(get_actual_label(label)) - } - StackReplacement::MacroVar(var) => get_arg(var).into(), - _ => replacement.clone(), - }) - .collect(); - Item::StackManipulation(before.clone(), after) - } - _ => item.clone(), - }) - .collect(); - - *macro_counter += 1; - - // Recursively expand any macros in the expanded code. - expand_macros(expanded_item, macros, macro_counter) -} - -fn inline_constants(body: Vec, constants: &HashMap) -> Vec { - let resolve_const = |c| { - *constants - .get(&c) - .unwrap_or_else(|| panic!("No such constant: {c}")) - }; - - body.into_iter() - .map(|item| { - if let Item::Push(PushTarget::Constant(c)) = item { - Item::Push(PushTarget::Literal(resolve_const(c))) - } else if let Item::Bytes(targets) = item { - let targets = targets - .into_iter() - .map(|target| { - if let BytesTarget::Constant(c) = target { - let c = resolve_const(c); - assert!( - c < U256::from(256), - "Constant in a BYTES object should be a byte" - ); - BytesTarget::Literal(c.byte(0)) - } else { - target - } - }) - .collect(); - Item::Bytes(targets) - } else if let Item::StackManipulation(from, to) = item { - let to = to - .into_iter() - .map(|replacement| { - if let StackReplacement::Constant(c) = replacement { - StackReplacement::Literal(resolve_const(c)) - } else { - replacement - } - }) - .collect(); - Item::StackManipulation(from, to) - } else { - item - } - }) - .collect() -} - -fn find_labels( - body: &[Item], - offset: &mut usize, - global_labels: &mut HashMap, - prover_inputs: &mut HashMap, -) -> HashMap { - // Discover the offset of each label in this file. - let mut local_labels = HashMap::::new(); - for item in body { - match item { - Item::MacroDef(_, _, _) - | Item::MacroCall(_, _) - | Item::Repeat(_, _) - | Item::StackManipulation(_, _) - | Item::MacroLabelDeclaration(_) => { - panic!("Item should have been expanded already: {item:?}"); - } - Item::GlobalLabelDeclaration(label) => { - let old = global_labels.insert(label.clone(), *offset); - assert!(old.is_none(), "Duplicate global label: {label}"); - } - Item::LocalLabelDeclaration(label) => { - let old = local_labels.insert(label.clone(), *offset); - assert!(old.is_none(), "Duplicate local label: {label}"); - } - Item::Push(target) => *offset += 1 + push_target_size(target) as usize, - Item::ProverInput(prover_input_fn) => { - prover_inputs.insert(*offset, prover_input_fn.clone()); - *offset += 1; - } - Item::StandardOp(_) => *offset += 1, - Item::Bytes(bytes) => *offset += bytes.len(), - Item::Jumptable(labels) => *offset += labels.len() * (BYTES_PER_OFFSET as usize), - } - } - local_labels -} - -fn look_up_label( - label: &String, - local_labels: &HashMap, - global_labels: &HashMap, -) -> Vec { - let offset = local_labels - .get(label) - .or_else(|| global_labels.get(label)) - .unwrap_or_else(|| panic!("No such label: {label}")); - // We want the BYTES_PER_OFFSET least significant bytes in BE order. - // It's easiest to rev the first BYTES_PER_OFFSET bytes of the LE encoding. - (0..BYTES_PER_OFFSET) - .rev() - .map(|i| offset.to_le_bytes()[i as usize]) - .collect() -} - -fn assemble_file( - body: Vec, - code: &mut Vec, - local_labels: HashMap, - global_labels: &HashMap, -) { - // Assemble the file. - for item in body { - match item { - Item::MacroDef(_, _, _) - | Item::MacroCall(_, _) - | Item::Repeat(_, _) - | Item::StackManipulation(_, _) - | Item::MacroLabelDeclaration(_) => { - panic!("Item should have been expanded already: {item:?}"); - } - Item::GlobalLabelDeclaration(_) | Item::LocalLabelDeclaration(_) => { - // Nothing to do; we processed labels in the prior phase. - } - Item::Push(target) => { - let target_bytes: Vec = match target { - PushTarget::Literal(n) => u256_to_trimmed_be_bytes(&n), - PushTarget::Label(label) => look_up_label(&label, &local_labels, global_labels), - PushTarget::MacroLabel(v) => panic!("Macro label not in a macro: {v}"), - PushTarget::MacroVar(v) => panic!("Variable not in a macro: {v}"), - PushTarget::Constant(c) => panic!("Constant wasn't inlined: {c}"), - }; - code.push(get_push_opcode(target_bytes.len() as u8)); - code.extend(target_bytes); - } - Item::ProverInput(_) => { - code.push(get_opcode("PROVER_INPUT")); - } - Item::StandardOp(opcode) => { - code.push(get_opcode(&opcode)); - } - Item::Bytes(targets) => { - for target in targets { - match target { - BytesTarget::Literal(n) => code.push(n), - BytesTarget::Constant(c) => panic!("Constant wasn't inlined: {c}"), - } - } - } - Item::Jumptable(labels) => { - for label in labels { - let bytes = look_up_label(&label, &local_labels, global_labels); - code.extend(bytes); - } - } - } - } -} - -/// The size of a `PushTarget`, in bytes. -fn push_target_size(target: &PushTarget) -> u8 { - match target { - PushTarget::Literal(n) => u256_to_trimmed_be_bytes(n).len() as u8, - PushTarget::Label(_) => BYTES_PER_OFFSET, - PushTarget::MacroLabel(v) => panic!("Macro label not in a macro: {v}"), - PushTarget::MacroVar(v) => panic!("Variable not in a macro: {v}"), - PushTarget::Constant(c) => panic!("Constant wasn't inlined: {c}"), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cpu::kernel::parser::parse; - - #[test] - fn two_files() { - // We will test two simple files, with a label and a jump, to ensure that jump offsets - // are correctly shifted based on the offset of the containing file. - - let file_1 = File { - body: vec![ - Item::GlobalLabelDeclaration("function_1".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - Item::StandardOp("ADD".to_string()), - Item::StandardOp("MUL".to_string()), - ], - }; - - let file_2 = File { - body: vec![ - Item::GlobalLabelDeclaration("function_2".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - Item::StandardOp("DIV".to_string()), - Item::LocalLabelDeclaration("mylabel".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - Item::StandardOp("MOD".to_string()), - Item::Push(PushTarget::Label("mylabel".to_string())), - Item::StandardOp("JUMP".to_string()), - ], - }; - - let expected_code = vec![ - get_opcode("JUMPDEST"), - get_opcode("ADD"), - get_opcode("MUL"), - get_opcode("JUMPDEST"), - get_opcode("DIV"), - get_opcode("JUMPDEST"), - get_opcode("MOD"), - get_push_opcode(BYTES_PER_OFFSET), - // The label offset, 5, in 3-byte BE form. - 0, - 0, - 5, - get_opcode("JUMP"), - ]; - - let mut expected_global_labels = HashMap::new(); - expected_global_labels.insert("function_1".to_string(), 0); - expected_global_labels.insert("function_2".to_string(), 3); - - let expected_kernel = Kernel::new(expected_code, expected_global_labels, HashMap::new()); - - let program = vec![file_1, file_2]; - assert_eq!(assemble(program, HashMap::new(), false), expected_kernel); - } - - #[test] - #[should_panic] - fn global_label_collision() { - let file_1 = File { - body: vec![ - Item::GlobalLabelDeclaration("foo".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - ], - }; - let file_2 = File { - body: vec![ - Item::GlobalLabelDeclaration("foo".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - ], - }; - assemble(vec![file_1, file_2], HashMap::new(), false); - } - - #[test] - #[should_panic] - fn local_label_collision() { - let file = File { - body: vec![ - Item::LocalLabelDeclaration("foo".to_string()), - Item::StandardOp("JUMPDEST".to_string()), - Item::LocalLabelDeclaration("foo".to_string()), - Item::StandardOp("ADD".to_string()), - ], - }; - assemble(vec![file], HashMap::new(), false); - } - - #[test] - fn literal_bytes() { - let file = File { - body: vec![ - Item::Bytes(vec![BytesTarget::Literal(0x12), BytesTarget::Literal(42)]), - Item::Bytes(vec![BytesTarget::Literal(0xFE), BytesTarget::Literal(255)]), - ], - }; - let code = assemble(vec![file], HashMap::new(), false).code; - assert_eq!(code, vec![0x12, 42, 0xfe, 255]); - } - - #[test] - fn macro_in_macro() { - let kernel = parse_and_assemble(&[ - "%macro foo %bar %bar %endmacro", - "%macro bar ADD %endmacro", - "%foo", - ]); - let add = get_opcode("ADD"); - assert_eq!(kernel.code, vec![add, add]); - } - - #[test] - fn macro_with_vars() { - let files = &[ - "%macro add(x, y) PUSH $x PUSH $y ADD %endmacro", - "%add(2, 3)", - ]; - let kernel = parse_and_assemble_ext(files, HashMap::new(), false); - let push1 = get_push_opcode(1); - let add = get_opcode("ADD"); - assert_eq!(kernel.code, vec![push1, 2, push1, 3, add]); - } - - #[test] - fn macro_with_label() { - let files = &[ - "%macro jump(x) PUSH $x JUMP %endmacro", - "%macro spin %%start: %jump(%%start) %endmacro", - "%spin %spin", - ]; - let kernel = parse_and_assemble_ext(files, HashMap::new(), false); - let push3 = get_push_opcode(BYTES_PER_OFFSET); - let jump = get_opcode("JUMP"); - assert_eq!( - kernel.code, - vec![push3, 0, 0, 0, jump, push3, 0, 0, 5, jump] - ); - } - - #[test] - fn macro_in_macro_with_vars() { - let kernel = parse_and_assemble(&[ - "%macro foo(x) %bar($x) %bar($x) %endmacro", - "%macro bar(y) PUSH $y %endmacro", - "%foo(42)", - ]); - let push1 = get_push_opcode(1); - assert_eq!(kernel.code, vec![push1, 42, push1, 42]); - } - - #[test] - fn macro_with_reserved_prefix() { - // The name `repeat` should be allowed, even though `rep` is reserved. - parse_and_assemble(&["%macro repeat %endmacro", "%repeat"]); - } - - #[test] - fn overloaded_macros() { - let kernel = parse_and_assemble(&[ - "%macro push(x) PUSH $x %endmacro", - "%macro push(x, y) PUSH $x PUSH $y %endmacro", - "%push(5)", - "%push(6, 7)", - ]); - let push1 = get_push_opcode(1); - assert_eq!(kernel.code, vec![push1, 5, push1, 6, push1, 7]); - } - - #[test] - fn pop2_macro() { - parse_and_assemble(&["%macro pop2 %rep 2 pop %endrep %endmacro", "%pop2"]); - } - - #[test] - #[should_panic] - fn macro_with_wrong_vars() { - parse_and_assemble(&[ - "%macro add(x, y) PUSH $x PUSH $y ADD %endmacro", - "%add(2, 3, 4)", - ]); - } - - #[test] - #[should_panic] - fn var_not_in_macro() { - parse_and_assemble(&["push $abc"]); - } - - #[test] - fn constants() { - let code = &["PUSH @DEAD_BEEF"]; - let mut constants = HashMap::new(); - constants.insert("DEAD_BEEF".into(), 0xDEADBEEFu64.into()); - - let kernel = parse_and_assemble_ext(code, constants, true); - let push4 = get_push_opcode(4); - assert_eq!(kernel.code, vec![push4, 0xDE, 0xAD, 0xBE, 0xEF]); - } - - #[test] - fn repeat() { - let kernel = parse_and_assemble(&["%rep 3 ADD %endrep"]); - let add = get_opcode("ADD"); - assert_eq!(kernel.code, vec![add, add, add]); - } - - #[test] - fn stack_manipulation() { - let pop = get_opcode("POP"); - let dup1 = get_opcode("DUP1"); - let swap1 = get_opcode("SWAP1"); - let swap2 = get_opcode("SWAP2"); - let swap3 = get_opcode("SWAP3"); - let push_one_byte = get_push_opcode(1); - let push_label = get_push_opcode(BYTES_PER_OFFSET); - - let kernel = parse_and_assemble(&["%stack () -> (1, 2, 3)"]); - assert_eq!( - kernel.code, - vec![push_one_byte, 3, push_one_byte, 2, push_one_byte, 1] - ); - - let kernel = parse_and_assemble(&["%stack (a) -> (a)"]); - assert_eq!(kernel.code, vec![] as Vec); - - let kernel = parse_and_assemble(&["%stack (a, b, c) -> (c, b, a)"]); - assert_eq!(kernel.code, vec![swap2]); - - let kernel = parse_and_assemble(&["%stack (a, b, c) -> (b)"]); - assert_eq!(kernel.code, vec![pop, swap1, pop]); - - let kernel = parse_and_assemble(&["%stack (a, b, c) -> (7, b)"]); - assert_eq!(kernel.code, vec![pop, swap1, pop, push_one_byte, 7]); - - let kernel = parse_and_assemble(&["%stack (a, b: 3, c) -> (c)"]); - assert_eq!(kernel.code, vec![pop, pop, pop, pop]); - - let kernel = parse_and_assemble(&["%stack (a: 2, b: 2) -> (b, a)"]); - assert_eq!(kernel.code, vec![swap1, swap3, swap1, swap2]); - - let kernel1 = parse_and_assemble(&["%stack (a: 3, b: 3, c) -> (c, b, a)"]); - let kernel2 = - parse_and_assemble(&["%stack (a, b, c, d, e, f, g) -> (g, d, e, f, a, b, c)"]); - assert_eq!(kernel1.code, kernel2.code); - - let mut consts = HashMap::new(); - consts.insert("LIFE".into(), 42.into()); - parse_and_assemble_ext(&["%stack (a, b) -> (b, @LIFE)"], consts, true); - // We won't check the code since there are two equally efficient implementations. - - let kernel = parse_and_assemble(&["start: %stack (a, b) -> (start)"]); - assert_eq!(kernel.code, vec![pop, pop, push_label, 0, 0, 0]); - - // The "start" label gets shadowed by the "start" named stack item. - let kernel = parse_and_assemble(&["start: %stack (start) -> (start, start)"]); - assert_eq!(kernel.code, vec![dup1]); - } - - #[test] - fn stack_manipulation_in_macro() { - let pop = get_opcode("POP"); - let push1 = get_push_opcode(1); - - let kernel = parse_and_assemble(&[ - "%macro set_top(x) %stack (a) -> ($x) %endmacro", - "%set_top(42)", - ]); - assert_eq!(kernel.code, vec![pop, push1, 42]); - } - - #[test] - fn stack_manipulation_in_macro_with_name_collision() { - let pop = get_opcode("POP"); - let push_label = get_push_opcode(BYTES_PER_OFFSET); - - // In the stack directive, there's a named item `foo`. - // But when we invoke `%foo(foo)`, the argument refers to the `foo` label. - // Thus the expanded macro is `%stack (foo) -> (label foo)` (not real syntax). - let kernel = parse_and_assemble(&[ - "global foo:", - "%macro foo(x) %stack (foo) -> ($x) %endmacro", - "%foo(foo)", - ]); - assert_eq!(kernel.code, vec![pop, push_label, 0, 0, 0]); - } - - fn parse_and_assemble(files: &[&str]) -> Kernel { - parse_and_assemble_ext(files, HashMap::new(), true) - } - - fn parse_and_assemble_ext( - files: &[&str], - constants: HashMap, - optimize: bool, - ) -> Kernel { - let parsed_files = files.iter().map(|f| parse(f)).collect_vec(); - assemble(parsed_files, constants, optimize) - } -} diff --git a/evm/src/cpu/kernel/ast.rs b/evm/src/cpu/kernel/ast.rs deleted file mode 100644 index 0af3bdabeb..0000000000 --- a/evm/src/cpu/kernel/ast.rs +++ /dev/null @@ -1,84 +0,0 @@ -use ethereum_types::U256; - -use crate::generation::prover_input::ProverInputFn; - -#[derive(Debug)] -pub(crate) struct File { - pub(crate) body: Vec, -} - -#[derive(Eq, PartialEq, Clone, Debug)] -pub(crate) enum Item { - /// Defines a new macro: name, params, body. - MacroDef(String, Vec, Vec), - /// Calls a macro: name, args. - MacroCall(String, Vec), - /// Repetition, like `%rep` in NASM. - Repeat(U256, Vec), - /// A directive to manipulate the stack according to a specified pattern. - /// The first list gives names to items on the top of the stack. - /// The second list specifies replacement items. - /// Example: `(a, b, c) -> (c, 5, 0x20, @SOME_CONST, a)`. - StackManipulation(Vec, Vec), - /// Declares a global label. - GlobalLabelDeclaration(String), - /// Declares a label that is local to the current file. - LocalLabelDeclaration(String), - /// Declares a label that is local to the macro it's declared in. - MacroLabelDeclaration(String), - /// A `PUSH` operation. - Push(PushTarget), - /// A `ProverInput` operation. - ProverInput(ProverInputFn), - /// Any opcode besides a PUSH opcode. - StandardOp(String), - /// Literal hex data; should contain an even number of hex chars. - Bytes(Vec), - /// Creates a table of addresses from a list of labels. - Jumptable(Vec), -} - -/// The left hand side of a %stack stack-manipulation macro. -#[derive(Eq, PartialEq, Clone, Debug)] -pub(crate) struct StackPlaceholder(pub String, pub usize); - -/// The right hand side of a %stack stack-manipulation macro. -#[derive(Eq, PartialEq, Clone, Debug)] -pub(crate) enum StackReplacement { - Literal(U256), - /// Can be either a named item or a label. - Identifier(String), - Label(String), - MacroLabel(String), - MacroVar(String), - Constant(String), -} - -impl From for StackReplacement { - fn from(target: PushTarget) -> Self { - match target { - PushTarget::Literal(x) => Self::Literal(x), - PushTarget::Label(l) => Self::Label(l), - PushTarget::MacroLabel(l) => Self::MacroLabel(l), - PushTarget::MacroVar(v) => Self::MacroVar(v), - PushTarget::Constant(c) => Self::Constant(c), - } - } -} - -/// The target of a `PUSH` operation. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub(crate) enum PushTarget { - Literal(U256), - Label(String), - MacroLabel(String), - MacroVar(String), - Constant(String), -} - -/// The target of a `BYTES` item. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub(crate) enum BytesTarget { - Literal(u8), - Constant(String), -} diff --git a/evm/src/cpu/kernel/constants/context_metadata.rs b/evm/src/cpu/kernel/constants/context_metadata.rs deleted file mode 100644 index ffcc65387a..0000000000 --- a/evm/src/cpu/kernel/constants/context_metadata.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::memory::segments::Segment; - -/// These metadata fields contain VM state specific to a particular context. -/// -/// Each value is directly scaled by the corresponding `Segment::ContextMetadata` value for faster -/// memory access in the kernel. -#[allow(clippy::enum_clike_unportable_variant)] -#[repr(usize)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] -pub(crate) enum ContextMetadata { - /// The ID of the context which created this one. - ParentContext = Segment::ContextMetadata as usize, - /// The program counter to return to when we return to the parent context. - ParentProgramCounter, - CalldataSize, - ReturndataSize, - /// The address of the account associated with this context. - Address, - /// The size of the code under the account associated with this context. - /// While this information could be obtained from the state trie, it is best to cache it since - /// the `CODESIZE` instruction is very cheap. - CodeSize, - /// The address of the caller who spawned this context. - Caller, - /// The value (in wei) deposited by the caller. - CallValue, - /// Whether this context was created by `STATICCALL`, in which case state changes are - /// prohibited. - Static, - /// Pointer to the initial version of the state trie, at the creation of this context. Used when - /// we need to revert a context. - StateTrieCheckpointPointer, - /// Size of the active main memory, in (32 byte) words. - MemWords, - StackSize, - /// The gas limit for this call (not the entire transaction). - GasLimit, - ContextCheckpointsLen, -} - -impl ContextMetadata { - pub(crate) const COUNT: usize = 14; - - /// Unscales this virtual offset by their respective `Segment` value. - pub(crate) const fn unscale(&self) -> usize { - *self as usize - Segment::ContextMetadata as usize - } - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::ParentContext, - Self::ParentProgramCounter, - Self::CalldataSize, - Self::ReturndataSize, - Self::Address, - Self::CodeSize, - Self::Caller, - Self::CallValue, - Self::Static, - Self::StateTrieCheckpointPointer, - Self::MemWords, - Self::StackSize, - Self::GasLimit, - Self::ContextCheckpointsLen, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - ContextMetadata::ParentContext => "CTX_METADATA_PARENT_CONTEXT", - ContextMetadata::ParentProgramCounter => "CTX_METADATA_PARENT_PC", - ContextMetadata::CalldataSize => "CTX_METADATA_CALLDATA_SIZE", - ContextMetadata::ReturndataSize => "CTX_METADATA_RETURNDATA_SIZE", - ContextMetadata::Address => "CTX_METADATA_ADDRESS", - ContextMetadata::CodeSize => "CTX_METADATA_CODE_SIZE", - ContextMetadata::Caller => "CTX_METADATA_CALLER", - ContextMetadata::CallValue => "CTX_METADATA_CALL_VALUE", - ContextMetadata::Static => "CTX_METADATA_STATIC", - ContextMetadata::StateTrieCheckpointPointer => "CTX_METADATA_STATE_TRIE_CHECKPOINT_PTR", - ContextMetadata::MemWords => "CTX_METADATA_MEM_WORDS", - ContextMetadata::StackSize => "CTX_METADATA_STACK_SIZE", - ContextMetadata::GasLimit => "CTX_METADATA_GAS_LIMIT", - ContextMetadata::ContextCheckpointsLen => "CTX_METADATA_CHECKPOINTS_LEN", - } - } -} diff --git a/evm/src/cpu/kernel/constants/exc_bitfields.rs b/evm/src/cpu/kernel/constants/exc_bitfields.rs deleted file mode 100644 index 59dec8b1e8..0000000000 --- a/evm/src/cpu/kernel/constants/exc_bitfields.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::ops::RangeInclusive; - -use ethereum_types::U256; - -/// Create a U256, where the bits at indices inside the specified ranges are set to 1, and all other -/// bits are set to 0. -const fn u256_from_set_index_ranges(ranges: &[RangeInclusive; N]) -> U256 { - let mut j = 0; - let mut res_limbs = [0u64; 4]; - while j < ranges.len() { - let range = &ranges[j]; - let mut i = *range.start(); - if i > *range.end() { - continue; - } - loop { - let i_lo = i & 0x3f; - let i_hi = i >> 6; - res_limbs[i_hi as usize] |= 1 << i_lo; - - if i >= *range.end() { - break; - } - i += 1; - } - j += 1; - } - U256(res_limbs) -} - -pub(crate) const STACK_LENGTH_INCREASING_OPCODES_USER: U256 = u256_from_set_index_ranges(&[ - 0x30..=0x30, // ADDRESS - 0x32..=0x34, // ORIGIN, CALLER, CALLVALUE - 0x36..=0x36, // CALLDATASIZE - 0x38..=0x38, // CODESIZE - 0x3a..=0x3a, // GASPRICE - 0x3d..=0x3d, // RETURNDATASIZE - 0x41..=0x48, // COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE - 0x58..=0x5a, // PC, MSIZE, GAS - 0x5f..=0x8f, // PUSH*, DUP* -]); - -pub(crate) const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges(&[ - 0x0c..=0x0f, - 0x1e..=0x1f, - 0x21..=0x2f, - 0x49..=0x4f, - 0x5c..=0x5e, - 0xa5..=0xef, - 0xf6..=0xf9, - 0xfb..=0xfc, - 0xfe..=0xfe, -]); diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs deleted file mode 100644 index 0b3f66481e..0000000000 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::memory::segments::Segment; - -/// These metadata fields contain global VM state, stored in the `Segment::Metadata` segment of the -/// kernel's context (which is zero). -/// -/// Each value is directly scaled by the corresponding `Segment::GlobalMetadata` value for faster -/// memory access in the kernel. -#[allow(clippy::enum_clike_unportable_variant)] -#[repr(usize)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] -pub(crate) enum GlobalMetadata { - /// The largest context ID that has been used so far in this execution. Tracking this allows us - /// give each new context a unique ID, so that its memory will be zero-initialized. - LargestContext = Segment::GlobalMetadata as usize, - /// The size of active memory, in bytes. - MemorySize, - /// The size of the `TrieData` segment, in bytes. In other words, the next address available for - /// appending additional trie data. - TrieDataSize, - /// The size of the `TrieData` segment, in bytes, represented as a whole address. - /// In other words, the next address available for appending additional trie data. - RlpDataSize, - /// A pointer to the root of the state trie within the `TrieData` buffer. - StateTrieRoot, - /// A pointer to the root of the transaction trie within the `TrieData` buffer. - TransactionTrieRoot, - /// A pointer to the root of the receipt trie within the `TrieData` buffer. - ReceiptTrieRoot, - - // The root digests of each Merkle trie before these transactions. - StateTrieRootDigestBefore, - TransactionTrieRootDigestBefore, - ReceiptTrieRootDigestBefore, - - // The root digests of each Merkle trie after these transactions. - StateTrieRootDigestAfter, - TransactionTrieRootDigestAfter, - ReceiptTrieRootDigestAfter, - - // Block metadata. - BlockBeneficiary, - BlockTimestamp, - BlockNumber, - BlockDifficulty, - BlockRandom, - BlockGasLimit, - BlockChainId, - BlockBaseFee, - BlockGasUsed, - /// Before current transactions block values. - BlockGasUsedBefore, - /// After current transactions block values. - BlockGasUsedAfter, - /// Current block header hash - BlockCurrentHash, - - /// Gas to refund at the end of the transaction. - RefundCounter, - /// Length of the addresses access list. - AccessedAddressesLen, - /// Length of the storage keys access list. - AccessedStorageKeysLen, - /// Length of the self-destruct list. - SelfDestructListLen, - /// Length of the bloom entry buffer. - BloomEntryLen, - - /// Length of the journal. - JournalLen, - /// Length of the `JournalData` segment. - JournalDataLen, - /// Current checkpoint. - CurrentCheckpoint, - TouchedAddressesLen, - // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost, - // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart, - // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen, - // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation, - IsPrecompileFromEoa, - CallStackDepth, - /// Transaction logs list length - LogsLen, - LogsDataLen, - LogsPayloadLen, - TxnNumberBefore, - TxnNumberAfter, - - KernelHash, - KernelLen, -} - -impl GlobalMetadata { - pub(crate) const COUNT: usize = 47; - - /// Unscales this virtual offset by their respective `Segment` value. - pub(crate) const fn unscale(&self) -> usize { - *self as usize - Segment::GlobalMetadata as usize - } - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::LargestContext, - Self::MemorySize, - Self::TrieDataSize, - Self::RlpDataSize, - Self::StateTrieRoot, - Self::TransactionTrieRoot, - Self::ReceiptTrieRoot, - Self::StateTrieRootDigestBefore, - Self::TransactionTrieRootDigestBefore, - Self::ReceiptTrieRootDigestBefore, - Self::StateTrieRootDigestAfter, - Self::TransactionTrieRootDigestAfter, - Self::ReceiptTrieRootDigestAfter, - Self::BlockBeneficiary, - Self::BlockTimestamp, - Self::BlockNumber, - Self::BlockDifficulty, - Self::BlockRandom, - Self::BlockGasLimit, - Self::BlockChainId, - Self::BlockBaseFee, - Self::BlockGasUsed, - Self::BlockGasUsedBefore, - Self::BlockGasUsedAfter, - Self::RefundCounter, - Self::AccessedAddressesLen, - Self::AccessedStorageKeysLen, - Self::SelfDestructListLen, - Self::BloomEntryLen, - Self::JournalLen, - Self::JournalDataLen, - Self::CurrentCheckpoint, - Self::TouchedAddressesLen, - Self::AccessListDataCost, - Self::AccessListRlpStart, - Self::AccessListRlpLen, - Self::ContractCreation, - Self::IsPrecompileFromEoa, - Self::CallStackDepth, - Self::LogsLen, - Self::LogsDataLen, - Self::LogsPayloadLen, - Self::BlockCurrentHash, - Self::TxnNumberBefore, - Self::TxnNumberAfter, - Self::KernelHash, - Self::KernelLen, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - Self::LargestContext => "GLOBAL_METADATA_LARGEST_CONTEXT", - Self::MemorySize => "GLOBAL_METADATA_MEMORY_SIZE", - Self::TrieDataSize => "GLOBAL_METADATA_TRIE_DATA_SIZE", - Self::RlpDataSize => "GLOBAL_METADATA_RLP_DATA_SIZE", - Self::StateTrieRoot => "GLOBAL_METADATA_STATE_TRIE_ROOT", - Self::TransactionTrieRoot => "GLOBAL_METADATA_TXN_TRIE_ROOT", - Self::ReceiptTrieRoot => "GLOBAL_METADATA_RECEIPT_TRIE_ROOT", - Self::StateTrieRootDigestBefore => "GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE", - Self::TransactionTrieRootDigestBefore => "GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE", - Self::ReceiptTrieRootDigestBefore => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE", - Self::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER", - Self::TransactionTrieRootDigestAfter => "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER", - Self::ReceiptTrieRootDigestAfter => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER", - Self::BlockBeneficiary => "GLOBAL_METADATA_BLOCK_BENEFICIARY", - Self::BlockTimestamp => "GLOBAL_METADATA_BLOCK_TIMESTAMP", - Self::BlockNumber => "GLOBAL_METADATA_BLOCK_NUMBER", - Self::BlockDifficulty => "GLOBAL_METADATA_BLOCK_DIFFICULTY", - Self::BlockRandom => "GLOBAL_METADATA_BLOCK_RANDOM", - Self::BlockGasLimit => "GLOBAL_METADATA_BLOCK_GAS_LIMIT", - Self::BlockChainId => "GLOBAL_METADATA_BLOCK_CHAIN_ID", - Self::BlockBaseFee => "GLOBAL_METADATA_BLOCK_BASE_FEE", - Self::BlockGasUsed => "GLOBAL_METADATA_BLOCK_GAS_USED", - Self::BlockGasUsedBefore => "GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE", - Self::BlockGasUsedAfter => "GLOBAL_METADATA_BLOCK_GAS_USED_AFTER", - Self::BlockCurrentHash => "GLOBAL_METADATA_BLOCK_CURRENT_HASH", - Self::RefundCounter => "GLOBAL_METADATA_REFUND_COUNTER", - Self::AccessedAddressesLen => "GLOBAL_METADATA_ACCESSED_ADDRESSES_LEN", - Self::AccessedStorageKeysLen => "GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN", - Self::SelfDestructListLen => "GLOBAL_METADATA_SELFDESTRUCT_LIST_LEN", - Self::BloomEntryLen => "GLOBAL_METADATA_BLOOM_ENTRY_LEN", - Self::JournalLen => "GLOBAL_METADATA_JOURNAL_LEN", - Self::JournalDataLen => "GLOBAL_METADATA_JOURNAL_DATA_LEN", - Self::CurrentCheckpoint => "GLOBAL_METADATA_CURRENT_CHECKPOINT", - Self::TouchedAddressesLen => "GLOBAL_METADATA_TOUCHED_ADDRESSES_LEN", - Self::AccessListDataCost => "GLOBAL_METADATA_ACCESS_LIST_DATA_COST", - Self::AccessListRlpStart => "GLOBAL_METADATA_ACCESS_LIST_RLP_START", - Self::AccessListRlpLen => "GLOBAL_METADATA_ACCESS_LIST_RLP_LEN", - Self::ContractCreation => "GLOBAL_METADATA_CONTRACT_CREATION", - Self::IsPrecompileFromEoa => "GLOBAL_METADATA_IS_PRECOMPILE_FROM_EOA", - Self::CallStackDepth => "GLOBAL_METADATA_CALL_STACK_DEPTH", - Self::LogsLen => "GLOBAL_METADATA_LOGS_LEN", - Self::LogsDataLen => "GLOBAL_METADATA_LOGS_DATA_LEN", - Self::LogsPayloadLen => "GLOBAL_METADATA_LOGS_PAYLOAD_LEN", - Self::TxnNumberBefore => "GLOBAL_METADATA_TXN_NUMBER_BEFORE", - Self::TxnNumberAfter => "GLOBAL_METADATA_TXN_NUMBER_AFTER", - Self::KernelHash => "GLOBAL_METADATA_KERNEL_HASH", - Self::KernelLen => "GLOBAL_METADATA_KERNEL_LEN", - } - } -} diff --git a/evm/src/cpu/kernel/constants/journal_entry.rs b/evm/src/cpu/kernel/constants/journal_entry.rs deleted file mode 100644 index d84f2ade8f..0000000000 --- a/evm/src/cpu/kernel/constants/journal_entry.rs +++ /dev/null @@ -1,51 +0,0 @@ -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] -pub(crate) enum JournalEntry { - AccountLoaded = 0, - AccountDestroyed = 1, - AccountTouched = 2, - BalanceTransfer = 3, - NonceChange = 4, - StorageChange = 5, - StorageLoaded = 6, - CodeChange = 7, - Refund = 8, - AccountCreated = 9, - Log = 10, -} - -impl JournalEntry { - pub(crate) const COUNT: usize = 11; - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::AccountLoaded, - Self::AccountDestroyed, - Self::AccountTouched, - Self::BalanceTransfer, - Self::NonceChange, - Self::StorageChange, - Self::StorageLoaded, - Self::CodeChange, - Self::Refund, - Self::AccountCreated, - Self::Log, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - Self::AccountLoaded => "JOURNAL_ENTRY_ACCOUNT_LOADED", - Self::AccountDestroyed => "JOURNAL_ENTRY_ACCOUNT_DESTROYED", - Self::AccountTouched => "JOURNAL_ENTRY_ACCOUNT_TOUCHED", - Self::BalanceTransfer => "JOURNAL_ENTRY_BALANCE_TRANSFER", - Self::NonceChange => "JOURNAL_ENTRY_NONCE_CHANGE", - Self::StorageChange => "JOURNAL_ENTRY_STORAGE_CHANGE", - Self::StorageLoaded => "JOURNAL_ENTRY_STORAGE_LOADED", - Self::CodeChange => "JOURNAL_ENTRY_CODE_CHANGE", - Self::Refund => "JOURNAL_ENTRY_REFUND", - Self::AccountCreated => "JOURNAL_ENTRY_ACCOUNT_CREATED", - Self::Log => "JOURNAL_ENTRY_LOG", - } - } -} diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs deleted file mode 100644 index 82c820f054..0000000000 --- a/evm/src/cpu/kernel/constants/mod.rs +++ /dev/null @@ -1,286 +0,0 @@ -use std::collections::HashMap; - -use ethereum_types::U256; -use hex_literal::hex; - -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::constants::journal_entry::JournalEntry; -use crate::cpu::kernel::constants::trie_type::PartialTrieType; -use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; -use crate::memory::segments::Segment; - -pub(crate) mod context_metadata; -mod exc_bitfields; -pub(crate) mod global_metadata; -pub(crate) mod journal_entry; -pub(crate) mod trie_type; -pub(crate) mod txn_fields; - -/// Constants that are accessible to our kernel assembly code. -pub(crate) fn evm_constants() -> HashMap { - let mut c = HashMap::new(); - - let hex_constants = MISC_CONSTANTS - .iter() - .chain(EC_CONSTANTS.iter()) - .chain(HASH_CONSTANTS.iter()) - .cloned(); - for (name, value) in hex_constants { - c.insert(name.into(), U256::from_big_endian(&value)); - } - - for (name, value) in GAS_CONSTANTS { - c.insert(name.into(), U256::from(value)); - } - - for (name, value) in REFUND_CONSTANTS { - c.insert(name.into(), U256::from(value)); - } - - for (name, value) in PRECOMPILES { - c.insert(name.into(), U256::from(value)); - } - - for (name, value) in PRECOMPILES_GAS { - c.insert(name.into(), U256::from(value)); - } - - for (name, value) in CODE_SIZE_LIMIT { - c.insert(name.into(), U256::from(value)); - } - - for (name, value) in SNARKV_POINTERS { - c.insert(name.into(), U256::from(value)); - } - - c.insert(MAX_NONCE.0.into(), U256::from(MAX_NONCE.1)); - c.insert(CALL_STACK_LIMIT.0.into(), U256::from(CALL_STACK_LIMIT.1)); - - for segment in Segment::all() { - c.insert(segment.var_name().into(), (segment as usize).into()); - } - for txn_field in NormalizedTxnField::all() { - // These offsets are already scaled by their respective segment. - c.insert(txn_field.var_name().into(), (txn_field as usize).into()); - } - for txn_field in GlobalMetadata::all() { - // These offsets are already scaled by their respective segment. - c.insert(txn_field.var_name().into(), (txn_field as usize).into()); - } - for txn_field in ContextMetadata::all() { - // These offsets are already scaled by their respective segment. - c.insert(txn_field.var_name().into(), (txn_field as usize).into()); - } - for trie_type in PartialTrieType::all() { - c.insert(trie_type.var_name().into(), (trie_type as u32).into()); - } - for entry in JournalEntry::all() { - c.insert(entry.var_name().into(), (entry as u32).into()); - } - c.insert( - "INVALID_OPCODES_USER".into(), - exc_bitfields::INVALID_OPCODES_USER, - ); - c.insert( - "STACK_LENGTH_INCREASING_OPCODES_USER".into(), - exc_bitfields::STACK_LENGTH_INCREASING_OPCODES_USER, - ); - c -} - -const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ - // Base for limbs used in bignum arithmetic. - ( - "BIGNUM_LIMB_BASE", - hex!("0000000000000000000000000000000100000000000000000000000000000000"), - ), - // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is - // equal to u32::MAX + @SEGMENT_RLP_RAW so that all rlp pointers are much smaller than that. - ( - "ENCODED_EMPTY_NODE_POS", - hex!("0000000000000000000000000000000000000000000000000000000CFFFFFFFF"), - ), - // 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. - ( - "MAX_RLP_BLOB_SIZE", - hex!("0000000000000000000000000000000000000000000000000000000000010000"), - ), -]; - -const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ - // Hash of an empty string: keccak(b'').hex() - ( - "EMPTY_STRING_HASH", - hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), - ), - // Hash of an empty node: keccak(rlp.encode(b'')).hex() - ( - "EMPTY_NODE_HASH", - hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), - ), -]; - -const EC_CONSTANTS: [(&str, [u8; 32]); 20] = [ - ( - "U256_MAX", - hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), - ), - ( - "BN_BASE", - hex!("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"), - ), - ( - "BN_TWISTED_RE", - hex!("2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5"), - ), - ( - "BN_TWISTED_IM", - hex!("009713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2"), - ), - ( - "BN_SCALAR", - hex!("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"), - ), - ( - "BN_GLV_BETA", - hex!("000000000000000059e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe"), - ), - ( - "BN_GLV_S", - hex!("0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd"), - ), - ( - "BN_GLV_MINUS_G1", - hex!("000000000000000000000000000000024ccef014a773d2cf7a7bd9d4391eb18d"), - ), - ( - "BN_GLV_G2", - hex!("000000000000000000000000000000000000000000000002d91d232ec7e0b3d7"), - ), - ( - "BN_GLV_B1", - hex!("30644e72e131a029b85045b68181585cb8e665ff8b011694c1d039a872b0eed9"), - ), - ( - "BN_GLV_B2", - hex!("00000000000000000000000000000000000000000000000089d3256894d213e3"), - ), - ( - "BN_BNEG_LOC", - // This just needs to be large enough to not interfere with anything else in SEGMENT_BN_TABLE_Q. - hex!("0000000000000000000000000000000000000000000000000000000000001337"), - ), - ( - "SECP_BASE", - hex!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"), - ), - ( - "SECP_SCALAR", - hex!("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), - ), - ( - "SECP_GLV_BETA", - hex!("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"), - ), - ( - "SECP_GLV_S", - hex!("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72"), - ), - ( - "SECP_GLV_MINUS_G1", - hex!("00000000000000000000000000000000e4437ed6010e88286f547fa90abfe4c4"), - ), - ( - "SECP_GLV_G2", - hex!("000000000000000000000000000000003086d221a7d46bcde86c90e49284eb15"), - ), - ( - "SECP_GLV_B1", - hex!("fffffffffffffffffffffffffffffffdd66b5e10ae3a1813507ddee3c5765c7e"), - ), - ( - "SECP_GLV_B2", - hex!("000000000000000000000000000000003086d221a7d46bcde86c90e49284eb15"), - ), -]; - -const GAS_CONSTANTS: [(&str, u16); 36] = [ - ("GAS_ZERO", 0), - ("GAS_JUMPDEST", 1), - ("GAS_BASE", 2), - ("GAS_VERYLOW", 3), - ("GAS_LOW", 5), - ("GAS_MID", 8), - ("GAS_HIGH", 10), - ("GAS_WARMACCESS", 100), - ("GAS_ACCESSLISTADDRESS", 2_400), - ("GAS_ACCESSLISTSTORAGE", 1_900), - ("GAS_COLDACCOUNTACCESS", 2_600), - ("GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS", 2_500), - ("GAS_COLDSLOAD", 2_100), - ("GAS_COLDSLOAD_MINUS_WARMACCESS", 2_000), - ("GAS_SSET", 20_000), - ("GAS_SRESET", 2_900), - ("GAS_SELFDESTRUCT", 5_000), - ("GAS_CREATE", 32_000), - ("GAS_CODEDEPOSIT", 200), - ("GAS_CALLVALUE", 9_000), - ("GAS_CALLSTIPEND", 2_300), - ("GAS_NEWACCOUNT", 25_000), - ("GAS_EXP", 10), - ("GAS_EXPBYTE", 50), - ("GAS_MEMORY", 3), - ("GAS_TXCREATE", 32_000), - ("GAS_TXDATAZERO", 4), - ("GAS_TXDATANONZERO", 16), - ("GAS_TRANSACTION", 21_000), - ("GAS_LOG", 375), - ("GAS_LOGDATA", 8), - ("GAS_LOGTOPIC", 375), - ("GAS_KECCAK256", 30), - ("GAS_KECCAK256WORD", 6), - ("GAS_COPY", 3), - ("GAS_BLOCKHASH", 20), -]; - -const REFUND_CONSTANTS: [(&str, u16); 2] = [("REFUND_SCLEAR", 4_800), ("MAX_REFUND_QUOTIENT", 5)]; - -const PRECOMPILES: [(&str, u16); 9] = [ - ("ECREC", 1), - ("SHA256", 2), - ("RIP160", 3), - ("ID", 4), - ("EXPMOD", 5), - ("BN_ADD", 6), - ("BN_MUL", 7), - ("SNARKV", 8), - ("BLAKE2_F", 9), -]; - -const PRECOMPILES_GAS: [(&str, u16); 13] = [ - ("ECREC_GAS", 3_000), - ("SHA256_STATIC_GAS", 60), - ("SHA256_DYNAMIC_GAS", 12), - ("RIP160_STATIC_GAS", 600), - ("RIP160_DYNAMIC_GAS", 120), - ("ID_STATIC_GAS", 15), - ("ID_DYNAMIC_GAS", 3), - ("EXPMOD_MIN_GAS", 200), - ("BN_ADD_GAS", 150), - ("BN_MUL_GAS", 6_000), - ("SNARKV_STATIC_GAS", 45_000), - ("SNARKV_DYNAMIC_GAS", 34_000), - ("BLAKE2_F__GAS", 1), -]; - -const SNARKV_POINTERS: [(&str, u64); 2] = [("SNARKV_INP", 112), ("SNARKV_OUT", 100)]; - -const CODE_SIZE_LIMIT: [(&str, u64); 3] = [ - ("MAX_CODE_SIZE", 0x6000), - ("MAX_INITCODE_SIZE", 0xc000), - ("INITCODE_WORD_COST", 2), -]; - -const MAX_NONCE: (&str, u64) = ("MAX_NONCE", 0xffffffffffffffff); -const CALL_STACK_LIMIT: (&str, u64) = ("CALL_STACK_LIMIT", 1024); diff --git a/evm/src/cpu/kernel/constants/trie_type.rs b/evm/src/cpu/kernel/constants/trie_type.rs deleted file mode 100644 index fd89f41000..0000000000 --- a/evm/src/cpu/kernel/constants/trie_type.rs +++ /dev/null @@ -1,49 +0,0 @@ -use core::ops::Deref; - -use eth_trie_utils::partial_trie::HashedPartialTrie; - -use crate::Node; - -#[derive(Copy, Clone, Debug)] -pub(crate) enum PartialTrieType { - Empty = 0, - Hash = 1, - Branch = 2, - Extension = 3, - Leaf = 4, -} - -impl PartialTrieType { - pub(crate) const COUNT: usize = 5; - - pub(crate) fn of(trie: &HashedPartialTrie) -> Self { - match trie.deref() { - Node::Empty => Self::Empty, - Node::Hash(_) => Self::Hash, - Node::Branch { .. } => Self::Branch, - Node::Extension { .. } => Self::Extension, - Node::Leaf { .. } => Self::Leaf, - } - } - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::Empty, - Self::Hash, - Self::Branch, - Self::Extension, - Self::Leaf, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - Self::Empty => "MPT_NODE_EMPTY", - Self::Hash => "MPT_NODE_HASH", - Self::Branch => "MPT_NODE_BRANCH", - Self::Extension => "MPT_NODE_EXTENSION", - Self::Leaf => "MPT_NODE_LEAF", - } - } -} diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs deleted file mode 100644 index 0b74409b37..0000000000 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::memory::segments::Segment; - -/// These are normalized transaction fields, i.e. not specific to any transaction type. -/// -/// Each value is directly scaled by the corresponding `Segment::TxnFields` value for faster -/// memory access in the kernel. -#[allow(dead_code)] -#[allow(clippy::enum_clike_unportable_variant)] -#[repr(usize)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] -pub(crate) enum NormalizedTxnField { - /// Whether a chain ID was present in the txn data. Type 0 transaction with v=27 or v=28 have - /// no chain ID. This affects what fields get signed. - ChainIdPresent = Segment::TxnFields as usize, - ChainId, - Nonce, - MaxPriorityFeePerGas, - MaxFeePerGas, - GasLimit, - IntrinsicGas, - To, - Value, - /// The length of the data field. The data itself is stored in another segment. - DataLen, - YParity, - R, - S, - Origin, - - /// The actual computed gas price for this transaction in the block. - /// This is not technically a transaction field, as it depends on the block's base fee. - ComputedFeePerGas, - ComputedPriorityFeePerGas, -} - -impl NormalizedTxnField { - pub(crate) const COUNT: usize = 16; - - /// Unscales this virtual offset by their respective `Segment` value. - pub(crate) const fn unscale(&self) -> usize { - *self as usize - Segment::TxnFields as usize - } - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::ChainIdPresent, - Self::ChainId, - Self::Nonce, - Self::MaxPriorityFeePerGas, - Self::MaxFeePerGas, - Self::GasLimit, - Self::IntrinsicGas, - Self::To, - Self::Value, - Self::DataLen, - Self::YParity, - Self::R, - Self::S, - Self::Origin, - Self::ComputedFeePerGas, - Self::ComputedPriorityFeePerGas, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - NormalizedTxnField::ChainIdPresent => "TXN_FIELD_CHAIN_ID_PRESENT", - NormalizedTxnField::ChainId => "TXN_FIELD_CHAIN_ID", - NormalizedTxnField::Nonce => "TXN_FIELD_NONCE", - NormalizedTxnField::MaxPriorityFeePerGas => "TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS", - NormalizedTxnField::MaxFeePerGas => "TXN_FIELD_MAX_FEE_PER_GAS", - NormalizedTxnField::GasLimit => "TXN_FIELD_GAS_LIMIT", - NormalizedTxnField::IntrinsicGas => "TXN_FIELD_INTRINSIC_GAS", - NormalizedTxnField::To => "TXN_FIELD_TO", - NormalizedTxnField::Value => "TXN_FIELD_VALUE", - NormalizedTxnField::DataLen => "TXN_FIELD_DATA_LEN", - NormalizedTxnField::YParity => "TXN_FIELD_Y_PARITY", - NormalizedTxnField::R => "TXN_FIELD_R", - NormalizedTxnField::S => "TXN_FIELD_S", - NormalizedTxnField::Origin => "TXN_FIELD_ORIGIN", - NormalizedTxnField::ComputedFeePerGas => "TXN_FIELD_COMPUTED_FEE_PER_GAS", - NormalizedTxnField::ComputedPriorityFeePerGas => { - "TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS" - } - } - } -} diff --git a/evm/src/cpu/kernel/cost_estimator.rs b/evm/src/cpu/kernel/cost_estimator.rs deleted file mode 100644 index 70cc726772..0000000000 --- a/evm/src/cpu/kernel/cost_estimator.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::cpu::kernel::assembler::BYTES_PER_OFFSET; -use crate::cpu::kernel::ast::Item; -use crate::cpu::kernel::ast::Item::*; -use crate::cpu::kernel::ast::PushTarget::*; -use crate::cpu::kernel::utils::u256_to_trimmed_be_bytes; - -pub(crate) fn is_code_improved(before: &[Item], after: &[Item]) -> bool { - cost_estimate(after) < cost_estimate(before) -} - -fn cost_estimate(code: &[Item]) -> u32 { - code.iter().map(cost_estimate_item).sum() -} - -fn cost_estimate_item(item: &Item) -> u32 { - match item { - MacroDef(_, _, _) => 0, - GlobalLabelDeclaration(_) => 0, - LocalLabelDeclaration(_) => 0, - Push(Literal(n)) => cost_estimate_push(u256_to_trimmed_be_bytes(n).len()), - Push(Label(_)) => cost_estimate_push(BYTES_PER_OFFSET as usize), - ProverInput(_) => 1, - StandardOp(op) => cost_estimate_standard_op(op.as_str()), - _ => panic!("Unexpected item: {item:?}"), - } -} - -const fn cost_estimate_standard_op(_op: &str) -> u32 { - // For now we just treat any standard operation as having the same cost. This is pretty naive, - // but should work fine with our current set of simple optimization rules. - 1 -} - -const fn cost_estimate_push(num_bytes: usize) -> u32 { - num_bytes as u32 -} diff --git a/evm/src/cpu/kernel/evm_asm.pest b/evm/src/cpu/kernel/evm_asm.pest deleted file mode 100644 index 40dec03b3e..0000000000 --- a/evm/src/cpu/kernel/evm_asm.pest +++ /dev/null @@ -1,47 +0,0 @@ -// Grammar for our EVM assembly code. -// Loosely based on https://gist.github.com/axic/17ddbbce4738ccf4040d30cbb5de484e - -WHITESPACE = _{ " " | "\t" | NEWLINE } -COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE } - -identifier_first_char = _{ ASCII_ALPHA | "_" } -identifier_char = _{ ASCII_ALPHANUMERIC | "_" } -identifier = @{ identifier_first_char ~ identifier_char* } - -literal_decimal = @{ ASCII_DIGIT+ } -literal_hex = @{ ^"0x" ~ ASCII_HEX_DIGIT+ } -literal = { literal_hex | literal_decimal } - -variable = ${ "$" ~ identifier } -constant = ${ "@" ~ identifier } - -item = { macro_def | macro_call | repeat | stack | global_label_decl | local_label_decl | macro_label_decl | bytes_item | jumptable_item | push_instruction | prover_input_instruction | nullary_instruction } -macro_def = { ^"%macro" ~ identifier ~ paramlist? ~ item* ~ ^"%endmacro" } -macro_call = ${ "%" ~ !((^"macro" | ^"endmacro" | ^"rep" | ^"endrep" | ^"stack") ~ !identifier_char) ~ identifier ~ macro_arglist? } -repeat = { ^"%rep" ~ literal ~ item* ~ ^"%endrep" } -paramlist = { "(" ~ identifier ~ ("," ~ identifier)* ~ ")" } -macro_arglist = !{ "(" ~ push_target ~ ("," ~ push_target)* ~ ")" } - -stack = { ^"%stack" ~ stack_placeholders ~ "->" ~ stack_replacements } -stack_placeholders = { "(" ~ (stack_placeholder ~ ("," ~ stack_placeholder)*)? ~ ")" } -stack_placeholder = { stack_block | identifier } -stack_block = { identifier ~ ":" ~ literal_decimal } -stack_replacements = { "(" ~ (stack_replacement ~ ("," ~ stack_replacement)*)? ~ ")" } -stack_replacement = { literal | identifier | constant | macro_label | variable } - -global_label_decl = ${ ^"GLOBAL " ~ identifier ~ ":" } -local_label_decl = ${ identifier ~ ":" } -macro_label_decl = ${ "%%" ~ identifier ~ ":" } -macro_label = ${ "%%" ~ identifier } - -bytes_item = { ^"BYTES " ~ bytes_target ~ ("," ~ bytes_target)* } -bytes_target = { literal | constant } -jumptable_item = { ^"JUMPTABLE " ~ identifier ~ ("," ~ identifier)* } -push_instruction = { ^"PUSH " ~ push_target } -push_target = { literal | identifier | macro_label | variable | constant } -prover_input_instruction = { ^"PROVER_INPUT" ~ "(" ~ prover_input_fn ~ ")" } -prover_input_fn = { identifier ~ ("::" ~ identifier)*} -nullary_instruction = { identifier } - -file = { SOI ~ item* ~ silent_eoi } -silent_eoi = _{ !ANY } diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs deleted file mode 100644 index a8937cf2e2..0000000000 --- a/evm/src/cpu/kernel/interpreter.rs +++ /dev/null @@ -1,1806 +0,0 @@ -//! An EVM interpreter for testing and debugging purposes. - -use core::cmp::Ordering; -use core::ops::Range; -use std::collections::{BTreeSet, HashMap}; - -use anyhow::{anyhow, bail}; -use eth_trie_utils::partial_trie::PartialTrie; -use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::field::types::Field; - -use super::assembler::BYTES_PER_OFFSET; -use super::utils::u256_from_bool; -use crate::cpu::halt; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; -use crate::cpu::stack::MAX_USER_STACK_SIZE; -use crate::extension_tower::BN_BASE; -use crate::generation::mpt::load_all_mpts; -use crate::generation::prover_input::ProverInputFn; -use crate::generation::rlp::all_rlp_prover_inputs_reversed; -use crate::generation::state::{all_withdrawals_prover_inputs_reversed, GenerationState}; -use crate::generation::GenerationInputs; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::util::{h2u, u256_to_u8, u256_to_usize}; -use crate::witness::errors::{ProgramError, ProverInputError}; -use crate::witness::gas::gas_to_charge; -use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; -use crate::witness::operation::{Operation, CONTEXT_SCALING_FACTOR}; -use crate::witness::state::RegistersState; -use crate::witness::transition::decode; -use crate::witness::util::stack_peek; - -/// Halt interpreter execution whenever a jump to this offset is done. -const DEFAULT_HALT_OFFSET: usize = 0xdeadbeef; - -impl MemoryState { - pub(crate) fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { - self.get(MemoryAddress::new(context, segment, offset)) - } - - fn mstore_general( - &mut self, - context: usize, - segment: Segment, - offset: usize, - value: U256, - ) -> InterpreterMemOpKind { - let old_value = self.mload_general(context, segment, offset); - self.set(MemoryAddress::new(context, segment, offset), value); - InterpreterMemOpKind::Write(old_value, context, segment as usize, offset) - } -} - -pub(crate) struct Interpreter<'a, F: Field> { - pub(crate) generation_state: GenerationState, - prover_inputs_map: &'a HashMap, - pub(crate) halt_offsets: Vec, - // The interpreter will halt only if the current context matches halt_context - halt_context: Option, - pub(crate) debug_offsets: Vec, - running: bool, - opcode_count: [usize; 0x100], - memops: Vec, - jumpdest_table: HashMap>, -} - -/// Structure storing the state of the interpreter's registers. -struct InterpreterRegistersState { - kernel_mode: bool, - context: usize, - registers: RegistersState, -} - -/// Interpreter state at the last checkpoint: we only need to store -/// the state of the registers and the length of the vector of memory operations. -/// This data is enough to revert in case of an exception. -struct InterpreterCheckpoint { - registers: InterpreterRegistersState, - mem_len: usize, -} - -pub(crate) fn run_interpreter( - initial_offset: usize, - initial_stack: Vec, -) -> anyhow::Result> { - run( - &KERNEL.code, - initial_offset, - initial_stack, - &KERNEL.prover_inputs, - ) -} - -#[derive(Clone)] -pub(crate) struct InterpreterMemoryInitialization { - pub label: String, - pub stack: Vec, - pub segment: Segment, - pub memory: Vec<(usize, Vec)>, -} - -pub(crate) fn run_interpreter_with_memory( - memory_init: InterpreterMemoryInitialization, -) -> anyhow::Result> { - let label = KERNEL.global_labels[&memory_init.label]; - let mut stack = memory_init.stack; - stack.reverse(); - let mut interpreter = Interpreter::new_with_kernel(label, stack); - for (pointer, data) in memory_init.memory { - for (i, term) in data.iter().enumerate() { - interpreter.generation_state.memory.set( - MemoryAddress::new(0, memory_init.segment, pointer + i), - *term, - ) - } - } - interpreter.run()?; - Ok(interpreter) -} - -pub(crate) fn run<'a, F: Field>( - code: &'a [u8], - initial_offset: usize, - initial_stack: Vec, - prover_inputs: &'a HashMap, -) -> anyhow::Result> { - let mut interpreter = Interpreter::new(code, initial_offset, initial_stack, prover_inputs); - interpreter.run()?; - Ok(interpreter) -} - -/// Simulates the CPU execution from `state` until the program counter reaches `final_label` -/// in the current context. -pub(crate) fn simulate_cpu_and_get_user_jumps( - final_label: &str, - state: &GenerationState, -) -> Option>> { - match state.jumpdest_table { - Some(_) => None, - None => { - let halt_pc = KERNEL.global_labels[final_label]; - let initial_context = state.registers.context; - let mut interpreter = - Interpreter::new_with_state_and_halt_condition(state, halt_pc, initial_context); - - log::debug!("Simulating CPU for jumpdest analysis."); - - interpreter.run(); - - log::debug!("jdt = {:?}", interpreter.jumpdest_table); - - interpreter - .generation_state - .set_jumpdest_analysis_inputs(interpreter.jumpdest_table); - - log::debug!("Simulated CPU for jumpdest analysis halted."); - interpreter.generation_state.jumpdest_table - } - } -} - -/// Different types of Memory operations in the interpreter, and the data required to revert them. -enum InterpreterMemOpKind { - /// We need to provide the context. - Push(usize), - /// If we pop a certain value, we need to push it back to the correct context when reverting. - Pop(U256, usize), - /// If we write a value at a certain address, we need to write the old value back when reverting. - Write(U256, usize, usize, usize), -} - -impl<'a, F: Field> Interpreter<'a, F> { - pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { - let mut result = Self::new( - &KERNEL.code, - initial_offset, - initial_stack, - &KERNEL.prover_inputs, - ); - result.initialize_rlp_segment(); - result - } - - /// Returns an instance of `Interpreter` given `GenerationInputs`, and assuming we are - /// initializing with the `KERNEL` code. - pub(crate) fn new_with_generation_inputs_and_kernel( - initial_offset: usize, - initial_stack: Vec, - inputs: GenerationInputs, - ) -> Self { - let mut result = Self::new_with_kernel(initial_offset, initial_stack); - result.initialize_interpreter_state_with_kernel(inputs); - result - } - - pub(crate) fn new( - code: &'a [u8], - initial_offset: usize, - initial_stack: Vec, - prover_inputs: &'a HashMap, - ) -> Self { - let mut result = Self { - generation_state: GenerationState::new(GenerationInputs::default(), code) - .expect("Default inputs are known-good"), - prover_inputs_map: prover_inputs, - // `DEFAULT_HALT_OFFSET` is used as a halting point for the interpreter, - // while the label `halt` is the halting label in the kernel. - halt_offsets: vec![DEFAULT_HALT_OFFSET, KERNEL.global_labels["halt"]], - halt_context: None, - debug_offsets: vec![], - running: false, - opcode_count: [0; 256], - memops: vec![], - jumpdest_table: HashMap::new(), - }; - result.generation_state.registers.program_counter = initial_offset; - let initial_stack_len = initial_stack.len(); - result.generation_state.registers.stack_len = initial_stack_len; - if !initial_stack.is_empty() { - result.generation_state.registers.stack_top = initial_stack[initial_stack_len - 1]; - *result.stack_segment_mut() = initial_stack; - result.stack_segment_mut().truncate(initial_stack_len - 1); - } - - result - } - - pub(crate) fn new_with_state_and_halt_condition( - state: &GenerationState, - halt_offset: usize, - halt_context: usize, - ) -> Self { - Self { - generation_state: state.soft_clone(), - prover_inputs_map: &KERNEL.prover_inputs, - halt_offsets: vec![halt_offset], - halt_context: Some(halt_context), - debug_offsets: vec![], - running: false, - opcode_count: [0; 256], - memops: vec![], - jumpdest_table: HashMap::new(), - } - } - - /// Initializes the interpreter state given `GenerationInputs`, using the KERNEL code. - pub(crate) fn initialize_interpreter_state_with_kernel(&mut self, inputs: GenerationInputs) { - self.initialize_interpreter_state(inputs, KERNEL.code_hash, KERNEL.code.len()); - } - - /// Initializes the interpreter state given `GenerationInputs`. - pub(crate) fn initialize_interpreter_state( - &mut self, - inputs: GenerationInputs, - kernel_hash: H256, - kernel_code_len: usize, - ) { - let tries = &inputs.tries; - - // Set state's inputs. - self.generation_state.inputs = inputs.clone(); - - // Initialize the MPT's pointers. - let (trie_root_ptrs, trie_data) = - load_all_mpts(tries).expect("Invalid MPT data for preinitialization"); - let trie_roots_after = &inputs.trie_roots_after; - self.generation_state.trie_root_ptrs = trie_root_ptrs; - - // Initialize the `TrieData` segment. - for (i, data) in trie_data.iter().enumerate() { - let trie_addr = MemoryAddress::new(0, Segment::TrieData, i); - self.generation_state.memory.set(trie_addr, data.into()); - } - - // Update the RLP and withdrawal prover inputs. - let rlp_prover_inputs = - all_rlp_prover_inputs_reversed(inputs.clone().signed_txn.as_ref().unwrap_or(&vec![])); - let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); - self.generation_state.rlp_prover_inputs = rlp_prover_inputs; - self.generation_state.withdrawal_prover_inputs = withdrawal_prover_inputs; - - // Set `GlobalMetadata` values. - let metadata = &inputs.block_metadata; - let global_metadata_to_set = [ - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian(&metadata.block_beneficiary.0), - ), - (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), - (GlobalMetadata::BlockNumber, metadata.block_number), - (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), - ( - GlobalMetadata::BlockRandom, - metadata.block_random.into_uint(), - ), - (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), - (GlobalMetadata::BlockChainId, metadata.block_chain_id), - (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), - ( - GlobalMetadata::BlockCurrentHash, - h2u(inputs.block_hashes.cur_hash), - ), - (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), - (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), - (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), - (GlobalMetadata::TxnNumberBefore, inputs.txn_number_before), - ( - GlobalMetadata::TxnNumberAfter, - inputs.txn_number_before + if inputs.signed_txn.is_some() { 1 } else { 0 }, - ), - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(tries.state_trie.hash()), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(tries.transactions_trie.hash()), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(tries.receipts_trie.hash()), - ), - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - (GlobalMetadata::KernelHash, h2u(kernel_hash)), - (GlobalMetadata::KernelLen, kernel_code_len.into()), - ]; - - self.set_global_metadata_multi_fields(&global_metadata_to_set); - - // Set final block bloom values. - let final_block_bloom_fields = (0..8) - .map(|i| { - ( - MemoryAddress::new_u256s( - U256::zero(), - (Segment::GlobalBlockBloom.unscale()).into(), - i.into(), - ) - .expect("This cannot panic as `virt` fits in a `u32`"), - metadata.block_bloom[i], - ) - }) - .collect::>(); - - self.set_memory_multi_addresses(&final_block_bloom_fields); - - // Set previous block hash. - let block_hashes_fields = (0..256) - .map(|i| { - ( - MemoryAddress::new_u256s( - U256::zero(), - (Segment::BlockHashes.unscale()).into(), - i.into(), - ) - .expect("This cannot panic as `virt` fits in a `u32`"), - h2u(inputs.block_hashes.prev_hashes[i]), - ) - }) - .collect::>(); - - self.set_memory_multi_addresses(&block_hashes_fields); - } - - fn checkpoint(&self) -> InterpreterCheckpoint { - let registers = InterpreterRegistersState { - kernel_mode: self.is_kernel(), - context: self.context(), - registers: self.generation_state.registers, - }; - InterpreterCheckpoint { - registers, - mem_len: self.memops.len(), - } - } - - fn roll_memory_back(&mut self, len: usize) -> Result<(), ProgramError> { - // We roll the memory back until `memops` reaches length `len`. - debug_assert!(self.memops.len() >= len); - while self.memops.len() > len { - if let Some(op) = self.memops.pop() { - match op { - InterpreterMemOpKind::Push(context) => { - self.generation_state.memory.contexts[context].segments - [Segment::Stack.unscale()] - .content - .pop() - .ok_or(ProgramError::StackUnderflow)?; - } - InterpreterMemOpKind::Pop(value, context) => { - self.generation_state.memory.contexts[context].segments - [Segment::Stack.unscale()] - .content - .push(value); - } - InterpreterMemOpKind::Write(value, context, segment, offset) => { - self.generation_state.memory.contexts[context].segments - [segment >> SEGMENT_SCALING_FACTOR] // we need to unscale the segment value - .content[offset] = value; - } - } - } - } - - Ok(()) - } - - fn rollback(&mut self, checkpoint: InterpreterCheckpoint) -> anyhow::Result<()> { - let InterpreterRegistersState { - kernel_mode, - context, - registers, - } = checkpoint.registers; - self.set_is_kernel(kernel_mode); - self.set_context(context); - self.generation_state.registers = registers; - self.roll_memory_back(checkpoint.mem_len) - .map_err(|_| anyhow!("Memory rollback failed unexpectedly.")) - } - - fn handle_error(&mut self, err: ProgramError) -> anyhow::Result<()> { - let exc_code: u8 = match err { - ProgramError::OutOfGas => 0, - ProgramError::InvalidOpcode => 1, - ProgramError::StackUnderflow => 2, - ProgramError::InvalidJumpDestination => 3, - ProgramError::InvalidJumpiDestination => 4, - ProgramError::StackOverflow => 5, - _ => bail!("TODO: figure out what to do with this..."), - }; - - self.run_exception(exc_code) - .map_err(|_| anyhow::Error::msg("error handling errored...")) - } - - pub(crate) fn run(&mut self) -> anyhow::Result<()> { - self.running = true; - while self.running { - let pc = self.generation_state.registers.program_counter; - - if let Some(halt_context) = self.halt_context { - if self.is_kernel() - && self.halt_offsets.contains(&pc) - && halt_context == self.generation_state.registers.context - { - self.running = false; - return Ok(()); - } - } else if self.halt_offsets.contains(&pc) { - return Ok(()); - } - - let checkpoint = self.checkpoint(); - let result = self.run_opcode(); - match result { - Ok(()) => Ok(()), - Err(e) => { - if self.is_kernel() { - let offset_name = - KERNEL.offset_name(self.generation_state.registers.program_counter); - bail!( - "{:?} in kernel at pc={}, stack={:?}, memory={:?}", - e, - offset_name, - self.stack(), - self.generation_state.memory.contexts[0].segments - [Segment::KernelGeneral.unscale()] - .content, - ); - } - self.rollback(checkpoint)?; - self.handle_error(e) - } - }?; - } - #[cfg(debug_assertions)] - { - println!("Opcode count:"); - for i in 0..0x100 { - if self.opcode_count[i] > 0 { - println!("{}: {}", get_mnemonic(i as u8), self.opcode_count[i]) - } - } - println!("Total: {}", self.opcode_count.into_iter().sum::()); - } - Ok(()) - } - - fn code(&self) -> &MemorySegmentState { - // The context is 0 if we are in kernel mode. - &self.generation_state.memory.contexts[(1 - self.is_kernel() as usize) * self.context()] - .segments[Segment::Code.unscale()] - } - - fn code_slice(&self, n: usize) -> Vec { - let pc = self.generation_state.registers.program_counter; - self.code().content[pc..pc + n] - .iter() - .map(|u256| u256.byte(0)) - .collect::>() - } - - pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { - // These fields are already scaled by their respective segment. - self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] - .get(field.unscale()) - } - - pub(crate) fn set_txn_field(&mut self, field: NormalizedTxnField, value: U256) { - // These fields are already scaled by their respective segment. - self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] - .set(field.unscale(), value); - } - - pub(crate) fn get_txn_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TxnData.unscale()].content - } - - pub(crate) fn get_context_metadata_field(&self, ctx: usize, field: ContextMetadata) -> U256 { - // These fields are already scaled by their respective segment. - self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] - .get(field.unscale()) - } - - pub(crate) fn set_context_metadata_field( - &mut self, - ctx: usize, - field: ContextMetadata, - value: U256, - ) { - // These fields are already scaled by their respective segment. - self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] - .set(field.unscale(), value) - } - - pub(crate) fn get_global_metadata_field(&self, field: GlobalMetadata) -> U256 { - // These fields are already scaled by their respective segment. - let field = field.unscale(); - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] - .get(field) - } - - pub(crate) fn set_global_metadata_field(&mut self, field: GlobalMetadata, value: U256) { - // These fields are already scaled by their respective segment. - let field = field.unscale(); - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] - .set(field, value) - } - - pub(crate) fn set_global_metadata_multi_fields(&mut self, metadata: &[(GlobalMetadata, U256)]) { - for &(field, value) in metadata { - let field = field.unscale(); - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] - .set(field, value); - } - } - - pub(crate) fn get_trie_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content - } - - pub(crate) fn get_trie_data_mut(&mut self) -> &mut Vec { - &mut self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content - } - - pub(crate) fn get_memory_segment(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment.unscale()] - .content - .clone() - } - - pub(crate) fn get_memory_segment_bytes(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment.unscale()] - .content - .iter() - .map(|x| x.low_u32() as u8) - .collect() - } - - pub(crate) fn get_current_general_memory(&self) -> Vec { - self.generation_state.memory.contexts[self.context()].segments - [Segment::KernelGeneral.unscale()] - .content - .clone() - } - - pub(crate) fn get_kernel_general_memory(&self) -> Vec { - self.get_memory_segment(Segment::KernelGeneral) - } - - pub(crate) fn get_rlp_memory(&self) -> Vec { - self.get_memory_segment_bytes(Segment::RlpRaw) - } - - pub(crate) fn set_current_general_memory(&mut self, memory: Vec) { - let context = self.context(); - self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral.unscale()] - .content = memory; - } - - pub(crate) fn set_memory_segment(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment.unscale()].content = memory; - } - - pub(crate) fn set_memory_segment_bytes(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment.unscale()].content = - memory.into_iter().map(U256::from).collect(); - } - - pub(crate) fn set_rlp_memory(&mut self, rlp: Vec) { - self.set_memory_segment_bytes(Segment::RlpRaw, rlp) - } - - pub(crate) fn set_code(&mut self, context: usize, code: Vec) { - assert_ne!(context, 0, "Can't modify kernel code."); - while self.generation_state.memory.contexts.len() <= context { - self.generation_state - .memory - .contexts - .push(MemoryContextState::default()); - } - self.generation_state.memory.set( - MemoryAddress::new( - context, - Segment::ContextMetadata, - ContextMetadata::CodeSize.unscale(), - ), - code.len().into(), - ); - self.generation_state.memory.contexts[context].segments[Segment::Code.unscale()].content = - code.into_iter().map(U256::from).collect(); - } - - pub(crate) fn set_memory_multi_addresses(&mut self, addrs: &[(MemoryAddress, U256)]) { - for &(addr, val) in addrs { - self.generation_state.memory.set(addr, val); - } - } - - pub(crate) fn set_jumpdest_analysis_inputs(&mut self, jumps: HashMap>) { - self.generation_state.set_jumpdest_analysis_inputs(jumps); - } - - pub(crate) fn incr(&mut self, n: usize) { - self.generation_state.registers.program_counter += n; - } - - pub(crate) fn stack(&self) -> Vec { - match self.stack_len().cmp(&1) { - Ordering::Greater => { - let mut stack = self.generation_state.memory.contexts[self.context()].segments - [Segment::Stack.unscale()] - .content - .clone(); - stack.truncate(self.stack_len() - 1); - stack.push( - self.stack_top() - .expect("The stack is checked to be nonempty"), - ); - stack - } - Ordering::Equal => { - vec![self - .stack_top() - .expect("The stack is checked to be nonempty")] - } - Ordering::Less => { - vec![] - } - } - } - fn stack_segment_mut(&mut self) -> &mut Vec { - let context = self.context(); - &mut self.generation_state.memory.contexts[context].segments[Segment::Stack.unscale()] - .content - } - - pub(crate) fn extract_kernel_memory(self, segment: Segment, range: Range) -> Vec { - let mut output: Vec = Vec::with_capacity(range.end); - for i in range { - let term = self - .generation_state - .memory - .get(MemoryAddress::new(0, segment, i)); - output.push(term); - } - output - } - - pub(crate) fn push(&mut self, x: U256) -> Result<(), ProgramError> { - if !self.is_kernel() && self.stack_len() >= MAX_USER_STACK_SIZE { - return Err(ProgramError::StackOverflow); - } - if self.stack_len() > 0 { - let top = self - .stack_top() - .expect("The stack is checked to be nonempty"); - let cur_len = self.stack_len(); - let stack_addr = MemoryAddress::new(self.context(), Segment::Stack, cur_len - 1); - self.generation_state.memory.set(stack_addr, top); - } - self.generation_state.registers.stack_top = x; - self.generation_state.registers.stack_len += 1; - self.memops.push(InterpreterMemOpKind::Push(self.context())); - - Ok(()) - } - - fn push_bool(&mut self, x: bool) -> Result<(), ProgramError> { - self.push(if x { U256::one() } else { U256::zero() }) - } - - pub(crate) fn pop(&mut self) -> Result { - let result = stack_peek(&self.generation_state, 0); - - if let Ok(val) = result { - self.memops - .push(InterpreterMemOpKind::Pop(val, self.context())); - } - if self.stack_len() > 1 { - let top = stack_peek(&self.generation_state, 1)?; - self.generation_state.registers.stack_top = top; - } - self.generation_state.registers.stack_len -= 1; - - result - } - - fn run_opcode(&mut self) -> Result<(), ProgramError> { - // Jumpdest analysis is performed natively by the interpreter and not - // using the non-deterministic Kernel assembly code. - if self.is_kernel() - && self.generation_state.registers.program_counter - == KERNEL.global_labels["jumpdest_analysis"] - { - self.generation_state.registers.program_counter = - KERNEL.global_labels["jumpdest_analysis_end"]; - self.generation_state - .set_jumpdest_bits(&self.generation_state.get_current_code()?); - } - - let opcode = self - .code() - .get(self.generation_state.registers.program_counter) - .byte(0); - self.opcode_count[opcode as usize] += 1; - self.incr(1); - - let op = decode(self.generation_state.registers, opcode)?; - self.generation_state.registers.gas_used += gas_to_charge(op); - - #[cfg(debug_assertions)] - if !self.is_kernel() { - println!( - "User instruction {:?}, stack = {:?}, ctx = {}", - op, - { - let mut stack = self.stack(); - stack.reverse(); - stack - }, - self.generation_state.registers.context - ); - } - - match opcode { - 0x00 => self.run_syscall(opcode, 0, false), // "STOP", - 0x01 => self.run_add(), // "ADD", - 0x02 => self.run_mul(), // "MUL", - 0x03 => self.run_sub(), // "SUB", - 0x04 => self.run_div(), // "DIV", - 0x05 => self.run_syscall(opcode, 2, false), // "SDIV", - 0x06 => self.run_mod(), // "MOD", - 0x07 => self.run_syscall(opcode, 2, false), // "SMOD", - 0x08 => self.run_addmod(), // "ADDMOD", - 0x09 => self.run_mulmod(), // "MULMOD", - 0x0a => self.run_syscall(opcode, 2, false), // "EXP", - 0x0b => self.run_syscall(opcode, 2, false), // "SIGNEXTEND", - 0x0c => self.run_addfp254(), // "ADDFP254", - 0x0d => self.run_mulfp254(), // "MULFP254", - 0x0e => self.run_subfp254(), // "SUBFP254", - 0x0f => self.run_submod(), // "SUBMOD", - 0x10 => self.run_lt(), // "LT", - 0x11 => self.run_gt(), // "GT", - 0x12 => self.run_syscall(opcode, 2, false), // "SLT", - 0x13 => self.run_syscall(opcode, 2, false), // "SGT", - 0x14 => self.run_eq(), // "EQ", - 0x15 => self.run_iszero(), // "ISZERO", - 0x16 => self.run_and(), // "AND", - 0x17 => self.run_or(), // "OR", - 0x18 => self.run_xor(), // "XOR", - 0x19 => self.run_not(), // "NOT", - 0x1a => self.run_byte(), // "BYTE", - 0x1b => self.run_shl(), // "SHL", - 0x1c => self.run_shr(), // "SHR", - 0x1d => self.run_syscall(opcode, 2, false), // "SAR", - 0x20 => self.run_syscall(opcode, 2, false), // "KECCAK256", - 0x21 => self.run_keccak_general(), // "KECCAK_GENERAL", - 0x30 => self.run_syscall(opcode, 0, true), // "ADDRESS", - 0x31 => self.run_syscall(opcode, 1, false), // "BALANCE", - 0x32 => self.run_syscall(opcode, 0, true), // "ORIGIN", - 0x33 => self.run_syscall(opcode, 0, true), // "CALLER", - 0x34 => self.run_syscall(opcode, 0, true), // "CALLVALUE", - 0x35 => self.run_syscall(opcode, 1, false), // "CALLDATALOAD", - 0x36 => self.run_syscall(opcode, 0, true), // "CALLDATASIZE", - 0x37 => self.run_syscall(opcode, 3, false), // "CALLDATACOPY", - 0x38 => self.run_syscall(opcode, 0, true), // "CODESIZE", - 0x39 => self.run_syscall(opcode, 3, false), // "CODECOPY", - 0x3a => self.run_syscall(opcode, 0, true), // "GASPRICE", - 0x3b => self.run_syscall(opcode, 1, false), // "EXTCODESIZE", - 0x3c => self.run_syscall(opcode, 4, false), // "EXTCODECOPY", - 0x3d => self.run_syscall(opcode, 0, true), // "RETURNDATASIZE", - 0x3e => self.run_syscall(opcode, 3, false), // "RETURNDATACOPY", - 0x3f => self.run_syscall(opcode, 1, false), // "EXTCODEHASH", - 0x40 => self.run_syscall(opcode, 1, false), // "BLOCKHASH", - 0x41 => self.run_syscall(opcode, 0, true), // "COINBASE", - 0x42 => self.run_syscall(opcode, 0, true), // "TIMESTAMP", - 0x43 => self.run_syscall(opcode, 0, true), // "NUMBER", - 0x44 => self.run_syscall(opcode, 0, true), // "DIFFICULTY", - 0x45 => self.run_syscall(opcode, 0, true), // "GASLIMIT", - 0x46 => self.run_syscall(opcode, 0, true), // "CHAINID", - 0x47 => self.run_syscall(opcode, 0, true), // SELFABALANCE, - 0x48 => self.run_syscall(opcode, 0, true), // "BASEFEE", - 0x49 => self.run_prover_input(), // "PROVER_INPUT", - 0x50 => self.run_pop(), // "POP", - 0x51 => self.run_syscall(opcode, 1, false), // "MLOAD", - 0x52 => self.run_syscall(opcode, 2, false), // "MSTORE", - 0x53 => self.run_syscall(opcode, 2, false), // "MSTORE8", - 0x54 => self.run_syscall(opcode, 1, false), // "SLOAD", - 0x55 => self.run_syscall(opcode, 2, false), // "SSTORE", - 0x56 => self.run_jump(), // "JUMP", - 0x57 => self.run_jumpi(), // "JUMPI", - 0x58 => self.run_pc(), // "PC", - 0x59 => self.run_syscall(opcode, 0, true), // "MSIZE", - 0x5a => self.run_syscall(opcode, 0, true), // "GAS", - 0x5b => self.run_jumpdest(), // "JUMPDEST", - x if (0x5f..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH" - x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f), // "DUP" - x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f), // "SWAP" - 0xa0 => self.run_syscall(opcode, 2, false), // "LOG0", - 0xa1 => self.run_syscall(opcode, 3, false), // "LOG1", - 0xa2 => self.run_syscall(opcode, 4, false), // "LOG2", - 0xa3 => self.run_syscall(opcode, 5, false), // "LOG3", - 0xa4 => self.run_syscall(opcode, 6, false), // "LOG4", - 0xa5 => { - log::warn!( - "Kernel panic at {}, stack = {:?}, memory = {:?}", - KERNEL.offset_name(self.generation_state.registers.program_counter), - self.stack(), - self.get_kernel_general_memory() - ); - Err(ProgramError::KernelPanic) - } // "PANIC", - x if (0xc0..0xe0).contains(&x) => self.run_mstore_32bytes(x - 0xc0 + 1), // "MSTORE_32BYTES", - 0xf0 => self.run_syscall(opcode, 3, false), // "CREATE", - 0xf1 => self.run_syscall(opcode, 7, false), // "CALL", - 0xf2 => self.run_syscall(opcode, 7, false), // "CALLCODE", - 0xf3 => self.run_syscall(opcode, 2, false), // "RETURN", - 0xf4 => self.run_syscall(opcode, 6, false), // "DELEGATECALL", - 0xf5 => self.run_syscall(opcode, 4, false), // "CREATE2", - 0xf6 => self.run_get_context(), // "GET_CONTEXT", - 0xf7 => self.run_set_context(), // "SET_CONTEXT", - 0xf8 => self.run_mload_32bytes(), // "MLOAD_32BYTES", - 0xf9 => self.run_exit_kernel(), // "EXIT_KERNEL", - 0xfa => self.run_syscall(opcode, 6, false), // "STATICCALL", - 0xfb => self.run_mload_general(), // "MLOAD_GENERAL", - 0xfc => self.run_mstore_general(), // "MSTORE_GENERAL", - 0xfd => self.run_syscall(opcode, 2, false), // "REVERT", - 0xfe => { - log::warn!( - "Invalid opcode at {}", - KERNEL.offset_name(self.generation_state.registers.program_counter), - ); - Err(ProgramError::InvalidOpcode) - } // "INVALID", - 0xff => self.run_syscall(opcode, 1, false), // "SELFDESTRUCT", - _ => { - log::warn!( - "Unrecognized opcode at {}", - KERNEL.offset_name(self.generation_state.registers.program_counter), - ); - Err(ProgramError::InvalidOpcode) - } - }?; - - #[cfg(debug_assertions)] - if self - .debug_offsets - .contains(&self.generation_state.registers.program_counter) - { - println!("At {},", self.offset_name()); - } else if let Some(label) = self.offset_label() { - println!("At {label}"); - } - - if !self.is_kernel() { - let gas_limit_address = MemoryAddress { - context: self.context(), - segment: Segment::ContextMetadata.unscale(), - virt: ContextMetadata::GasLimit.unscale(), - }; - let gas_limit = - u256_to_usize(self.generation_state.memory.get(gas_limit_address))? as u64; - if self.generation_state.registers.gas_used > gas_limit { - return Err(ProgramError::OutOfGas); - } - } - - Ok(()) - } - - fn offset_name(&self) -> String { - KERNEL.offset_name(self.generation_state.registers.program_counter) - } - - fn offset_label(&self) -> Option { - KERNEL.offset_label(self.generation_state.registers.program_counter) - } - - fn run_add(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x.overflowing_add(y).0) - } - - fn run_mul(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x.overflowing_mul(y).0) - } - - fn run_sub(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x.overflowing_sub(y).0) - } - - fn run_addfp254(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()? % BN_BASE; - let y = self.pop()? % BN_BASE; - // BN_BASE is 254-bit so addition can't overflow - self.push((x + y) % BN_BASE) - } - - fn run_mulfp254(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push( - U256::try_from(x.full_mul(y) % BN_BASE) - .expect("BN_BASE is 254 bit so the U512 fits in a U256"), - ) - } - - fn run_subfp254(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()? % BN_BASE; - let y = self.pop()? % BN_BASE; - // BN_BASE is 254-bit so addition can't overflow - self.push((x + (BN_BASE - y)) % BN_BASE) - } - - fn run_div(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(if y.is_zero() { U256::zero() } else { x / y }) - } - - fn run_mod(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(if y.is_zero() { U256::zero() } else { x % y }) - } - - fn run_addmod(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - let z = self.pop()?; - self.push(if z.is_zero() { - z - } else { - let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); - U256::try_from((x + y) % z) - .expect("Inputs are U256 and their sum mod a U256 fits in a U256.") - }) - } - - fn run_submod(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - let z = self.pop()?; - self.push(if z.is_zero() { - z - } else { - let (x, y, z) = (U512::from(x), U512::from(y), U512::from(z)); - U256::try_from((z + x - y) % z) - .expect("Inputs are U256 and their difference mod a U256 fits in a U256.") - }) - } - - fn run_mulmod(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - let z = self.pop()?; - self.push(if z.is_zero() { - z - } else { - U256::try_from(x.full_mul(y) % z) - .expect("Inputs are U256 and their product mod a U256 fits in a U256.") - }) - } - - fn run_lt(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push_bool(x < y) - } - - fn run_gt(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push_bool(x > y) - } - - fn run_eq(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push_bool(x == y) - } - - fn run_iszero(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - self.push_bool(x.is_zero()) - } - - fn run_and(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x & y) - } - - fn run_or(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x | y) - } - - fn run_xor(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let y = self.pop()?; - self.push(x ^ y) - } - - fn run_not(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - self.push(!x) - } - - fn run_byte(&mut self) -> anyhow::Result<(), ProgramError> { - let i = self.pop()?; - let x = self.pop()?; - let result = if i < 32.into() { - // Calling `as_usize()` here is safe. - x.byte(31 - i.as_usize()) - } else { - 0 - }; - self.push(result.into()) - } - - fn run_shl(&mut self) -> anyhow::Result<(), ProgramError> { - let shift = self.pop()?; - let value = self.pop()?; - self.push(if shift < U256::from(256usize) { - value << shift - } else { - U256::zero() - }) - } - - fn run_shr(&mut self) -> anyhow::Result<(), ProgramError> { - let shift = self.pop()?; - let value = self.pop()?; - self.push(value >> shift) - } - - fn run_keccak_general(&mut self) -> anyhow::Result<(), ProgramError> { - let addr = self.pop()?; - let (context, segment, offset) = unpack_address!(addr); - - let size = u256_to_usize(self.pop()?)?; - let bytes = (offset..offset + size) - .map(|i| { - self.generation_state - .memory - .mload_general(context, segment, i) - .byte(0) - }) - .collect::>(); - #[cfg(debug_assertions)] - println!("Hashing {:?}", &bytes); - let hash = keccak(bytes); - self.push(U256::from_big_endian(hash.as_bytes())) - } - - fn run_prover_input(&mut self) -> Result<(), ProgramError> { - let prover_input_fn = self - .prover_inputs_map - .get(&(self.generation_state.registers.program_counter - 1)) - .ok_or(ProgramError::ProverInputError( - ProverInputError::InvalidMptInput, - ))?; - let output = self.generation_state.prover_input(prover_input_fn)?; - self.push(output) - } - - fn run_pop(&mut self) -> anyhow::Result<(), ProgramError> { - self.pop().map(|_| ()) - } - - fn run_syscall( - &mut self, - opcode: u8, - stack_values_read: usize, - stack_len_increased: bool, - ) -> Result<(), ProgramError> { - TryInto::::try_into(self.generation_state.registers.gas_used) - .map_err(|_| ProgramError::GasLimitError)?; - if self.generation_state.registers.stack_len < stack_values_read { - return Err(ProgramError::StackUnderflow); - } - - if stack_len_increased - && !self.is_kernel() - && self.generation_state.registers.stack_len >= MAX_USER_STACK_SIZE - { - return Err(ProgramError::StackOverflow); - }; - - let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"]; - let handler_addr = { - let offset = handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize); - self.get_memory_segment(Segment::Code)[offset..offset + 3] - .iter() - .fold(U256::from(0), |acc, &elt| acc * (1 << 8) + elt) - }; - - let new_program_counter = - u256_to_usize(handler_addr).map_err(|_| ProgramError::IntegerTooLarge)?; - - let syscall_info = U256::from(self.generation_state.registers.program_counter) - + U256::from((self.is_kernel() as usize) << 32) - + (U256::from(self.generation_state.registers.gas_used) << 192); - self.generation_state.registers.program_counter = new_program_counter; - - self.set_is_kernel(true); - self.generation_state.registers.gas_used = 0; - self.push(syscall_info) - } - - fn get_jumpdest_bit(&self, offset: usize) -> U256 { - if self.generation_state.memory.contexts[self.context()].segments - [Segment::JumpdestBits.unscale()] - .content - .len() - > offset - { - self.generation_state.memory.get(MemoryAddress { - context: self.context(), - segment: Segment::JumpdestBits.unscale(), - virt: offset, - }) - } else { - 0.into() - } - } - - pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { - self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] - .content - .iter() - .map(|x| x.bit(0)) - .collect() - } - - fn add_jumpdest_offset(&mut self, offset: usize) { - if let Some(jumpdest_table) = self - .jumpdest_table - .get_mut(&self.generation_state.registers.context) - { - jumpdest_table.insert(offset); - } else { - self.jumpdest_table.insert( - self.generation_state.registers.context, - BTreeSet::from([offset]), - ); - } - } - - fn run_jump(&mut self) -> anyhow::Result<(), ProgramError> { - let offset = self.pop()?; - - // Check that the destination is valid. - let offset: usize = u256_to_usize(offset)?; - - let jumpdest_bit = self.get_jumpdest_bit(offset); - - if !self.is_kernel() && jumpdest_bit != U256::one() { - return Err(ProgramError::InvalidJumpDestination); - } - - self.jump_to(offset, false) - } - - fn run_jumpi(&mut self) -> anyhow::Result<(), ProgramError> { - let offset = self.pop()?; - let cond = self.pop()?; - - let offset: usize = offset - .try_into() - .map_err(|_| ProgramError::InvalidJumpiDestination)?; - - let jumpdest_bit = self.get_jumpdest_bit(offset); - - if !cond.is_zero() && (self.is_kernel() || jumpdest_bit == U256::one()) { - self.jump_to(offset, true)?; - } - - if !cond.is_zero() && !self.is_kernel() && jumpdest_bit != U256::one() { - return Err(ProgramError::InvalidJumpiDestination); - } - Ok(()) - } - - fn run_pc(&mut self) -> anyhow::Result<(), ProgramError> { - self.push( - (self - .generation_state - .registers - .program_counter - .saturating_sub(1)) - .into(), - ) - } - - fn run_jumpdest(&mut self) -> anyhow::Result<(), ProgramError> { - assert!(!self.is_kernel(), "JUMPDEST is not needed in kernel code"); - Ok(()) - } - - fn jump_to(&mut self, offset: usize, is_jumpi: bool) -> anyhow::Result<(), ProgramError> { - self.generation_state.registers.program_counter = offset; - - if offset == KERNEL.global_labels["observe_new_address"] { - let tip_u256 = stack_peek(&self.generation_state, 0)?; - let tip_h256 = H256::from_uint(&tip_u256); - let tip_h160 = H160::from(tip_h256); - self.generation_state.observe_address(tip_h160); - } else if offset == KERNEL.global_labels["observe_new_contract"] { - let tip_u256 = stack_peek(&self.generation_state, 0)?; - let tip_h256 = H256::from_uint(&tip_u256); - self.generation_state.observe_contract(tip_h256)?; - } - - if !self.is_kernel() { - self.add_jumpdest_offset(offset); - } - - Ok(()) - } - - fn run_push(&mut self, num_bytes: u8) -> anyhow::Result<(), ProgramError> { - let x = U256::from_big_endian(&self.code_slice(num_bytes as usize)); - self.incr(num_bytes as usize); - self.push(x) - } - - fn run_dup(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { - let len = self.stack_len(); - if !self.is_kernel() && len >= MAX_USER_STACK_SIZE { - return Err(ProgramError::StackOverflow); - } - if n as usize > self.stack_len() { - return Err(ProgramError::StackUnderflow); - } - self.push(stack_peek(&self.generation_state, n as usize - 1)?) - } - - fn run_swap(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { - let len = self.stack_len(); - if n as usize >= len { - return Err(ProgramError::StackUnderflow); - } - let to_swap = stack_peek(&self.generation_state, n as usize)?; - let old_value = self.stack_segment_mut()[len - n as usize - 1]; - - self.stack_segment_mut()[len - n as usize - 1] = self.stack_top()?; - let mem_write_op = InterpreterMemOpKind::Write( - old_value, - self.context(), - Segment::Stack.unscale(), - len - n as usize - 1, - ); - self.memops.push(mem_write_op); - self.generation_state.registers.stack_top = to_swap; - Ok(()) - } - - fn run_get_context(&mut self) -> anyhow::Result<(), ProgramError> { - self.push(U256::from(self.context()) << CONTEXT_SCALING_FACTOR) - } - - fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { - let x = self.pop()?; - let new_ctx = u256_to_usize(x >> CONTEXT_SCALING_FACTOR)?; - let sp_to_save = self.stack_len().into(); - - let old_ctx = self.context(); - - let sp_field = ContextMetadata::StackSize.unscale(); - - let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); - let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); - self.generation_state.memory.set(old_sp_addr, sp_to_save); - - let new_sp = u256_to_usize(self.generation_state.memory.get(new_sp_addr))?; - - if new_sp > 0 { - let new_stack_top = self.generation_state.memory.contexts[new_ctx].segments - [Segment::Stack.unscale()] - .content[new_sp - 1]; - self.generation_state.registers.stack_top = new_stack_top; - } - self.set_context(new_ctx); - self.generation_state.registers.stack_len = new_sp; - - Ok(()) - } - - fn run_mload_general(&mut self) -> anyhow::Result<(), ProgramError> { - let addr = self.pop()?; - let (context, segment, offset) = unpack_address!(addr); - let value = self - .generation_state - .memory - .mload_general(context, segment, offset); - assert!(value.bits() <= segment.bit_range()); - self.push(value) - } - - fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { - let addr = self.pop()?; - let (context, segment, offset) = unpack_address!(addr); - let len = u256_to_usize(self.pop()?)?; - if len > 32 { - return Err(ProgramError::IntegerTooLarge); - } - let bytes: Vec = (0..len) - .map(|i| { - self.generation_state - .memory - .mload_general(context, segment, offset + i) - .low_u32() as u8 - }) - .collect(); - let value = U256::from_big_endian(&bytes); - self.push(value) - } - - fn run_mstore_general(&mut self) -> anyhow::Result<(), ProgramError> { - let value = self.pop()?; - let addr = self.pop()?; - let (context, segment, offset) = unpack_address!(addr); - let memop = self - .generation_state - .memory - .mstore_general(context, segment, offset, value); - self.memops.push(memop); - Ok(()) - } - - fn run_mstore_32bytes(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { - let addr = self.pop()?; - let (context, segment, offset) = unpack_address!(addr); - let value = self.pop()?; - - let mut bytes = vec![0; 32]; - value.to_little_endian(&mut bytes); - bytes.resize(n as usize, 0); - bytes.reverse(); - - for (i, &byte) in bytes.iter().enumerate() { - let memop = self.generation_state.memory.mstore_general( - context, - segment, - offset + i, - byte.into(), - ); - self.memops.push(memop); - } - - self.push(addr + U256::from(n)) - } - - fn run_exit_kernel(&mut self) -> anyhow::Result<(), ProgramError> { - let kexit_info = self.pop()?; - - let kexit_info_u64 = kexit_info.0[0]; - let program_counter = kexit_info_u64 as u32 as usize; - let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32; - assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1); - let is_kernel_mode = is_kernel_mode_val != 0; - let gas_used_val = kexit_info.0[3]; - TryInto::::try_into(gas_used_val).map_err(|_| ProgramError::GasLimitError)?; - - self.generation_state.registers.program_counter = program_counter; - self.set_is_kernel(is_kernel_mode); - self.generation_state.registers.gas_used = gas_used_val; - - Ok(()) - } - - fn run_exception(&mut self, exc_code: u8) -> Result<(), ProgramError> { - let disallowed_len = MAX_USER_STACK_SIZE + 1; - - if self.stack_len() == disallowed_len { - // This is a stack overflow that should have been caught earlier. - return Err(ProgramError::StackOverflow); - }; - - let handler_jumptable_addr = KERNEL.global_labels["exception_jumptable"]; - let handler_addr = { - let offset = handler_jumptable_addr + (exc_code as usize) * (BYTES_PER_OFFSET as usize); - assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); - self.get_memory_segment(Segment::Code)[offset..offset + 3] - .iter() - .fold(U256::from(0), |acc, &elt| acc * 256 + elt) - }; - - let new_program_counter = u256_to_usize(handler_addr)?; - - let exc_info = U256::from(self.generation_state.registers.program_counter) - + (U256::from(self.generation_state.registers.gas_used) << 192); - - self.push(exc_info)?; - - // Set registers before pushing to the stack; in particular, we need to set kernel mode so we - // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we - // make `exc_info`, which should contain the old values. - self.generation_state.registers.program_counter = new_program_counter; - self.set_is_kernel(true); - self.generation_state.registers.gas_used = 0; - - Ok(()) - } - - pub(crate) const fn stack_len(&self) -> usize { - self.generation_state.registers.stack_len - } - - pub(crate) fn stack_top(&self) -> anyhow::Result { - if self.stack_len() > 0 { - Ok(self.generation_state.registers.stack_top) - } else { - Err(ProgramError::StackUnderflow) - } - } - - pub(crate) const fn is_kernel(&self) -> bool { - self.generation_state.registers.is_kernel - } - - pub(crate) fn set_is_kernel(&mut self, is_kernel: bool) { - self.generation_state.registers.is_kernel = is_kernel - } - - pub(crate) const fn context(&self) -> usize { - self.generation_state.registers.context - } - - pub(crate) fn set_context(&mut self, context: usize) { - if context == 0 { - assert!(self.is_kernel()); - } - self.generation_state.registers.context = context; - } - - /// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS. - pub(crate) fn initialize_rlp_segment(&mut self) { - self.generation_state.memory.set( - MemoryAddress::new(0, Segment::RlpRaw, 0xFFFFFFFF), - 128.into(), - ) - } -} - -fn get_mnemonic(opcode: u8) -> &'static str { - match opcode { - 0x00 => "STOP", - 0x01 => "ADD", - 0x02 => "MUL", - 0x03 => "SUB", - 0x04 => "DIV", - 0x05 => "SDIV", - 0x06 => "MOD", - 0x07 => "SMOD", - 0x08 => "ADDMOD", - 0x09 => "MULMOD", - 0x0a => "EXP", - 0x0b => "SIGNEXTEND", - 0x0c => "ADDFP254", - 0x0d => "MULFP254", - 0x0e => "SUBFP254", - 0x0f => "SUBMOD", - 0x10 => "LT", - 0x11 => "GT", - 0x12 => "SLT", - 0x13 => "SGT", - 0x14 => "EQ", - 0x15 => "ISZERO", - 0x16 => "AND", - 0x17 => "OR", - 0x18 => "XOR", - 0x19 => "NOT", - 0x1a => "BYTE", - 0x1b => "SHL", - 0x1c => "SHR", - 0x1d => "SAR", - 0x20 => "KECCAK256", - 0x21 => "KECCAK_GENERAL", - 0x30 => "ADDRESS", - 0x31 => "BALANCE", - 0x32 => "ORIGIN", - 0x33 => "CALLER", - 0x34 => "CALLVALUE", - 0x35 => "CALLDATALOAD", - 0x36 => "CALLDATASIZE", - 0x37 => "CALLDATACOPY", - 0x38 => "CODESIZE", - 0x39 => "CODECOPY", - 0x3a => "GASPRICE", - 0x3b => "EXTCODESIZE", - 0x3c => "EXTCODECOPY", - 0x3d => "RETURNDATASIZE", - 0x3e => "RETURNDATACOPY", - 0x3f => "EXTCODEHASH", - 0x40 => "BLOCKHASH", - 0x41 => "COINBASE", - 0x42 => "TIMESTAMP", - 0x43 => "NUMBER", - 0x44 => "DIFFICULTY", - 0x45 => "GASLIMIT", - 0x46 => "CHAINID", - 0x48 => "BASEFEE", - 0x49 => "PROVER_INPUT", - 0x50 => "POP", - 0x51 => "MLOAD", - 0x52 => "MSTORE", - 0x53 => "MSTORE8", - 0x54 => "SLOAD", - 0x55 => "SSTORE", - 0x56 => "JUMP", - 0x57 => "JUMPI", - 0x58 => "GETPC", - 0x59 => "MSIZE", - 0x5a => "GAS", - 0x5b => "JUMPDEST", - 0x5f => "PUSH0", - 0x60 => "PUSH1", - 0x61 => "PUSH2", - 0x62 => "PUSH3", - 0x63 => "PUSH4", - 0x64 => "PUSH5", - 0x65 => "PUSH6", - 0x66 => "PUSH7", - 0x67 => "PUSH8", - 0x68 => "PUSH9", - 0x69 => "PUSH10", - 0x6a => "PUSH11", - 0x6b => "PUSH12", - 0x6c => "PUSH13", - 0x6d => "PUSH14", - 0x6e => "PUSH15", - 0x6f => "PUSH16", - 0x70 => "PUSH17", - 0x71 => "PUSH18", - 0x72 => "PUSH19", - 0x73 => "PUSH20", - 0x74 => "PUSH21", - 0x75 => "PUSH22", - 0x76 => "PUSH23", - 0x77 => "PUSH24", - 0x78 => "PUSH25", - 0x79 => "PUSH26", - 0x7a => "PUSH27", - 0x7b => "PUSH28", - 0x7c => "PUSH29", - 0x7d => "PUSH30", - 0x7e => "PUSH31", - 0x7f => "PUSH32", - 0x80 => "DUP1", - 0x81 => "DUP2", - 0x82 => "DUP3", - 0x83 => "DUP4", - 0x84 => "DUP5", - 0x85 => "DUP6", - 0x86 => "DUP7", - 0x87 => "DUP8", - 0x88 => "DUP9", - 0x89 => "DUP10", - 0x8a => "DUP11", - 0x8b => "DUP12", - 0x8c => "DUP13", - 0x8d => "DUP14", - 0x8e => "DUP15", - 0x8f => "DUP16", - 0x90 => "SWAP1", - 0x91 => "SWAP2", - 0x92 => "SWAP3", - 0x93 => "SWAP4", - 0x94 => "SWAP5", - 0x95 => "SWAP6", - 0x96 => "SWAP7", - 0x97 => "SWAP8", - 0x98 => "SWAP9", - 0x99 => "SWAP10", - 0x9a => "SWAP11", - 0x9b => "SWAP12", - 0x9c => "SWAP13", - 0x9d => "SWAP14", - 0x9e => "SWAP15", - 0x9f => "SWAP16", - 0xa0 => "LOG0", - 0xa1 => "LOG1", - 0xa2 => "LOG2", - 0xa3 => "LOG3", - 0xa4 => "LOG4", - 0xa5 => "PANIC", - 0xc0 => "MSTORE_32BYTES_1", - 0xc1 => "MSTORE_32BYTES_2", - 0xc2 => "MSTORE_32BYTES_3", - 0xc3 => "MSTORE_32BYTES_4", - 0xc4 => "MSTORE_32BYTES_5", - 0xc5 => "MSTORE_32BYTES_6", - 0xc6 => "MSTORE_32BYTES_7", - 0xc7 => "MSTORE_32BYTES_8", - 0xc8 => "MSTORE_32BYTES_9", - 0xc9 => "MSTORE_32BYTES_10", - 0xca => "MSTORE_32BYTES_11", - 0xcb => "MSTORE_32BYTES_12", - 0xcc => "MSTORE_32BYTES_13", - 0xcd => "MSTORE_32BYTES_14", - 0xce => "MSTORE_32BYTES_15", - 0xcf => "MSTORE_32BYTES_16", - 0xd0 => "MSTORE_32BYTES_17", - 0xd1 => "MSTORE_32BYTES_18", - 0xd2 => "MSTORE_32BYTES_19", - 0xd3 => "MSTORE_32BYTES_20", - 0xd4 => "MSTORE_32BYTES_21", - 0xd5 => "MSTORE_32BYTES_22", - 0xd6 => "MSTORE_32BYTES_23", - 0xd7 => "MSTORE_32BYTES_24", - 0xd8 => "MSTORE_32BYTES_25", - 0xd9 => "MSTORE_32BYTES_26", - 0xda => "MSTORE_32BYTES_27", - 0xdb => "MSTORE_32BYTES_28", - 0xdc => "MSTORE_32BYTES_29", - 0xdd => "MSTORE_32BYTES_30", - 0xde => "MSTORE_32BYTES_31", - 0xdf => "MSTORE_32BYTES_32", - 0xf0 => "CREATE", - 0xf1 => "CALL", - 0xf2 => "CALLCODE", - 0xf3 => "RETURN", - 0xf4 => "DELEGATECALL", - 0xf5 => "CREATE2", - 0xf6 => "GET_CONTEXT", - 0xf7 => "SET_CONTEXT", - 0xf8 => "MLOAD_32BYTES", - 0xf9 => "EXIT_KERNEL", - 0xfa => "STATICCALL", - 0xfb => "MLOAD_GENERAL", - 0xfc => "MSTORE_GENERAL", - 0xfd => "REVERT", - 0xfe => "INVALID", - 0xff => "SELFDESTRUCT", - _ => panic!("Unrecognized opcode {opcode}"), - } -} - -macro_rules! unpack_address { - ($addr:ident) => {{ - let offset = $addr.low_u32() as usize; - let segment = Segment::all()[($addr >> SEGMENT_SCALING_FACTOR).low_u32() as usize]; - let context = ($addr >> CONTEXT_SCALING_FACTOR).low_u32() as usize; - (context, segment, offset) - }}; -} -pub(crate) use unpack_address; - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use ethereum_types::U256; - use plonky2::field::goldilocks_field::GoldilocksField as F; - - use crate::cpu::kernel::constants::context_metadata::ContextMetadata; - use crate::cpu::kernel::interpreter::{run, Interpreter}; - use crate::memory::segments::Segment; - use crate::witness::memory::MemoryAddress; - use crate::witness::operation::CONTEXT_SCALING_FACTOR; - - #[test] - fn test_run() -> anyhow::Result<()> { - let code = vec![ - 0x60, 0x1, 0x60, 0x2, 0x1, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56, - ]; // PUSH1, 1, PUSH1, 2, ADD, PUSH4 deadbeef, JUMP - assert_eq!( - run::(&code, 0, vec![], &HashMap::new())?.stack(), - &[0x3.into()], - ); - Ok(()) - } - - #[test] - fn test_run_with_memory() -> anyhow::Result<()> { - // PUSH1 0xff - // PUSH1 0 - // MSTORE - - // PUSH1 0 - // MLOAD - - // PUSH1 1 - // MLOAD - - // PUSH1 0x42 - // PUSH1 0x27 - // MSTORE8 - let code = [ - 0x60, 0xff, 0x60, 0x0, 0x52, 0x60, 0, 0x51, 0x60, 0x1, 0x51, 0x60, 0x42, 0x60, 0x27, - 0x53, - ]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); - - interpreter.set_code(1, code.to_vec()); - - interpreter.generation_state.memory.contexts[1].segments - [Segment::ContextMetadata.unscale()] - .set(ContextMetadata::GasLimit.unscale(), 100_000.into()); - // Set context and kernel mode. - interpreter.set_context(1); - interpreter.set_is_kernel(false); - // Set memory necessary to sys_stop. - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentProgramCounter.unscale(), - ), - 0xdeadbeefu32.into(), - ); - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentContext.unscale(), - ), - U256::one() << CONTEXT_SCALING_FACTOR, - ); - - interpreter.run()?; - - // sys_stop returns `success` and `cum_gas_used`, that we need to pop. - interpreter.pop().expect("Stack should not be empty"); - interpreter.pop().expect("Stack should not be empty"); - - assert_eq!(interpreter.stack(), &[0xff.into(), 0xff00.into()]); - assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] - .get(0x27), - 0x42.into() - ); - assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] - .get(0x1f), - 0xff.into() - ); - Ok(()) - } -} diff --git a/evm/src/cpu/kernel/keccak_util.rs b/evm/src/cpu/kernel/keccak_util.rs deleted file mode 100644 index e1cae7c27b..0000000000 --- a/evm/src/cpu/kernel/keccak_util.rs +++ /dev/null @@ -1,59 +0,0 @@ -use tiny_keccak::keccakf; - -use crate::keccak_sponge::columns::{KECCAK_WIDTH_BYTES, KECCAK_WIDTH_U32S}; - -/// Like tiny-keccak's `keccakf`, but deals with `u32` limbs instead of `u64` limbs. -pub(crate) fn keccakf_u32s(state_u32s: &mut [u32; KECCAK_WIDTH_U32S]) { - let mut state_u64s: [u64; 25] = core::array::from_fn(|i| { - let lo = state_u32s[i * 2] as u64; - let hi = state_u32s[i * 2 + 1] as u64; - lo | (hi << 32) - }); - keccakf(&mut state_u64s); - *state_u32s = core::array::from_fn(|i| { - let u64_limb = state_u64s[i / 2]; - let is_hi = i % 2; - (u64_limb >> (is_hi * 32)) as u32 - }); -} - -/// Like tiny-keccak's `keccakf`, but deals with bytes instead of `u64` limbs. -pub(crate) fn keccakf_u8s(state_u8s: &mut [u8; KECCAK_WIDTH_BYTES]) { - let mut state_u64s: [u64; 25] = - core::array::from_fn(|i| u64::from_le_bytes(state_u8s[i * 8..][..8].try_into().unwrap())); - keccakf(&mut state_u64s); - *state_u8s = core::array::from_fn(|i| { - let u64_limb = state_u64s[i / 8]; - u64_limb.to_le_bytes()[i % 8] - }); -} - -#[cfg(test)] -mod tests { - use tiny_keccak::keccakf; - - use crate::cpu::kernel::keccak_util::{keccakf_u32s, keccakf_u8s}; - - #[test] - #[rustfmt::skip] - fn test_consistency() { - // We will hash the same data using keccakf, keccakf_u32s and keccakf_u8s. - // The inputs were randomly generated in Python. - let mut state_u64s: [u64; 25] = [0x5dc43ed05dc64048, 0x7bb9e18cdc853880, 0xc1fde300665b008f, 0xeeab85e089d5e431, 0xf7d61298e9ef27ea, 0xc2c5149d1a492455, 0x37a2f4eca0c2d2f2, 0xa35e50c015b3e85c, 0xd2daeced29446ebe, 0x245845f1bac1b98e, 0x3b3aa8783f30a9bf, 0x209ca9a81956d241, 0x8b8ea714da382165, 0x6063e67e202c6d29, 0xf4bac2ded136b907, 0xb17301b461eae65, 0xa91ff0e134ed747c, 0xcc080b28d0c20f1d, 0xf0f79cbec4fb551c, 0x25e04cb0aa930cad, 0x803113d1b541a202, 0xfaf1e4e7cd23b7ec, 0x36a03bbf2469d3b0, 0x25217341908cdfc0, 0xe9cd83f88fdcd500]; - let mut state_u32s: [u32; 50] = [0x5dc64048, 0x5dc43ed0, 0xdc853880, 0x7bb9e18c, 0x665b008f, 0xc1fde300, 0x89d5e431, 0xeeab85e0, 0xe9ef27ea, 0xf7d61298, 0x1a492455, 0xc2c5149d, 0xa0c2d2f2, 0x37a2f4ec, 0x15b3e85c, 0xa35e50c0, 0x29446ebe, 0xd2daeced, 0xbac1b98e, 0x245845f1, 0x3f30a9bf, 0x3b3aa878, 0x1956d241, 0x209ca9a8, 0xda382165, 0x8b8ea714, 0x202c6d29, 0x6063e67e, 0xd136b907, 0xf4bac2de, 0x461eae65, 0xb17301b, 0x34ed747c, 0xa91ff0e1, 0xd0c20f1d, 0xcc080b28, 0xc4fb551c, 0xf0f79cbe, 0xaa930cad, 0x25e04cb0, 0xb541a202, 0x803113d1, 0xcd23b7ec, 0xfaf1e4e7, 0x2469d3b0, 0x36a03bbf, 0x908cdfc0, 0x25217341, 0x8fdcd500, 0xe9cd83f8]; - let mut state_u8s: [u8; 200] = [0x48, 0x40, 0xc6, 0x5d, 0xd0, 0x3e, 0xc4, 0x5d, 0x80, 0x38, 0x85, 0xdc, 0x8c, 0xe1, 0xb9, 0x7b, 0x8f, 0x0, 0x5b, 0x66, 0x0, 0xe3, 0xfd, 0xc1, 0x31, 0xe4, 0xd5, 0x89, 0xe0, 0x85, 0xab, 0xee, 0xea, 0x27, 0xef, 0xe9, 0x98, 0x12, 0xd6, 0xf7, 0x55, 0x24, 0x49, 0x1a, 0x9d, 0x14, 0xc5, 0xc2, 0xf2, 0xd2, 0xc2, 0xa0, 0xec, 0xf4, 0xa2, 0x37, 0x5c, 0xe8, 0xb3, 0x15, 0xc0, 0x50, 0x5e, 0xa3, 0xbe, 0x6e, 0x44, 0x29, 0xed, 0xec, 0xda, 0xd2, 0x8e, 0xb9, 0xc1, 0xba, 0xf1, 0x45, 0x58, 0x24, 0xbf, 0xa9, 0x30, 0x3f, 0x78, 0xa8, 0x3a, 0x3b, 0x41, 0xd2, 0x56, 0x19, 0xa8, 0xa9, 0x9c, 0x20, 0x65, 0x21, 0x38, 0xda, 0x14, 0xa7, 0x8e, 0x8b, 0x29, 0x6d, 0x2c, 0x20, 0x7e, 0xe6, 0x63, 0x60, 0x7, 0xb9, 0x36, 0xd1, 0xde, 0xc2, 0xba, 0xf4, 0x65, 0xae, 0x1e, 0x46, 0x1b, 0x30, 0x17, 0xb, 0x7c, 0x74, 0xed, 0x34, 0xe1, 0xf0, 0x1f, 0xa9, 0x1d, 0xf, 0xc2, 0xd0, 0x28, 0xb, 0x8, 0xcc, 0x1c, 0x55, 0xfb, 0xc4, 0xbe, 0x9c, 0xf7, 0xf0, 0xad, 0xc, 0x93, 0xaa, 0xb0, 0x4c, 0xe0, 0x25, 0x2, 0xa2, 0x41, 0xb5, 0xd1, 0x13, 0x31, 0x80, 0xec, 0xb7, 0x23, 0xcd, 0xe7, 0xe4, 0xf1, 0xfa, 0xb0, 0xd3, 0x69, 0x24, 0xbf, 0x3b, 0xa0, 0x36, 0xc0, 0xdf, 0x8c, 0x90, 0x41, 0x73, 0x21, 0x25, 0x0, 0xd5, 0xdc, 0x8f, 0xf8, 0x83, 0xcd, 0xe9]; - - // The first output was generated using tiny-keccak; the others were derived from it. - let out_u64s: [u64; 25] = [0x8a541df597e79a72, 0x5c26b8c84faaebb3, 0xc0e8f4e67ca50497, 0x95d98a688de12dec, 0x1c837163975ffaed, 0x9481ec7ef948900e, 0x6a072c65d050a9a1, 0x3b2817da6d615bee, 0x7ffb3c4f8b94bf21, 0x85d6c418cced4a11, 0x18edbe0442884135, 0x2bf265ef3204b7fd, 0xc1e12ce30630d105, 0x8c554dbc61844574, 0x5504db652ce9e42c, 0x2217f3294d0dabe5, 0x7df8eebbcf5b74df, 0x3a56ebb61956f501, 0x7840219dc6f37cc, 0x23194159c967947, 0x9da289bf616ba14d, 0x5a90aaeeca9e9e5b, 0x885dcdc4a549b4e3, 0x46cb188c20947df7, 0x1ef285948ee3d8ab]; - let out_u32s: [u32; 50] = [0x97e79a72, 0x8a541df5, 0x4faaebb3, 0x5c26b8c8, 0x7ca50497, 0xc0e8f4e6, 0x8de12dec, 0x95d98a68, 0x975ffaed, 0x1c837163, 0xf948900e, 0x9481ec7e, 0xd050a9a1, 0x6a072c65, 0x6d615bee, 0x3b2817da, 0x8b94bf21, 0x7ffb3c4f, 0xcced4a11, 0x85d6c418, 0x42884135, 0x18edbe04, 0x3204b7fd, 0x2bf265ef, 0x630d105, 0xc1e12ce3, 0x61844574, 0x8c554dbc, 0x2ce9e42c, 0x5504db65, 0x4d0dabe5, 0x2217f329, 0xcf5b74df, 0x7df8eebb, 0x1956f501, 0x3a56ebb6, 0xdc6f37cc, 0x7840219, 0x9c967947, 0x2319415, 0x616ba14d, 0x9da289bf, 0xca9e9e5b, 0x5a90aaee, 0xa549b4e3, 0x885dcdc4, 0x20947df7, 0x46cb188c, 0x8ee3d8ab, 0x1ef28594]; - let out_u8s: [u8; 200] = [0x72, 0x9a, 0xe7, 0x97, 0xf5, 0x1d, 0x54, 0x8a, 0xb3, 0xeb, 0xaa, 0x4f, 0xc8, 0xb8, 0x26, 0x5c, 0x97, 0x4, 0xa5, 0x7c, 0xe6, 0xf4, 0xe8, 0xc0, 0xec, 0x2d, 0xe1, 0x8d, 0x68, 0x8a, 0xd9, 0x95, 0xed, 0xfa, 0x5f, 0x97, 0x63, 0x71, 0x83, 0x1c, 0xe, 0x90, 0x48, 0xf9, 0x7e, 0xec, 0x81, 0x94, 0xa1, 0xa9, 0x50, 0xd0, 0x65, 0x2c, 0x7, 0x6a, 0xee, 0x5b, 0x61, 0x6d, 0xda, 0x17, 0x28, 0x3b, 0x21, 0xbf, 0x94, 0x8b, 0x4f, 0x3c, 0xfb, 0x7f, 0x11, 0x4a, 0xed, 0xcc, 0x18, 0xc4, 0xd6, 0x85, 0x35, 0x41, 0x88, 0x42, 0x4, 0xbe, 0xed, 0x18, 0xfd, 0xb7, 0x4, 0x32, 0xef, 0x65, 0xf2, 0x2b, 0x5, 0xd1, 0x30, 0x6, 0xe3, 0x2c, 0xe1, 0xc1, 0x74, 0x45, 0x84, 0x61, 0xbc, 0x4d, 0x55, 0x8c, 0x2c, 0xe4, 0xe9, 0x2c, 0x65, 0xdb, 0x4, 0x55, 0xe5, 0xab, 0xd, 0x4d, 0x29, 0xf3, 0x17, 0x22, 0xdf, 0x74, 0x5b, 0xcf, 0xbb, 0xee, 0xf8, 0x7d, 0x1, 0xf5, 0x56, 0x19, 0xb6, 0xeb, 0x56, 0x3a, 0xcc, 0x37, 0x6f, 0xdc, 0x19, 0x2, 0x84, 0x7, 0x47, 0x79, 0x96, 0x9c, 0x15, 0x94, 0x31, 0x2, 0x4d, 0xa1, 0x6b, 0x61, 0xbf, 0x89, 0xa2, 0x9d, 0x5b, 0x9e, 0x9e, 0xca, 0xee, 0xaa, 0x90, 0x5a, 0xe3, 0xb4, 0x49, 0xa5, 0xc4, 0xcd, 0x5d, 0x88, 0xf7, 0x7d, 0x94, 0x20, 0x8c, 0x18, 0xcb, 0x46, 0xab, 0xd8, 0xe3, 0x8e, 0x94, 0x85, 0xf2, 0x1e]; - - keccakf(&mut state_u64s); - keccakf_u32s(&mut state_u32s); - keccakf_u8s(&mut state_u8s); - - assert_eq!(state_u64s, out_u64s); - assert_eq!(state_u32s, out_u32s); - assert_eq!(state_u8s, out_u8s); - } -} diff --git a/evm/src/cpu/kernel/mod.rs b/evm/src/cpu/kernel/mod.rs deleted file mode 100644 index 5a6717f214..0000000000 --- a/evm/src/cpu/kernel/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub mod aggregator; -pub mod assembler; -mod ast; -pub(crate) mod constants; -mod cost_estimator; -pub(crate) mod keccak_util; -pub mod opcodes; -mod optimizer; -mod parser; -pub mod stack; -mod utils; - -pub(crate) mod interpreter; -#[cfg(test)] -mod tests; - -use assembler::assemble; -use parser::parse; - -use crate::cpu::kernel::constants::evm_constants; - -/// Assemble files, outputting bytes. -/// This is for debugging the kernel only. -pub fn assemble_to_bytes(files: &[String]) -> Vec { - let parsed_files: Vec<_> = files.iter().map(|f| parse(f)).collect(); - let kernel = assemble(parsed_files, evm_constants(), true); - kernel.code -} diff --git a/evm/src/cpu/kernel/opcodes.rs b/evm/src/cpu/kernel/opcodes.rs deleted file mode 100644 index 538fe0a104..0000000000 --- a/evm/src/cpu/kernel/opcodes.rs +++ /dev/null @@ -1,167 +0,0 @@ -/// The opcode of the `PUSH[n]` instruction, given a byte count `n`. -pub fn get_push_opcode(n: u8) -> u8 { - assert!(n <= 32); - 0x5f + n -} - -/// The opcode of a standard instruction (not a `PUSH`). -pub fn get_opcode(mnemonic: &str) -> u8 { - match mnemonic.to_uppercase().as_str() { - "STOP" => 0x00, - "ADD" => 0x01, - "MUL" => 0x02, - "SUB" => 0x03, - "DIV" => 0x04, - "SDIV" => 0x05, - "MOD" => 0x06, - "SMOD" => 0x07, - "ADDMOD" => 0x08, - "MULMOD" => 0x09, - "EXP" => 0x0a, - "SIGNEXTEND" => 0x0b, - "ADDFP254" => 0x0c, - "MULFP254" => 0x0d, - "SUBFP254" => 0x0e, - "SUBMOD" => 0x0f, - "LT" => 0x10, - "GT" => 0x11, - "SLT" => 0x12, - "SGT" => 0x13, - "EQ" => 0x14, - "ISZERO" => 0x15, - "AND" => 0x16, - "OR" => 0x17, - "XOR" => 0x18, - "NOT" => 0x19, - "BYTE" => 0x1a, - "SHL" => 0x1b, - "SHR" => 0x1c, - "SAR" => 0x1d, - "KECCAK256" => 0x20, - "KECCAK_GENERAL" => 0x21, - "ADDRESS" => 0x30, - "BALANCE" => 0x31, - "ORIGIN" => 0x32, - "CALLER" => 0x33, - "CALLVALUE" => 0x34, - "CALLDATALOAD" => 0x35, - "CALLDATASIZE" => 0x36, - "CALLDATACOPY" => 0x37, - "CODESIZE" => 0x38, - "CODECOPY" => 0x39, - "GASPRICE" => 0x3a, - "EXTCODESIZE" => 0x3b, - "EXTCODECOPY" => 0x3c, - "RETURNDATASIZE" => 0x3d, - "RETURNDATACOPY" => 0x3e, - "EXTCODEHASH" => 0x3f, - "BLOCKHASH" => 0x40, - "COINBASE" => 0x41, - "TIMESTAMP" => 0x42, - "NUMBER" => 0x43, - "DIFFICULTY" => 0x44, - "GASLIMIT" => 0x45, - "CHAINID" => 0x46, - "BASEFEE" => 0x48, - "PROVER_INPUT" => 0x49, - "POP" => 0x50, - "MLOAD" => 0x51, - "MSTORE" => 0x52, - "MSTORE8" => 0x53, - "SLOAD" => 0x54, - "SSTORE" => 0x55, - "JUMP" => 0x56, - "JUMPI" => 0x57, - "GETPC" => 0x58, - "MSIZE" => 0x59, - "GAS" => 0x5a, - "JUMPDEST" => 0x5b, - "DUP1" => 0x80, - "DUP2" => 0x81, - "DUP3" => 0x82, - "DUP4" => 0x83, - "DUP5" => 0x84, - "DUP6" => 0x85, - "DUP7" => 0x86, - "DUP8" => 0x87, - "DUP9" => 0x88, - "DUP10" => 0x89, - "DUP11" => 0x8a, - "DUP12" => 0x8b, - "DUP13" => 0x8c, - "DUP14" => 0x8d, - "DUP15" => 0x8e, - "DUP16" => 0x8f, - "SWAP1" => 0x90, - "SWAP2" => 0x91, - "SWAP3" => 0x92, - "SWAP4" => 0x93, - "SWAP5" => 0x94, - "SWAP6" => 0x95, - "SWAP7" => 0x96, - "SWAP8" => 0x97, - "SWAP9" => 0x98, - "SWAP10" => 0x99, - "SWAP11" => 0x9a, - "SWAP12" => 0x9b, - "SWAP13" => 0x9c, - "SWAP14" => 0x9d, - "SWAP15" => 0x9e, - "SWAP16" => 0x9f, - "LOG0" => 0xa0, - "LOG1" => 0xa1, - "LOG2" => 0xa2, - "LOG3" => 0xa3, - "LOG4" => 0xa4, - "PANIC" => 0xa5, - "MSTORE_32BYTES_1" => 0xc0, - "MSTORE_32BYTES_2" => 0xc1, - "MSTORE_32BYTES_3" => 0xc2, - "MSTORE_32BYTES_4" => 0xc3, - "MSTORE_32BYTES_5" => 0xc4, - "MSTORE_32BYTES_6" => 0xc5, - "MSTORE_32BYTES_7" => 0xc6, - "MSTORE_32BYTES_8" => 0xc7, - "MSTORE_32BYTES_9" => 0xc8, - "MSTORE_32BYTES_10" => 0xc9, - "MSTORE_32BYTES_11" => 0xca, - "MSTORE_32BYTES_12" => 0xcb, - "MSTORE_32BYTES_13" => 0xcc, - "MSTORE_32BYTES_14" => 0xcd, - "MSTORE_32BYTES_15" => 0xce, - "MSTORE_32BYTES_16" => 0xcf, - "MSTORE_32BYTES_17" => 0xd0, - "MSTORE_32BYTES_18" => 0xd1, - "MSTORE_32BYTES_19" => 0xd2, - "MSTORE_32BYTES_20" => 0xd3, - "MSTORE_32BYTES_21" => 0xd4, - "MSTORE_32BYTES_22" => 0xd5, - "MSTORE_32BYTES_23" => 0xd6, - "MSTORE_32BYTES_24" => 0xd7, - "MSTORE_32BYTES_25" => 0xd8, - "MSTORE_32BYTES_26" => 0xd9, - "MSTORE_32BYTES_27" => 0xda, - "MSTORE_32BYTES_28" => 0xdb, - "MSTORE_32BYTES_29" => 0xdc, - "MSTORE_32BYTES_30" => 0xdd, - "MSTORE_32BYTES_31" => 0xde, - "MSTORE_32BYTES_32" => 0xdf, - "CREATE" => 0xf0, - "CALL" => 0xf1, - "CALLCODE" => 0xf2, - "RETURN" => 0xf3, - "DELEGATECALL" => 0xf4, - "CREATE2" => 0xf5, - "GET_CONTEXT" => 0xf6, - "SET_CONTEXT" => 0xf7, - "MLOAD_32BYTES" => 0xf8, - "EXIT_KERNEL" => 0xf9, - "STATICCALL" => 0xfa, - "MLOAD_GENERAL" => 0xfb, - "MSTORE_GENERAL" => 0xfc, - "REVERT" => 0xfd, - "INVALID" => 0xfe, - "SELFDESTRUCT" => 0xff, - _ => panic!("Unrecognized mnemonic {mnemonic}"), - } -} diff --git a/evm/src/cpu/kernel/optimizer.rs b/evm/src/cpu/kernel/optimizer.rs deleted file mode 100644 index f29c96137b..0000000000 --- a/evm/src/cpu/kernel/optimizer.rs +++ /dev/null @@ -1,285 +0,0 @@ -use ethereum_types::U256; -use Item::{Push, StandardOp}; -use PushTarget::Literal; - -use crate::cpu::kernel::ast::Item::{GlobalLabelDeclaration, LocalLabelDeclaration}; -use crate::cpu::kernel::ast::PushTarget::Label; -use crate::cpu::kernel::ast::{Item, PushTarget}; -use crate::cpu::kernel::cost_estimator::is_code_improved; -use crate::cpu::kernel::utils::{replace_windows, u256_from_bool}; - -pub(crate) fn optimize_asm(code: &mut Vec) { - // Run the optimizer until nothing changes. - loop { - let old_code = code.clone(); - optimize_asm_once(code); - if code == &old_code { - break; - } - } -} - -/// A single optimization pass. -fn optimize_asm_once(code: &mut Vec) { - constant_propagation(code); - identity_operations(code); - no_op_jumps(code); - remove_swapped_pushes(code); - remove_swaps_commutative(code); - remove_ignored_values(code); -} - -/// Constant propagation. -fn constant_propagation(code: &mut Vec) { - // Constant propagation for unary ops: `[PUSH x, UNARYOP] -> [PUSH UNARYOP(x)]` - replace_windows_if_better(code, |window| { - if let [Push(Literal(x)), StandardOp(op)] = window { - match op.as_str() { - "ISZERO" => Some(vec![Push(Literal(u256_from_bool(x.is_zero())))]), - "NOT" => Some(vec![Push(Literal(!x))]), - _ => None, - } - } else { - None - } - }); - - // Constant propagation for binary ops: `[PUSH y, PUSH x, BINOP] -> [PUSH BINOP(x, y)]` - replace_windows_if_better(code, |window| { - if let [Push(Literal(y)), Push(Literal(x)), StandardOp(op)] = window { - match op.as_str() { - "ADD" => Some(x.overflowing_add(y).0), - "SUB" => Some(x.overflowing_sub(y).0), - "MUL" => Some(x.overflowing_mul(y).0), - "DIV" => Some(x.checked_div(y).unwrap_or(U256::zero())), - "MOD" => Some(x.checked_rem(y).unwrap_or(U256::zero())), - "EXP" => Some(x.overflowing_pow(y).0), - "SHL" => Some(y << x), - "SHR" => Some(y >> x), - "AND" => Some(x & y), - "OR" => Some(x | y), - "XOR" => Some(x ^ y), - "LT" => Some(u256_from_bool(x < y)), - "GT" => Some(u256_from_bool(x > y)), - "EQ" => Some(u256_from_bool(x == y)), - "BYTE" => Some(if x < 32.into() { - y.byte(x.as_usize()).into() - } else { - U256::zero() - }), - _ => None, - } - .map(|res| vec![Push(Literal(res))]) - } else { - None - } - }); -} - -/// Remove identity operations, e.g. `[PUSH 1, MUL] -> []`. -fn identity_operations(code: &mut Vec) { - let zero = U256::zero(); - let one = U256::one(); - replace_windows(code, |window| { - if let [Push(Literal(x)), StandardOp(op)] = window { - match op.as_str() { - "ADD" => (x == zero).then_some(vec![]), - "MUL" => (x == one).then_some(vec![]), - "OR" => (x == zero).then_some(vec![]), - "XOR" => (x == zero).then_some(vec![]), - _ => None, - } - } else { - None - } - }) -} - -/// Remove no-op jumps: `[PUSH label, JUMP, label:] -> [label:]`. -fn no_op_jumps(code: &mut Vec) { - replace_windows(code, |window| { - if let [Push(Label(l)), StandardOp(jump), decl] = window - && &jump == "JUMP" - && (decl == LocalLabelDeclaration(l.clone()) || decl == GlobalLabelDeclaration(l)) - { - Some(vec![decl]) - } else { - None - } - }); -} - -/// Remove swaps: `[PUSH x, PUSH y, SWAP1] -> [PUSH y, PUSH x]`. -// Could be generalized to recognize more than two pushes. -fn remove_swapped_pushes(code: &mut Vec) { - replace_windows(code, |window| { - if let [Push(x), Push(y), StandardOp(swap1)] = window - && &swap1 == "SWAP1" - { - Some(vec![Push(y), Push(x)]) - } else { - None - } - }); -} - -/// Remove SWAP1 before a commutative function. -fn remove_swaps_commutative(code: &mut Vec) { - replace_windows(code, |window| { - if let [StandardOp(swap1), StandardOp(f)] = window - && &swap1 == "SWAP1" - { - let commutative = matches!(f.as_str(), "ADD" | "MUL" | "AND" | "OR" | "XOR" | "EQ"); - commutative.then_some(vec![StandardOp(f)]) - } else { - None - } - }); -} - -/// Remove push-pop type patterns, such as: `[DUP1, POP]`. -// Could be extended to other non-side-effecting operations, e.g. [DUP1, ADD, POP] -> [POP]. -fn remove_ignored_values(code: &mut Vec) { - replace_windows(code, |[a, b]| { - if let StandardOp(pop) = b - && &pop == "POP" - { - match a { - Push(_) => Some(vec![]), - StandardOp(dup) if dup.starts_with("DUP") => Some(vec![]), - _ => None, - } - } else { - None - } - }); -} - -/// Like `replace_windows`, but specifically for code, and only makes replacements if our cost -/// estimator thinks that the new code is more efficient. -fn replace_windows_if_better(code: &mut Vec, maybe_replace: F) -where - F: Fn([Item; W]) -> Option>, -{ - replace_windows(code, |window| { - maybe_replace(window.clone()).filter(|suggestion| is_code_improved(&window, suggestion)) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_constant_propagation_iszero() { - let mut code = vec![Push(Literal(3.into())), StandardOp("ISZERO".into())]; - constant_propagation(&mut code); - assert_eq!(code, vec![Push(Literal(0.into()))]); - } - - #[test] - fn test_constant_propagation_add_overflowing() { - let mut code = vec![ - Push(Literal(U256::max_value())), - Push(Literal(U256::max_value())), - StandardOp("ADD".into()), - ]; - constant_propagation(&mut code); - assert_eq!(code, vec![Push(Literal(U256::max_value() - 1))]); - } - - #[test] - fn test_constant_propagation_sub_underflowing() { - let original = vec![ - Push(Literal(U256::one())), - Push(Literal(U256::zero())), - StandardOp("SUB".into()), - ]; - let mut code = original.clone(); - constant_propagation(&mut code); - // Constant propagation could replace the code with [PUSH U256::MAX], but that's actually - // more expensive, so the code shouldn't be changed. - // (The code could also be replaced with [PUSH 0; NOT], which would be an improvement, but - // our optimizer isn't smart enough yet.) - assert_eq!(code, original); - } - - #[test] - fn test_constant_propagation_mul() { - let mut code = vec![ - Push(Literal(3.into())), - Push(Literal(4.into())), - StandardOp("MUL".into()), - ]; - constant_propagation(&mut code); - assert_eq!(code, vec![Push(Literal(12.into()))]); - } - - #[test] - fn test_constant_propagation_div() { - let mut code = vec![ - Push(Literal(3.into())), - Push(Literal(8.into())), - StandardOp("DIV".into()), - ]; - constant_propagation(&mut code); - assert_eq!(code, vec![Push(Literal(2.into()))]); - } - - #[test] - fn test_constant_propagation_div_zero() { - let mut code = vec![ - Push(Literal(0.into())), - Push(Literal(1.into())), - StandardOp("DIV".into()), - ]; - constant_propagation(&mut code); - assert_eq!(code, vec![Push(Literal(0.into()))]); - } - - #[test] - fn test_no_op_jump() { - let mut code = vec![ - Push(Label("mylabel".into())), - StandardOp("JUMP".into()), - LocalLabelDeclaration("mylabel".into()), - ]; - no_op_jumps(&mut code); - assert_eq!(code, vec![LocalLabelDeclaration("mylabel".into())]); - } - - #[test] - fn test_remove_swapped_pushes() { - let mut code = vec![ - Push(Literal("42".into())), - Push(Label("mylabel".into())), - StandardOp("SWAP1".into()), - ]; - remove_swapped_pushes(&mut code); - assert_eq!( - code, - vec![Push(Label("mylabel".into())), Push(Literal("42".into()))] - ); - } - - #[test] - fn test_remove_swap_mul() { - let mut code = vec![StandardOp("SWAP1".into()), StandardOp("MUL".into())]; - remove_swaps_commutative(&mut code); - assert_eq!(code, vec![StandardOp("MUL".into())]); - } - - #[test] - fn test_remove_push_pop() { - let mut code = vec![Push(Literal("42".into())), StandardOp("POP".into())]; - remove_ignored_values(&mut code); - assert_eq!(code, vec![]); - } - - #[test] - fn test_remove_dup_pop() { - let mut code = vec![StandardOp("DUP5".into()), StandardOp("POP".into())]; - remove_ignored_values(&mut code); - assert_eq!(code, vec![]); - } -} diff --git a/evm/src/cpu/kernel/parser.rs b/evm/src/cpu/kernel/parser.rs deleted file mode 100644 index 7864acfe0e..0000000000 --- a/evm/src/cpu/kernel/parser.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::str::FromStr; - -use ethereum_types::U256; -use pest::iterators::Pair; -use pest::Parser; - -use super::ast::{BytesTarget, StackPlaceholder}; -use crate::cpu::kernel::ast::{File, Item, PushTarget, StackReplacement}; - -/// Parses EVM assembly code. -#[derive(pest_derive::Parser)] -#[grammar = "cpu/kernel/evm_asm.pest"] -struct AsmParser; - -pub(crate) fn parse(s: &str) -> File { - let file = AsmParser::parse(Rule::file, s) - .expect("Parsing failed") - .next() - .unwrap(); - let body = file.into_inner().map(parse_item).collect(); - File { body } -} - -fn parse_item(item: Pair) -> Item { - assert_eq!(item.as_rule(), Rule::item); - let item = item.into_inner().next().unwrap(); - match item.as_rule() { - Rule::macro_def => parse_macro_def(item), - Rule::macro_call => parse_macro_call(item), - Rule::repeat => parse_repeat(item), - Rule::stack => parse_stack(item), - Rule::global_label_decl => { - Item::GlobalLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) - } - Rule::local_label_decl => { - Item::LocalLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) - } - Rule::macro_label_decl => { - Item::MacroLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) - } - Rule::bytes_item => Item::Bytes(item.into_inner().map(parse_bytes_target).collect()), - Rule::jumptable_item => { - Item::Jumptable(item.into_inner().map(|i| i.as_str().into()).collect()) - } - Rule::push_instruction => Item::Push(parse_push_target(item.into_inner().next().unwrap())), - Rule::prover_input_instruction => Item::ProverInput( - item.into_inner() - .next() - .unwrap() - .into_inner() - .map(|x| x.as_str().into()) - .collect::>() - .into(), - ), - Rule::nullary_instruction => Item::StandardOp(item.as_str().to_uppercase()), - _ => panic!("Unexpected {:?}", item.as_rule()), - } -} - -fn parse_macro_def(item: Pair) -> Item { - assert_eq!(item.as_rule(), Rule::macro_def); - let mut inner = item.into_inner().peekable(); - - let name = inner.next().unwrap().as_str().into(); - - // The parameter list is optional. - let params = if let Some(Rule::paramlist) = inner.peek().map(|pair| pair.as_rule()) { - let params = inner.next().unwrap().into_inner(); - params.map(|param| param.as_str().to_string()).collect() - } else { - vec![] - }; - - Item::MacroDef(name, params, inner.map(parse_item).collect()) -} - -fn parse_macro_call(item: Pair) -> Item { - assert_eq!(item.as_rule(), Rule::macro_call); - let mut inner = item.into_inner(); - - let name = inner.next().unwrap().as_str().into(); - - // The arg list is optional. - let args = if let Some(arglist) = inner.next() { - assert_eq!(arglist.as_rule(), Rule::macro_arglist); - arglist.into_inner().map(parse_push_target).collect() - } else { - vec![] - }; - - Item::MacroCall(name, args) -} - -fn parse_repeat(item: Pair) -> Item { - assert_eq!(item.as_rule(), Rule::repeat); - let mut inner = item.into_inner(); - let count = parse_literal_u256(inner.next().unwrap()); - Item::Repeat(count, inner.map(parse_item).collect()) -} - -fn parse_stack(item: Pair) -> Item { - assert_eq!(item.as_rule(), Rule::stack); - let mut inner = item.into_inner(); - - let placeholders = inner.next().unwrap(); - assert_eq!(placeholders.as_rule(), Rule::stack_placeholders); - let replacements = inner.next().unwrap(); - assert_eq!(replacements.as_rule(), Rule::stack_replacements); - - let placeholders = placeholders - .into_inner() - .map(parse_stack_placeholder) - .collect(); - let replacements = replacements - .into_inner() - .map(parse_stack_replacement) - .collect(); - Item::StackManipulation(placeholders, replacements) -} - -fn parse_stack_placeholder(target: Pair) -> StackPlaceholder { - assert_eq!(target.as_rule(), Rule::stack_placeholder); - let inner = target.into_inner().next().unwrap(); - match inner.as_rule() { - Rule::identifier => StackPlaceholder(inner.as_str().into(), 1), - Rule::stack_block => { - let mut block = inner.into_inner(); - let identifier = block.next().unwrap().as_str(); - let length = block.next().unwrap().as_str().parse().unwrap(); - StackPlaceholder(identifier.to_string(), length) - } - _ => panic!("Unexpected {:?}", inner.as_rule()), - } -} - -fn parse_stack_replacement(target: Pair) -> StackReplacement { - assert_eq!(target.as_rule(), Rule::stack_replacement); - let inner = target.into_inner().next().unwrap(); - match inner.as_rule() { - Rule::identifier => StackReplacement::Identifier(inner.as_str().into()), - Rule::literal => StackReplacement::Literal(parse_literal_u256(inner)), - Rule::macro_label => { - StackReplacement::MacroLabel(inner.into_inner().next().unwrap().as_str().into()) - } - Rule::variable => { - StackReplacement::MacroVar(inner.into_inner().next().unwrap().as_str().into()) - } - Rule::constant => { - StackReplacement::Constant(inner.into_inner().next().unwrap().as_str().into()) - } - _ => panic!("Unexpected {:?}", inner.as_rule()), - } -} - -fn parse_push_target(target: Pair) -> PushTarget { - assert_eq!(target.as_rule(), Rule::push_target); - let inner = target.into_inner().next().unwrap(); - match inner.as_rule() { - Rule::literal => PushTarget::Literal(parse_literal_u256(inner)), - Rule::identifier => PushTarget::Label(inner.as_str().into()), - Rule::macro_label => { - PushTarget::MacroLabel(inner.into_inner().next().unwrap().as_str().into()) - } - Rule::variable => PushTarget::MacroVar(inner.into_inner().next().unwrap().as_str().into()), - Rule::constant => PushTarget::Constant(inner.into_inner().next().unwrap().as_str().into()), - _ => panic!("Unexpected {:?}", inner.as_rule()), - } -} - -fn parse_bytes_target(target: Pair) -> BytesTarget { - assert_eq!(target.as_rule(), Rule::bytes_target); - let inner = target.into_inner().next().unwrap(); - match inner.as_rule() { - Rule::literal => BytesTarget::Literal(parse_literal_u8(inner)), - Rule::constant => BytesTarget::Constant(inner.into_inner().next().unwrap().as_str().into()), - _ => panic!("Unexpected {:?}", inner.as_rule()), - } -} - -fn parse_literal_u8(literal: Pair) -> u8 { - let literal = literal.into_inner().next().unwrap(); - match literal.as_rule() { - Rule::literal_decimal => { - u8::from_str(literal.as_str()).expect("Failed to parse literal decimal byte") - } - Rule::literal_hex => { - u8::from_str_radix(&parse_hex(literal), 16).expect("Failed to parse literal hex byte") - } - _ => panic!("Unexpected {:?}", literal.as_rule()), - } -} - -fn parse_literal_u256(literal: Pair) -> U256 { - let literal = literal.into_inner().next().unwrap(); - match literal.as_rule() { - Rule::literal_decimal => { - U256::from_dec_str(literal.as_str()).expect("Failed to parse literal decimal") - } - Rule::literal_hex => { - U256::from_str_radix(&parse_hex(literal), 16).expect("Failed to parse literal hex") - } - _ => panic!("Unexpected {:?}", literal.as_rule()), - } -} - -fn parse_hex(hex: Pair) -> String { - let prefix = &hex.as_str()[..2]; - debug_assert!(prefix == "0x" || prefix == "0X"); - hex.as_str()[2..].to_string() -} diff --git a/evm/src/cpu/kernel/stack/mod.rs b/evm/src/cpu/kernel/stack/mod.rs deleted file mode 100644 index 4c7640e474..0000000000 --- a/evm/src/cpu/kernel/stack/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod permutations; -pub mod stack_manipulation; diff --git a/evm/src/cpu/kernel/stack/permutations.rs b/evm/src/cpu/kernel/stack/permutations.rs deleted file mode 100644 index 71304edd0c..0000000000 --- a/evm/src/cpu/kernel/stack/permutations.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! This module contains logic for finding the optimal sequence of swaps to get from one stack state -//! to another, specifically for the case where the source and destination states are permutations -//! of one another. -//! -//! We solve the problem in three steps: -//! 1. Find a permutation `P` such that `P A = B`. -//! 2. If `A` contains duplicates, optimize `P` by reducing the number of cycles. -//! 3. Convert each cycle into a set of `(0 i)` transpositions, which correspond to swap -//! instructions in the EVM. -//! -//! We typically represent a permutation as a sequence of cycles. For example, the permutation -//! `(1 2 3)(1 2)(4 5)` acts as: -//! -//! ```ignore -//! (1 2 3)(1 2)(4 5)[A_0, A_1, A_2, A_3, A_4, A_5] = (1 2 3)(1 2)[A_0, A_1, A_2, A_3, A_5, A_4] -//! = (1 2 3)[A_0, A_2, A_1, A_3, A_5, A_4] -//! = [A_0, A_3, A_2, A_1, A_5, A_4] -//! ``` -//! -//! We typically represent a `(0 i)` transposition as a single scalar `i`. - -use core::hash::Hash; -use std::collections::{HashMap, HashSet}; - -use crate::cpu::kernel::stack::stack_manipulation::{StackItem, StackOp}; - -/// Find the optimal sequence of stack operations to get from `src` to `dst`. Assumes that `src` and -/// `dst` are permutations of one another. -pub(crate) fn get_stack_ops_for_perm(src: &[StackItem], dst: &[StackItem]) -> Vec { - // We store stacks with the tip at the end, but the permutation calls below use the opposite - // convention. They're a bit simpler when SWAP are (0 i) transposes. - let mut src = src.to_vec(); - let mut dst = dst.to_vec(); - src.reverse(); - dst.reverse(); - - let perm = find_permutation(&src, &dst); - let optimized_perm = combine_cycles(perm, &src); - let trans = permutation_to_transpositions(optimized_perm); - transpositions_to_stack_ops(trans) -} - -/// Apply the given permutation to the given list. -#[cfg(test)] -fn apply_perm(permutation: Vec>, mut lst: Vec) -> Vec { - // Run through perm in REVERSE order. - for cycl in permutation.iter().rev() { - let n = cycl.len(); - let last = lst[cycl[n - 1]].clone(); - for i in (0..n - 1).rev() { - let j = (i + 1) % n; - lst[cycl[j]] = lst[cycl[i]].clone(); - } - lst[cycl[0]] = last; - } - lst -} - -/// This function does STEP 1. -/// Given 2 lists A, B find a permutation P such that P . A = B. -pub(crate) fn find_permutation(lst_a: &[T], lst_b: &[T]) -> Vec> { - // We should check to ensure that A and B are indeed rearrangements of each other. - assert!(is_permutation(lst_a, lst_b)); - - let n = lst_a.len(); - - // Keep track of the A_i's which have been already placed into the correct position. - let mut correct_a = HashSet::new(); - - // loc_b is a dictionary where loc_b[b] is the indices i where b = B_i != A_i. - // We need to swap appropriate A_j's into these positions. - let mut loc_b: HashMap> = HashMap::new(); - - for i in 0..n { - if lst_a[i] == lst_b[i] { - // If A_i = B_i, we never do SWAP_i as we are already in the correct position. - correct_a.insert(i); - } else { - loc_b.entry(lst_b[i].clone()).or_default().push(i); - } - } - - // This will be a list of disjoint cycles. - let mut permutation = vec![]; - - // For technical reasons, it's handy to include [0] as a trivial cycle. - // This is because if A_0 = A_i for some other i in a cycle, - // we can save transpositions by expanding the cycle to include 0. - if correct_a.contains(&0) { - permutation.push(vec![0]); - } - - for i in 0..n { - // If i is both not in the correct position and not already in a cycle, it will start a new cycle. - if correct_a.contains(&i) { - continue; - } - - correct_a.insert(i); - let mut cycl = vec![i]; - - // lst_a[i] need to be swapped into an index j such that lst_b[j] = lst_a[i]. - // This exactly means j should be an element of loc_b[lst_a[i]]. - // We pop as each j should only be used once. - // In this step we simply find any permutation. We will improve it to an optimal one in STEP 2. - let mut j = loc_b.get_mut(&lst_a[i]).unwrap().pop().unwrap(); - - // Keep adding elements to the cycle until we return to our initial index - while j != i { - correct_a.insert(j); - cycl.push(j); - j = loc_b.get_mut(&lst_a[j]).unwrap().pop().unwrap(); - } - - permutation.push(cycl); - } - permutation -} - -/// This function does STEP 2. It tests to see if cycles can be combined which might occur if A has duplicates. -fn combine_cycles(mut perm: Vec>, lst_a: &[T]) -> Vec> { - // If perm is a single cycle, there is nothing to combine. - if perm.len() == 1 { - return perm; - } - - let n = lst_a.len(); - - // Need a dictionary to keep track of duplicates in lst_a. - let mut all_a_positions: HashMap> = HashMap::new(); - for i in 0..n { - all_a_positions.entry(lst_a[i].clone()).or_default().push(i); - } - - // For each element a which occurs at positions i1, ..., ij, combine cycles such that all - // ik which occur in a cycle occur in the same cycle. - for positions in all_a_positions.values() { - if positions.len() == 1 { - continue; - } - - let mut joinedperm = vec![]; - let mut newperm = vec![]; - let mut pos = 0; - for cycl in perm { - // Does cycl include an element of positions? - let mut disjoint = true; - - for term in positions { - if cycl.contains(term) { - if joinedperm.is_empty() { - // This is the first cycle we have found including an element of positions. - joinedperm = cycl.clone(); - pos = cycl.iter().position(|x| x == term).unwrap(); - } else { - // Need to merge 2 cycles. If A_i = A_j then the permutations - // (C_1, ..., C_k1, i, C_{k1 + 1}, ... C_k2)(D_1, ..., D_k3, j, D_{k3 + 1}, ... D_k4) - // (C_1, ..., C_k1, i, D_{k3 + 1}, ... D_k4, D_1, ..., D_k3, j, C_{k1 + 1}, ... C_k2) - // lead to the same oupput but the second will require less transpositions. - let newpos = cycl.iter().position(|x| x == term).unwrap(); - joinedperm = [ - &joinedperm[..pos + 1], - &cycl[newpos + 1..], - &cycl[..newpos + 1], - &joinedperm[pos + 1..], - ] - .concat(); - } - disjoint = false; - break; - } - } - if disjoint { - newperm.push(cycl); - } - } - if !joinedperm.is_empty() { - newperm.push(joinedperm); - } - perm = newperm; - } - perm -} - -// This function does STEP 3. Converting all cycles to [0, i] transpositions. -fn permutation_to_transpositions(perm: Vec>) -> Vec { - let mut trans = vec![]; - // The method is pretty simple, we have: - // (0 C_1 ... C_i) = (0 C_i) ... (0 C_1) - // (C_1 ... C_i) = (0 C_1) (0 C_i) ... (0\ C_1). - // We simply need to check to see if 0 is in our cycle to see which one to use. - for cycl in perm { - let n = cycl.len(); - let zero_pos = cycl.iter().position(|x| *x == 0); - if let Some(pos) = zero_pos { - trans.extend((1..n).map(|i| cycl[(n + pos - i) % n])); - } else { - trans.extend((0..=n).map(|i| cycl[(n - i) % n])); - } - } - trans -} - -#[cfg(test)] -fn trans_to_perm(trans: Vec) -> Vec> { - trans.into_iter().map(|i| vec![0, i]).collect() -} - -fn transpositions_to_stack_ops(trans: Vec) -> Vec { - trans.into_iter().map(|i| StackOp::Swap(i as u8)).collect() -} - -pub(crate) fn is_permutation(a: &[T], b: &[T]) -> bool { - make_multiset(a) == make_multiset(b) -} - -fn make_multiset(vals: &[T]) -> HashMap { - let mut counts = HashMap::new(); - for val in vals { - *counts.entry(val.clone()).or_default() += 1; - } - counts -} - -#[cfg(test)] -mod tests { - use rand::prelude::SliceRandom; - use rand::thread_rng; - - use crate::cpu::kernel::stack::permutations::{ - apply_perm, combine_cycles, find_permutation, is_permutation, - permutation_to_transpositions, trans_to_perm, - }; - - #[test] - fn test_combine_cycles() { - assert_eq!( - combine_cycles(vec![vec![0, 2], vec![3, 4]], &['a', 'b', 'c', 'd', 'a']), - vec![vec![0, 3, 4, 2]] - ); - } - - #[test] - fn test_is_permutation() { - assert!(is_permutation(&['a', 'b', 'c'], &['b', 'c', 'a'])); - assert!(!is_permutation(&['a', 'b', 'c'], &['a', 'b', 'b', 'c'])); - assert!(!is_permutation(&['a', 'b', 'c'], &['a', 'd', 'c'])); - } - - #[test] - fn test_all() { - let mut test_lst = vec![ - 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'e', 'f', 'g', 'h', 'k', - ]; - - let mut rng = thread_rng(); - test_lst.shuffle(&mut rng); - for _ in 0..1000 { - let lst_a = test_lst.clone(); - test_lst.shuffle(&mut rng); - let lst_b = test_lst.clone(); - - let perm = find_permutation(&lst_a, &lst_b); - assert_eq!(apply_perm(perm.clone(), lst_a.clone()), lst_b); - - let shortperm = combine_cycles(perm.clone(), &lst_a); - assert_eq!(apply_perm(shortperm.clone(), lst_a.clone()), lst_b); - - let trans = trans_to_perm(permutation_to_transpositions(perm)); - assert_eq!(apply_perm(trans.clone(), lst_a.clone()), lst_b); - - let shorttrans = trans_to_perm(permutation_to_transpositions(shortperm)); - assert_eq!(apply_perm(shorttrans.clone(), lst_a.clone()), lst_b); - - assert!(shorttrans.len() <= trans.len()); - } - } -} diff --git a/evm/src/cpu/kernel/stack/stack_manipulation.rs b/evm/src/cpu/kernel/stack/stack_manipulation.rs deleted file mode 100644 index a7b376c5ea..0000000000 --- a/evm/src/cpu/kernel/stack/stack_manipulation.rs +++ /dev/null @@ -1,373 +0,0 @@ -use core::cmp::Ordering; -use core::hash::Hash; -use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::collections::{BinaryHeap, HashMap}; - -use itertools::Itertools; - -use crate::cpu::columns::NUM_CPU_COLUMNS; -use crate::cpu::kernel::assembler::BYTES_PER_OFFSET; -use crate::cpu::kernel::ast::{Item, PushTarget, StackPlaceholder, StackReplacement}; -use crate::cpu::kernel::stack::permutations::{get_stack_ops_for_perm, is_permutation}; -use crate::cpu::kernel::stack::stack_manipulation::StackOp::Pop; -use crate::cpu::kernel::utils::u256_to_trimmed_be_bytes; -use crate::memory; - -pub(crate) fn expand_stack_manipulation(body: Vec) -> Vec { - let mut expanded = vec![]; - for item in body { - if let Item::StackManipulation(names, replacements) = item { - expanded.extend(expand(names, replacements)); - } else { - expanded.push(item); - } - } - expanded -} - -fn expand(names: Vec, replacements: Vec) -> Vec { - let mut stack_blocks = HashMap::new(); - - let mut src = names - .iter() - .cloned() - .flat_map(|StackPlaceholder(name, n)| { - stack_blocks.insert(name.clone(), n); - (0..n) - .map(|i| { - let literal_name = format!("@{name}.{i}"); - StackItem::NamedItem(literal_name) - }) - .collect_vec() - }) - .collect_vec(); - - let mut dst = replacements - .into_iter() - .flat_map(|item| match item { - StackReplacement::Literal(n) => vec![StackItem::PushTarget(PushTarget::Literal(n))], - StackReplacement::Identifier(name) => { - // May be either a named item or a label. Named items have precedence. - if stack_blocks.contains_key(&name) { - let n = *stack_blocks.get(&name).unwrap(); - (0..n) - .map(|i| { - let literal_name = format!("@{name}.{i}"); - StackItem::NamedItem(literal_name) - }) - .collect_vec() - } else { - vec![StackItem::PushTarget(PushTarget::Label(name))] - } - } - StackReplacement::Label(name) => vec![StackItem::PushTarget(PushTarget::Label(name))], - StackReplacement::MacroLabel(_) - | StackReplacement::MacroVar(_) - | StackReplacement::Constant(_) => { - panic!("Should have been expanded already: {item:?}") - } - }) - .collect_vec(); - - // %stack uses our convention where the top item is written on the left side. - // `shortest_path` expects the opposite, so we reverse src and dst. - src.reverse(); - dst.reverse(); - - let unique_push_targets = dst - .iter() - .filter_map(|item| match item { - StackItem::PushTarget(target) => Some(target.clone()), - _ => None, - }) - .unique() - .collect_vec(); - - let path = shortest_path(src, dst, unique_push_targets); - path.into_iter().map(StackOp::into_item).collect() -} - -/// Finds the lowest-cost sequence of `StackOp`s that transforms `src` to `dst`. -/// Uses a variant of Dijkstra's algorithm. -fn shortest_path( - src: Vec, - dst: Vec, - unique_push_targets: Vec, -) -> Vec { - // Nodes to visit, starting with the lowest-cost node. - let mut queue = BinaryHeap::new(); - queue.push(Node { - stack: src.clone(), - cost: 0, - }); - - // For each node, stores `(best_cost, Option<(parent, op)>)`. - let mut node_info = HashMap::, (u32, Option<(Vec, StackOp)>)>::new(); - node_info.insert(src.clone(), (0, None)); - - while let Some(node) = queue.pop() { - if node.stack == dst { - // The destination is now the lowest-cost node, so we must have found the best path. - let mut path = vec![]; - let mut stack = &node.stack; - // Rewind back to src, recording a list of operations which will be backwards. - while let Some((parent, op)) = &node_info[stack].1 { - stack = parent; - path.push(op.clone()); - } - assert_eq!(stack, &src); - path.reverse(); - return path; - } - - let (best_cost, _) = node_info[&node.stack]; - if best_cost < node.cost { - // Since we can't efficiently remove nodes from the heap, it can contain duplicates. - // In this case, we've already visited this stack state with a lower cost. - continue; - } - - for op in next_ops(&node.stack, &dst, &unique_push_targets) { - let neighbor = match op.apply_to(node.stack.clone()) { - Some(n) => n, - None => continue, - }; - - let cost = node.cost + op.cost(); - let entry = node_info.entry(neighbor.clone()); - if let Occupied(e) = &entry - && e.get().0 <= cost - { - // We already found a better or equal path. - continue; - } - - let neighbor_info = (cost, Some((node.stack.clone(), op.clone()))); - match entry { - Occupied(mut e) => { - e.insert(neighbor_info); - } - Vacant(e) => { - e.insert(neighbor_info); - } - } - - queue.push(Node { - stack: neighbor, - cost, - }); - } - } - - panic!("No path found from {src:?} to {dst:?}") -} - -/// A node in the priority queue used by Dijkstra's algorithm. -#[derive(Eq, PartialEq)] -struct Node { - stack: Vec, - cost: u32, -} - -impl PartialOrd for Node { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Node { - fn cmp(&self, other: &Self) -> Ordering { - // We want a min-heap rather than the default max-heap, so this is the opposite of the - // natural ordering of costs. - other.cost.cmp(&self.cost) - } -} - -/// Like `StackReplacement`, but without constants or macro vars, since those were expanded already. -#[derive(Eq, PartialEq, Hash, Clone, Debug)] -pub(crate) enum StackItem { - NamedItem(String), - PushTarget(PushTarget), -} - -#[derive(Clone, Debug)] -pub(crate) enum StackOp { - Push(PushTarget), - Pop, - Dup(u8), - Swap(u8), -} - -/// A set of candidate operations to consider for the next step in the path from `src` to `dst`. -fn next_ops( - src: &[StackItem], - dst: &[StackItem], - unique_push_targets: &[PushTarget], -) -> Vec { - if let Some(top) = src.last() - && !dst.contains(top) - { - // If the top of src doesn't appear in dst, don't bother with anything other than a POP. - return vec![StackOp::Pop]; - } - - if is_permutation(src, dst) { - // The transpositions are right-associative, so the last one gets applied first, hence pop. - return vec![get_stack_ops_for_perm(src, dst).pop().unwrap()]; - } - - let mut ops = vec![StackOp::Pop]; - - ops.extend( - unique_push_targets - .iter() - // Only consider pushing this target if we need more occurrences of it, otherwise swaps - // will be a better way to rearrange the existing occurrences as needed. - .filter(|push_target| { - let item = StackItem::PushTarget((*push_target).clone()); - let src_count = src.iter().filter(|x| **x == item).count(); - let dst_count = dst.iter().filter(|x| **x == item).count(); - src_count < dst_count - }) - .cloned() - .map(StackOp::Push), - ); - - let src_len = src.len() as u8; - - ops.extend( - (1..=src_len) - // Only consider duplicating this item if we need more occurrences of it, otherwise swaps - // will be a better way to rearrange the existing occurrences as needed. - .filter(|i| { - let item = &src[src.len() - *i as usize]; - let src_count = src.iter().filter(|x| *x == item).count(); - let dst_count = dst.iter().filter(|x| *x == item).count(); - src_count < dst_count - }) - .map(StackOp::Dup), - ); - - ops.extend( - (1..src_len) - .filter(|i| should_try_swap(src, dst, *i)) - .map(StackOp::Swap), - ); - - ops -} - -/// Whether we should consider `SWAP_i` in the search. -fn should_try_swap(src: &[StackItem], dst: &[StackItem], i: u8) -> bool { - if src.is_empty() { - return false; - } - - let i = i as usize; - let i_from = src.len() - 1; - let i_to = i_from - i; - - // Only consider a swap if it places one of the two affected elements in the desired position. - let top_correct_pos = i_to < dst.len() && src[i_from] == dst[i_to]; - let other_correct_pos = i_from < dst.len() && src[i_to] == dst[i_from]; - top_correct_pos | other_correct_pos -} - -impl StackOp { - fn cost(&self) -> u32 { - let (cpu_rows, memory_rows) = match self { - StackOp::Push(target) => { - let bytes = match target { - PushTarget::Literal(n) => u256_to_trimmed_be_bytes(n).len() as u32, - PushTarget::Label(_) => BYTES_PER_OFFSET as u32, - PushTarget::MacroLabel(_) - | PushTarget::MacroVar(_) - | PushTarget::Constant(_) => { - panic!("Target should have been expanded already: {target:?}") - } - }; - // A PUSH takes one cycle, and 1 memory read per byte. - (1, bytes + 1) - } - // A POP takes one cycle, and most of the time a read to update the top of the stack. - Pop => (1, 1), - // A DUP takes one cycle, and a read and a write. - StackOp::Dup(_) => (1, 2), - // A SWAP takes one cycle with three memory ops, to read both values then write to them. - StackOp::Swap(_) => (1, 3), - }; - - let cpu_cost = cpu_rows * NUM_CPU_COLUMNS as u32; - let memory_cost = memory_rows * memory::columns::NUM_COLUMNS as u32; - cpu_cost + memory_cost - } - - /// Returns an updated stack after this operation is performed, or `None` if this operation - /// would not be valid on the given stack. - fn apply_to(&self, mut stack: Vec) -> Option> { - let len = stack.len(); - match self { - StackOp::Push(target) => { - stack.push(StackItem::PushTarget(target.clone())); - } - Pop => { - stack.pop()?; - } - StackOp::Dup(n) => { - let idx = len.checked_sub(*n as usize)?; - stack.push(stack[idx].clone()); - } - StackOp::Swap(n) => { - let from = len.checked_sub(1)?; - let to = len.checked_sub(*n as usize + 1)?; - stack.swap(from, to); - } - } - Some(stack) - } - - fn into_item(self) -> Item { - match self { - StackOp::Push(target) => Item::Push(target), - Pop => Item::StandardOp("POP".into()), - StackOp::Dup(n) => Item::StandardOp(format!("DUP{n}")), - StackOp::Swap(n) => Item::StandardOp(format!("SWAP{n}")), - } - } -} - -#[cfg(test)] -mod tests { - use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; - - use crate::cpu::kernel::stack::stack_manipulation::StackItem::NamedItem; - use crate::cpu::kernel::stack::stack_manipulation::{shortest_path, StackItem}; - - #[test] - fn test_shortest_path() { - init_logger(); - shortest_path( - vec![named("ret"), named("a"), named("b"), named("d")], - vec![named("ret"), named("b"), named("a")], - vec![], - ); - } - - #[test] - fn test_shortest_path_permutation() { - init_logger(); - shortest_path( - vec![named("a"), named("b"), named("c")], - vec![named("c"), named("a"), named("b")], - vec![], - ); - } - - fn named(name: &str) -> StackItem { - NamedItem(name.into()) - } - - fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug")); - } -} diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs deleted file mode 100644 index b3a075cf7a..0000000000 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ /dev/null @@ -1,474 +0,0 @@ -use std::collections::HashMap; - -use anyhow::Result; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2::field::types::Field; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata::{self, GasLimit}; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::mpt::nibbles_64; -use crate::generation::mpt::{load_all_mpts, AccountRlp}; -use crate::generation::TrieInputs; -use crate::memory::segments::Segment; -use crate::witness::memory::MemoryAddress; -use crate::witness::operation::CONTEXT_SCALING_FACTOR; -use crate::Node; - -pub(crate) fn initialize_mpts( - interpreter: &mut Interpreter, - trie_inputs: &TrieInputs, -) { - // Load all MPTs. - let (trie_root_ptrs, trie_data) = - load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - - let state_addr = - MemoryAddress::new_bundle((GlobalMetadata::StateTrieRoot as usize).into()).unwrap(); - let txn_addr = - MemoryAddress::new_bundle((GlobalMetadata::TransactionTrieRoot as usize).into()).unwrap(); - let receipts_addr = - MemoryAddress::new_bundle((GlobalMetadata::ReceiptTrieRoot as usize).into()).unwrap(); - let len_addr = - MemoryAddress::new_bundle((GlobalMetadata::TrieDataSize as usize).into()).unwrap(); - - let to_set = [ - (state_addr, trie_root_ptrs.state_root_ptr.into()), - (txn_addr, trie_root_ptrs.txn_root_ptr.into()), - (receipts_addr, trie_root_ptrs.receipt_root_ptr.into()), - (len_addr, trie_data.len().into()), - ]; - - interpreter.set_memory_multi_addresses(&to_set); - - for (i, data) in trie_data.iter().enumerate() { - let trie_addr = MemoryAddress::new(0, Segment::TrieData, i); - interpreter - .generation_state - .memory - .set(trie_addr, data.into()); - } -} - -// Test account with a given code hash. -fn test_account(code: &[u8]) -> AccountRlp { - AccountRlp { - nonce: U256::from(1111), - balance: U256::from(2222), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak(code), - } -} - -fn random_code() -> Vec { - let mut rng = thread_rng(); - let num_bytes = rng.gen_range(0..1000); - (0..num_bytes).map(|_| rng.gen()).collect() -} - -// Stolen from `tests/mpt/insert.rs` -// Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( - interpreter: &mut Interpreter, - address: Address, - account: &AccountRlp, -) -> Result<()> { - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let mut state_trie: HashedPartialTrie = Default::default(); - let trie_inputs = Default::default(); - - initialize_mpts(interpreter, &trie_inputs); - - let k = nibbles_64(U256::from_big_endian( - keccak(address.to_fixed_bytes()).as_bytes(), - )); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; - let trie_data = interpreter.get_trie_data_mut(); - if trie_data.is_empty() { - // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. - // Since we don't explicitly set it to 0, we need to do so here. - trie_data.push(0.into()); - } - let value_ptr = trie_data.len(); - trie_data.push(account.nonce); - trie_data.push(account.balance); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(H256::zero().into_uint()); - trie_data.push(account.code_hash.into_uint()); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into_u256().unwrap()) - .expect("The stack should not overflow"); // key - - interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 0, - "Expected empty stack after insert, found {:?}", - interpreter.stack() - ); - - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - let hash = H256::from_uint(&interpreter.stack()[1]); - - state_trie.insert(k, rlp::encode(account).to_vec()); - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(hash, expected_state_trie_hash); - - Ok(()) -} - -#[test] -fn test_extcodesize() -> Result<()> { - let code = random_code(); - let account = test_account(&code); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); - let address: Address = thread_rng().gen(); - // Prepare the interpreter by inserting the account in the state trie. - prepare_interpreter(&mut interpreter, address, &account)?; - - let extcodesize = KERNEL.global_labels["extcodesize"]; - - // Test `extcodesize` - interpreter.generation_state.registers.program_counter = extcodesize; - interpreter.pop().expect("The stack should not be empty"); - interpreter.pop().expect("The stack should not be empty"); - assert!(interpreter.stack().is_empty()); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(U256::from_big_endian(address.as_bytes())) - .expect("The stack should not overflow"); - interpreter.generation_state.inputs.contract_code = - HashMap::from([(keccak(&code), code.clone())]); - interpreter.run()?; - - assert_eq!(interpreter.stack(), vec![code.len().into()]); - - Ok(()) -} - -#[test] -fn test_extcodecopy() -> Result<()> { - let code = random_code(); - let account = test_account(&code); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); - let address: Address = thread_rng().gen(); - // Prepare the interpreter by inserting the account in the state trie. - prepare_interpreter(&mut interpreter, address, &account)?; - - let context = interpreter.context(); - interpreter.generation_state.memory.contexts[context].segments - [Segment::ContextMetadata.unscale()] - .set(GasLimit.unscale(), U256::from(1000000000000u64)); - - let extcodecopy = KERNEL.global_labels["sys_extcodecopy"]; - - // Put random data in main memory and the `KernelAccountCode` segment for realism. - let mut rng = thread_rng(); - for i in 0..2000 { - interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory.unscale()] - .set(i, U256::from(rng.gen::())); - interpreter.generation_state.memory.contexts[context].segments - [Segment::KernelAccountCode.unscale()] - .set(i, U256::from(rng.gen::())); - } - - // Random inputs - let dest_offset = rng.gen_range(0..3000); - let offset = rng.gen_range(0..1500); - let size = rng.gen_range(0..1500); - - // Test `extcodecopy` - interpreter.generation_state.registers.program_counter = extcodecopy; - interpreter.pop().expect("The stack should not be empty"); - interpreter.pop().expect("The stack should not be empty"); - assert!(interpreter.stack().is_empty()); - interpreter - .push(size.into()) - .expect("The stack should not overflow"); - interpreter - .push(offset.into()) - .expect("The stack should not overflow"); - interpreter - .push(dest_offset.into()) - .expect("The stack should not overflow"); - interpreter - .push(U256::from_big_endian(address.as_bytes())) - .expect("The stack should not overflow"); - interpreter - .push((0xDEADBEEFu64 + (1 << 32)).into()) - .expect("The stack should not overflow"); // kexit_info - interpreter.generation_state.inputs.contract_code = - HashMap::from([(keccak(&code), code.clone())]); - interpreter.run()?; - - assert!(interpreter.stack().is_empty()); - // Check that the code was correctly copied to memory. - for i in 0..size { - let memory = interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory.unscale()] - .get(dest_offset + i); - assert_eq!( - memory, - code.get(offset + i).copied().unwrap_or_default().into() - ); - } - - Ok(()) -} - -/// Prepare the interpreter for storage tests by inserting all necessary accounts -/// in the state trie, adding the code we want to context 1 and switching the context. -fn prepare_interpreter_all_accounts( - interpreter: &mut Interpreter, - trie_inputs: TrieInputs, - addr: [u8; 20], - code: &[u8], -) -> Result<()> { - // Load all MPTs. - initialize_mpts(interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Switch context and initialize memory with the data we need for the tests. - interpreter.generation_state.registers.program_counter = 0; - interpreter.set_code(1, code.to_vec()); - interpreter.set_context_metadata_field( - 1, - ContextMetadata::Address, - U256::from_big_endian(&addr), - ); - interpreter.set_context_metadata_field(1, ContextMetadata::GasLimit, 100_000.into()); - interpreter.set_context(1); - interpreter.set_is_kernel(false); - interpreter.set_context_metadata_field( - 1, - ContextMetadata::ParentProgramCounter, - 0xdeadbeefu32.into(), - ); - interpreter.set_context_metadata_field( - 1, - ContextMetadata::ParentContext, - U256::one() << CONTEXT_SCALING_FACTOR, // ctx = 1 - ); - - Ok(()) -} - -/// Tests an SSTORE within a code similar to the contract code in add11_yml. -#[test] -fn sstore() -> Result<()> { - // We take the same `to` account as in add11_yml. - let addr = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let addr_hashed = keccak(addr); - - let addr_nibbles = Nibbles::from_bytes_be(addr_hashed.as_bytes()).unwrap(); - - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); - - let account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - - state_trie_before.insert(addr_nibbles, rlp::encode(&account_before).to_vec()); - - let trie_inputs = TrieInputs { - state_trie: state_trie_before.clone(), - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(addr_hashed, Node::Empty.into())], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - - // Prepare the interpreter by inserting the account in the state trie. - prepare_interpreter_all_accounts(&mut interpreter, trie_inputs, addr, &code)?; - - interpreter.run()?; - - // The first two elements in the stack are `success` and `leftover_gas`, - // returned by the `sys_stop` opcode. - interpreter.pop().expect("Stack should not be empty"); - interpreter.pop().expect("Stack should not be empty"); - - // The code should have added an element to the storage of `to_account`. We run - // `mpt_hash_state_trie` to check that. - let account_after = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), - ..AccountRlp::default() - }; - // Now, execute mpt_hash_state_trie. - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.set_is_kernel(true); - interpreter.set_context(0); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - - let hash = H256::from_uint(&interpreter.stack()[1]); - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert(addr_nibbles, rlp::encode(&account_after).to_vec()); - - let expected_state_trie_hash = expected_state_trie_after.hash(); - assert_eq!(hash, expected_state_trie_hash); - Ok(()) -} - -/// Tests an SLOAD within a code similar to the contract code in add11_yml. -#[test] -fn sload() -> Result<()> { - // We take the same `to` account as in add11_yml. - let addr = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let addr_hashed = keccak(addr); - - let addr_nibbles = Nibbles::from_bytes_be(addr_hashed.as_bytes()).unwrap(); - - // This code is similar to the one in add11_yml's contract, but we pop the added value - // and carry out an SLOAD instead of an SSTORE. We also add a PUSH at the end. - let code = [ - 0x60, 0x01, 0x60, 0x01, 0x01, 0x50, 0x60, 0x00, 0x54, 0x60, 0x03, 0x00, - ]; - let code_hash = keccak(code); - - let account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - - state_trie_before.insert(addr_nibbles, rlp::encode(&account_before).to_vec()); - - let trie_inputs = TrieInputs { - state_trie: state_trie_before.clone(), - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(addr_hashed, Node::Empty.into())], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - - // Prepare the interpreter by inserting the account in the state trie. - prepare_interpreter_all_accounts(&mut interpreter, trie_inputs, addr, &code)?; - interpreter.run()?; - - // The first two elements in the stack are `success` and `leftover_gas`, - // returned by the `sys_stop` opcode. - interpreter - .pop() - .expect("The stack length should not be empty."); - interpreter - .pop() - .expect("The stack length should not be empty."); - - // The SLOAD in the provided code should return 0, since - // the storage trie is empty. The last step in the code - // pushes the value 3. - assert_eq!(interpreter.stack(), vec![0x0.into(), 0x3.into()]); - interpreter - .pop() - .expect("The stack length should not be empty."); - interpreter - .pop() - .expect("The stack length should not be empty."); - // Now, execute mpt_hash_state_trie. We check that the state trie has not changed. - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter.set_is_kernel(true); - interpreter.set_context(0); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow."); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow."); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - - let trie_data_segment_len = interpreter.stack()[0]; - assert_eq!( - trie_data_segment_len, - interpreter - .get_memory_segment(Segment::TrieData) - .len() - .into() - ); - - let hash = H256::from_uint(&interpreter.stack()[1]); - - let expected_state_trie_hash = state_trie_before.hash(); - assert_eq!(hash, expected_state_trie_hash); - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs deleted file mode 100644 index de5450c5ce..0000000000 --- a/evm/src/cpu/kernel/tests/add11.rs +++ /dev/null @@ -1,312 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; - -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use crate::generation::TrieInputs; -use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use crate::GenerationInputs; - -#[test] -fn test_add11_yml() { - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); - let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], - }; - - let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - - let gas_used = 0xa868u64.into(); - - let expected_state_trie_after = { - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_after = AccountRlp { - balance: 0xde0b6b3a75be550u64.into(), - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - balance: 0xde0b6b3a76586a0u64.into(), - code_hash, - // Storage map: { 0 => 2 } - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), - ..AccountRlp::default() - }; - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after - }; - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used, - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: gas_used, - block_bloom: [0.into(); 8], - }; - - let tries_inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code: contract_code.clone(), - block_metadata, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used, - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = - Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); - - let route_txn_label = KERNEL.global_labels["main"]; - // Switch context and initialize memory with the data we need for the tests. - interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); - interpreter.set_is_kernel(true); - interpreter.run().expect("Proving add11 failed."); -} - -#[test] -fn test_add11_yml_with_exception() { - // In this test, we make sure that the user code throws a stack underflow exception. - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); - let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x8e, 0x00]; - let code_hash = keccak(code); - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], - }; - - let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - let txn_gas_limit = 400_000; - let gas_price = 10; - - // Here, since the transaction fails, it consumes its gas limit, and does nothing else. - let expected_state_trie_after = { - let beneficiary_account_after = beneficiary_account_before; - // This is the only account that changes: the nonce and the balance are updated. - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - txn_gas_limit * gas_price, - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = to_account_before; - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after - }; - - let receipt_0 = LegacyReceiptRlp { - status: false, - cum_gas_used: txn_gas_limit.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: txn_gas_limit.into(), - block_bloom: [0.into(); 8], - }; - - let tries_inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code: contract_code.clone(), - block_metadata, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: txn_gas_limit.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = - Interpreter::new_with_generation_inputs_and_kernel(0, initial_stack, tries_inputs); - - let route_txn_label = KERNEL.global_labels["main"]; - // Switch context and initialize memory with the data we need for the tests. - interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); - interpreter.set_is_kernel(true); - interpreter - .run() - .expect("Proving add11 with exception failed."); -} diff --git a/evm/src/cpu/kernel/tests/balance.rs b/evm/src/cpu/kernel/tests/balance.rs deleted file mode 100644 index af190ae4ce..0000000000 --- a/evm/src/cpu/kernel/tests/balance.rs +++ /dev/null @@ -1,133 +0,0 @@ -use anyhow::Result; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2::field::types::Field; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::nibbles_64; -use crate::generation::mpt::AccountRlp; -use crate::Node; - -// Test account with a given code hash. -fn test_account(balance: U256) -> AccountRlp { - AccountRlp { - nonce: U256::from(1111), - balance, - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: H256::from_uint(&U256::from(8888)), - } -} - -// Stolen from `tests/mpt/insert.rs` -// Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( - interpreter: &mut Interpreter, - address: Address, - account: &AccountRlp, -) -> Result<()> { - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let mut state_trie: HashedPartialTrie = Default::default(); - let trie_inputs = Default::default(); - - initialize_mpts(interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let k = nibbles_64(U256::from_big_endian( - keccak(address.to_fixed_bytes()).as_bytes(), - )); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; - let trie_data = interpreter.get_trie_data_mut(); - if trie_data.is_empty() { - // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. - // Since we don't explicitly set it to 0, we need to do so here. - trie_data.push(0.into()); - } - let value_ptr = trie_data.len(); - trie_data.push(account.nonce); - trie_data.push(account.balance); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(H256::zero().into_uint()); - trie_data.push(account.code_hash.into_uint()); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into_u256().unwrap()) - .expect("The stack should not overflow"); // key - - interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 0, - "Expected empty stack after insert, found {:?}", - interpreter.stack() - ); - - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial trie data segment size, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - let hash = H256::from_uint(&interpreter.stack()[1]); - - state_trie.insert(k, rlp::encode(account).to_vec()); - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(hash, expected_state_trie_hash); - - Ok(()) -} - -#[test] -fn test_balance() -> Result<()> { - let mut rng = thread_rng(); - let balance = U256(rng.gen()); - let account = test_account(balance); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); - let address: Address = rng.gen(); - // Prepare the interpreter by inserting the account in the state trie. - prepare_interpreter(&mut interpreter, address, &account)?; - - // Test `balance` - interpreter.generation_state.registers.program_counter = KERNEL.global_labels["balance"]; - interpreter.pop().expect("The stack should not be empty"); - interpreter.pop().expect("The stack should not be empty"); - assert!(interpreter.stack().is_empty()); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(U256::from_big_endian(address.as_bytes())) - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!(interpreter.stack(), vec![balance]); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/bignum/mod.rs b/evm/src/cpu/kernel/tests/bignum/mod.rs deleted file mode 100644 index cc0e47af3b..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/mod.rs +++ /dev/null @@ -1,593 +0,0 @@ -use core::cmp::Ordering; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::path::PathBuf; - -use anyhow::Result; -use ethereum_types::U256; -use itertools::Itertools; -use num::{BigUint, One, Zero}; -use num_bigint::RandBigInt; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2_util::ceil_div_usize; -use rand::Rng; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint}; - -const BIGNUM_LIMB_BITS: usize = 128; -const MINUS_ONE: U256 = U256::MAX; - -const TEST_DATA_BIGNUM_INPUTS: &str = "bignum_inputs"; -const TEST_DATA_U128_INPUTS: &str = "u128_inputs"; - -const TEST_DATA_SHR_OUTPUTS: &str = "shr_outputs"; -const TEST_DATA_ISZERO_OUTPUTS: &str = "iszero_outputs"; -const TEST_DATA_CMP_OUTPUTS: &str = "cmp_outputs"; -const TEST_DATA_ADD_OUTPUTS: &str = "add_outputs"; -const TEST_DATA_ADDMUL_OUTPUTS: &str = "addmul_outputs"; -const TEST_DATA_MUL_OUTPUTS: &str = "mul_outputs"; -const TEST_DATA_MODMUL_OUTPUTS: &str = "modmul_outputs"; -const TEST_DATA_MODEXP_OUTPUTS: &str = "modexp_outputs"; -const TEST_DATA_MODEXP_OUTPUTS_FULL: &str = "modexp_outputs_full"; - -const BIT_SIZES_TO_TEST: [usize; 15] = [ - 0, 1, 2, 127, 128, 129, 255, 256, 257, 512, 1000, 1023, 1024, 1025, 31415, -]; - -fn full_path(filename: &str) -> PathBuf { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("src/cpu/kernel/tests/bignum/test_data"); - path.push(filename); - path -} - -fn test_data_biguint(filename: &str) -> Vec { - let file = File::open(full_path(filename)).unwrap(); - let lines = BufReader::new(file).lines(); - lines - .map(|line| BigUint::parse_bytes(line.unwrap().as_bytes(), 10).unwrap()) - .collect() -} - -fn test_data_u128(filename: &str) -> Vec { - let file = File::open(full_path(filename)).unwrap(); - let lines = BufReader::new(file).lines(); - lines - .map(|line| line.unwrap().parse::().unwrap()) - .collect() -} - -fn test_data_u256(filename: &str) -> Vec { - let file = File::open(full_path(filename)).unwrap(); - let lines = BufReader::new(file).lines(); - lines - .map(|line| U256::from_dec_str(&line.unwrap()).unwrap()) - .collect() -} - -// Convert each biguint to a vector of bignum limbs, pad to the given length, and concatenate. -fn pad_bignums(biguints: &[BigUint], length: usize) -> Vec { - biguints - .iter() - .flat_map(|biguint| { - biguint_to_mem_vec(biguint.clone()) - .into_iter() - .pad_using(length, |_| U256::zero()) - }) - .collect() -} - -fn gen_bignum(bit_size: usize) -> BigUint { - let mut rng = rand::thread_rng(); - rng.gen_biguint(bit_size as u64) -} - -fn max_bignum(bit_size: usize) -> BigUint { - (BigUint::one() << bit_size) - BigUint::one() -} - -fn bignum_len(a: &BigUint) -> usize { - ceil_div_usize(a.bits() as usize, BIGNUM_LIMB_BITS) -} - -fn run_test(fn_label: &str, memory: Vec, stack: Vec) -> Result<(Vec, Vec)> { - let fn_label = KERNEL.global_labels[fn_label]; - let retdest = 0xDEADBEEFu32.into(); - - let mut initial_stack: Vec = stack; - initial_stack.push(retdest); - initial_stack.reverse(); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(fn_label, initial_stack); - interpreter.set_current_general_memory(memory); - interpreter.run()?; - - let new_memory = interpreter.get_current_general_memory(); - - Ok((new_memory, interpreter.stack().to_vec())) -} - -fn test_shr_bignum(input: BigUint, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&input); - let memory = biguint_to_mem_vec(input); - - let input_start_loc = 0; - let (new_memory, _new_stack) = run_test( - "shr_bignum", - memory, - vec![len.into(), input_start_loc.into()], - )?; - - let output = mem_vec_to_biguint(&new_memory[input_start_loc..input_start_loc + len]); - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_iszero_bignum(input: BigUint, expected_output: U256) -> Result<()> { - let len = bignum_len(&input); - let memory = biguint_to_mem_vec(input); - - let input_start_loc = 0; - let (_new_memory, new_stack) = run_test( - "iszero_bignum", - memory, - vec![len.into(), input_start_loc.into()], - )?; - - let output = new_stack[0]; - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_cmp_bignum(a: BigUint, b: BigUint, expected_output: U256) -> Result<()> { - let len = bignum_len(&a).max(bignum_len(&b)); - let memory = pad_bignums(&[a, b], len); - - let a_start_loc = 0; - let b_start_loc = len; - let (_new_memory, new_stack) = run_test( - "cmp_bignum", - memory, - vec![len.into(), a_start_loc.into(), b_start_loc.into()], - )?; - - let output = new_stack[0]; - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_add_bignum(a: BigUint, b: BigUint, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&a).max(bignum_len(&b)); - let memory = pad_bignums(&[a, b], len); - - let a_start_loc = 0; - let b_start_loc = len; - let (mut new_memory, new_stack) = run_test( - "add_bignum", - memory, - vec![len.into(), a_start_loc.into(), b_start_loc.into()], - )?; - - // Determine actual sum, appending the final carry if nonzero. - let carry_limb = new_stack[0]; - if carry_limb > 0.into() { - new_memory[len] = carry_limb; - } - - let expected_output = biguint_to_mem_vec(expected_output); - let output = &new_memory[a_start_loc..a_start_loc + expected_output.len()]; - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_addmul_bignum(a: BigUint, b: BigUint, c: u128, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&a).max(bignum_len(&b)); - let mut memory = pad_bignums(&[a, b], len); - memory.splice(len..len, [0.into(); 2].iter().cloned()); - - let a_start_loc = 0; - let b_start_loc = len + 2; - let (mut new_memory, new_stack) = run_test( - "addmul_bignum", - memory, - vec![len.into(), a_start_loc.into(), b_start_loc.into(), c.into()], - )?; - - // Determine actual sum, appending the final carry if nonzero. - let carry_limb = new_stack[0]; - if carry_limb > 0.into() { - new_memory[len] = carry_limb; - } - - let expected_output = biguint_to_mem_vec(expected_output); - let output = &new_memory[a_start_loc..a_start_loc + expected_output.len()]; - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_mul_bignum(a: BigUint, b: BigUint, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&a).max(bignum_len(&b)); - let output_len = len * 2; - let memory = pad_bignums(&[a, b], len); - - let a_start_loc = 0; - let b_start_loc = len; - let output_start_loc = 2 * len; - let (new_memory, _new_stack) = run_test( - "mul_bignum", - memory, - vec![ - len.into(), - a_start_loc.into(), - b_start_loc.into(), - output_start_loc.into(), - ], - )?; - - let output = mem_vec_to_biguint(&new_memory[output_start_loc..output_start_loc + output_len]); - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_modmul_bignum(a: BigUint, b: BigUint, m: BigUint, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&a).max(bignum_len(&b)).max(bignum_len(&m)); - let output_len = len; - let memory = pad_bignums(&[a, b, m], len); - - let a_start_loc = 0; - let b_start_loc = len; - let m_start_loc = 2 * len; - let output_start_loc = 3 * len; - let scratch_1 = 4 * len; // size 2*len - let scratch_2 = 6 * len; // size 2*len - let scratch_3 = 8 * len; // size 2*len - let (new_memory, _new_stack) = run_test( - "modmul_bignum", - memory, - vec![ - len.into(), - a_start_loc.into(), - b_start_loc.into(), - m_start_loc.into(), - output_start_loc.into(), - scratch_1.into(), - scratch_2.into(), - scratch_3.into(), - ], - )?; - - let output = mem_vec_to_biguint(&new_memory[output_start_loc..output_start_loc + output_len]); - assert_eq!(output, expected_output); - - Ok(()) -} - -fn test_modexp_bignum(b: BigUint, e: BigUint, m: BigUint, expected_output: BigUint) -> Result<()> { - let len = bignum_len(&b).max(bignum_len(&e)).max(bignum_len(&m)); - let output_len = len; - let memory = pad_bignums(&[b, e, m], len); - - let b_start_loc = 0; - let e_start_loc = len; - let m_start_loc = 2 * len; - let output_start_loc = 3 * len; - let scratch_1 = 4 * len; - let scratch_2 = 5 * len; // size 2*len - let scratch_3 = 7 * len; // size 2*len - let scratch_4 = 9 * len; // size 2*len - let scratch_5 = 11 * len; // size 2*len - let (mut new_memory, _new_stack) = run_test( - "modexp_bignum", - memory, - vec![ - len.into(), - b_start_loc.into(), - e_start_loc.into(), - m_start_loc.into(), - output_start_loc.into(), - scratch_1.into(), - scratch_2.into(), - scratch_3.into(), - scratch_4.into(), - scratch_5.into(), - ], - )?; - new_memory.resize( - new_memory.len().max(output_start_loc + output_len), - 0.into(), - ); - - let output = mem_vec_to_biguint(&new_memory[output_start_loc..output_start_loc + output_len]); - assert_eq!(output, expected_output); - - Ok(()) -} - -#[test] -fn test_shr_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let input = gen_bignum(bit_size); - let output = input.clone() >> 1; - test_shr_bignum(input, output)?; - - let input = max_bignum(bit_size); - let output = input.clone() >> 1; - test_shr_bignum(input, output)?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let shr_outputs = test_data_biguint(TEST_DATA_SHR_OUTPUTS); - for (input, output) in inputs.iter().zip(shr_outputs.iter()) { - test_shr_bignum(input.clone(), output.clone())?; - } - - Ok(()) -} - -#[test] -fn test_iszero_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let input = gen_bignum(bit_size); - let output = input.is_zero() as u8; - test_iszero_bignum(input, output.into())?; - - let input = max_bignum(bit_size); - let output = bit_size.is_zero() as u8; - test_iszero_bignum(input, output.into())?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let iszero_outputs = test_data_u256(TEST_DATA_ISZERO_OUTPUTS); - let mut iszero_outputs_iter = iszero_outputs.iter(); - for input in inputs { - let output = iszero_outputs_iter.next().unwrap(); - test_iszero_bignum(input.clone(), *output)?; - } - - Ok(()) -} - -#[test] -fn test_cmp_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let a = gen_bignum(bit_size); - let b = gen_bignum(bit_size); - let output = match a.cmp(&b) { - Ordering::Less => MINUS_ONE, - Ordering::Equal => 0.into(), - Ordering::Greater => 1.into(), - }; - test_cmp_bignum(a, b, output)?; - - let a = max_bignum(bit_size); - let b = max_bignum(bit_size); - let output = 0.into(); - test_cmp_bignum(a, b, output)?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let cmp_outputs = test_data_u256(TEST_DATA_CMP_OUTPUTS); - let mut cmp_outputs_iter = cmp_outputs.iter(); - for a in &inputs { - for b in &inputs { - let output = cmp_outputs_iter.next().unwrap(); - test_cmp_bignum(a.clone(), b.clone(), *output)?; - } - } - - Ok(()) -} - -#[test] -fn test_add_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let a = gen_bignum(bit_size); - let b = gen_bignum(bit_size); - let output = a.clone() + b.clone(); - test_add_bignum(a, b, output)?; - - let a = max_bignum(bit_size); - let b = max_bignum(bit_size); - let output = a.clone() + b.clone(); - test_add_bignum(a, b, output)?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let add_outputs = test_data_biguint(TEST_DATA_ADD_OUTPUTS); - let mut add_outputs_iter = add_outputs.iter(); - for a in &inputs { - for b in &inputs { - let output = add_outputs_iter.next().unwrap(); - test_add_bignum(a.clone(), b.clone(), output.clone())?; - } - } - - Ok(()) -} - -#[test] -fn test_addmul_bignum_all() -> Result<()> { - let mut rng = rand::thread_rng(); - - for bit_size in BIT_SIZES_TO_TEST { - let a = gen_bignum(bit_size); - let b = gen_bignum(bit_size); - let c: u128 = rng.gen(); - let output = a.clone() + b.clone() * c; - test_addmul_bignum(a, b, c, output)?; - - let a = max_bignum(bit_size); - let b = max_bignum(bit_size); - let c: u128 = rng.gen(); - let output = a.clone() + b.clone() * c; - test_addmul_bignum(a, b, c, output)?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let u128_inputs = test_data_u128(TEST_DATA_U128_INPUTS); - let addmul_outputs = test_data_biguint(TEST_DATA_ADDMUL_OUTPUTS); - let mut addmul_outputs_iter = addmul_outputs.iter(); - for a in &inputs { - for b in &inputs { - for c in &u128_inputs { - let output = addmul_outputs_iter.next().unwrap(); - test_addmul_bignum(a.clone(), b.clone(), *c, output.clone())?; - } - } - } - - Ok(()) -} - -#[test] -fn test_mul_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let a = gen_bignum(bit_size); - let b = gen_bignum(bit_size); - let output = a.clone() * b.clone(); - test_mul_bignum(a, b, output)?; - - let a = max_bignum(bit_size); - let b = max_bignum(bit_size); - let output = a.clone() * b.clone(); - test_mul_bignum(a, b, output)?; - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let mul_outputs = test_data_biguint(TEST_DATA_MUL_OUTPUTS); - let mut mul_outputs_iter = mul_outputs.iter(); - for a in &inputs { - for b in &inputs { - let output = mul_outputs_iter.next().unwrap(); - test_mul_bignum(a.clone(), b.clone(), output.clone())?; - } - } - - Ok(()) -} - -#[test] -fn test_modmul_bignum_all() -> Result<()> { - for bit_size in BIT_SIZES_TO_TEST { - let a = gen_bignum(bit_size); - let b = gen_bignum(bit_size); - let m = gen_bignum(bit_size); - if !m.is_zero() { - let output = &a * &b % &m; - test_modmul_bignum(a, b, m, output)?; - } - - let a = max_bignum(bit_size); - let b = max_bignum(bit_size); - let m = max_bignum(bit_size); - if !m.is_zero() { - let output = &a * &b % &m; - test_modmul_bignum(a, b, m, output)?; - } - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let modmul_outputs = test_data_biguint(TEST_DATA_MODMUL_OUTPUTS); - let mut modmul_outputs_iter = modmul_outputs.into_iter(); - for a in &inputs { - for b in &inputs { - // For m, skip the first input, which is zero. - for m in &inputs[1..] { - let output = modmul_outputs_iter.next().unwrap(); - test_modmul_bignum(a.clone(), b.clone(), m.clone(), output)?; - } - } - } - - Ok(()) -} - -#[test] -fn test_modexp_bignum_all() -> Result<()> { - let exp_bit_sizes = vec![2, 9, 11, 16]; - - for bit_size in &BIT_SIZES_TO_TEST[3..7] { - for exp_bit_size in &exp_bit_sizes { - let b = gen_bignum(*bit_size); - let e = gen_bignum(*exp_bit_size); - let m = gen_bignum(*bit_size); - if !m.is_zero() { - let output = b.clone().modpow(&e, &m); - test_modexp_bignum(b, e, m, output)?; - } - - let b = max_bignum(*bit_size); - let e = max_bignum(*exp_bit_size); - let m = max_bignum(*bit_size); - if !m.is_zero() { - let output = b.modpow(&e, &m); - test_modexp_bignum(b, e, m, output)?; - } - } - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let modexp_outputs = test_data_biguint(TEST_DATA_MODEXP_OUTPUTS); - let mut modexp_outputs_iter = modexp_outputs.into_iter(); - for b in &inputs[..9] { - // Include only smaller exponents, to keep tests from becoming too slow. - for e in &inputs[..6] { - for m in &inputs[..9] { - let output = modexp_outputs_iter.next().unwrap(); - test_modexp_bignum(b.clone(), e.clone(), m.clone(), output)?; - } - } - } - - Ok(()) -} - -#[test] -#[ignore] // Too slow to run on CI. -fn test_modexp_bignum_all_full() -> Result<()> { - // Only test smaller values for exponent. - let exp_bit_sizes = vec![2, 100, 127, 128, 129]; - - for bit_size in &BIT_SIZES_TO_TEST[3..14] { - for exp_bit_size in &exp_bit_sizes { - let b = gen_bignum(*bit_size); - let e = gen_bignum(*exp_bit_size); - let m = gen_bignum(*bit_size); - if !m.is_zero() { - let output = b.clone().modpow(&e, &m); - test_modexp_bignum(b, e, m, output)?; - } - - let b = max_bignum(*bit_size); - let e = max_bignum(*exp_bit_size); - let m = max_bignum(*bit_size); - if !m.is_zero() { - let output = b.modpow(&e, &m); - test_modexp_bignum(b, e, m, output)?; - } - } - } - - let inputs = test_data_biguint(TEST_DATA_BIGNUM_INPUTS); - let modexp_outputs = test_data_biguint(TEST_DATA_MODEXP_OUTPUTS_FULL); - let mut modexp_outputs_iter = modexp_outputs.into_iter(); - for b in &inputs { - // Include only smaller exponents, to keep tests from becoming too slow. - for e in &inputs[..7] { - for m in &inputs { - let output = modexp_outputs_iter.next().unwrap(); - test_modexp_bignum(b.clone(), e.clone(), m.clone(), output)?; - } - } - } - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/add_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/add_outputs deleted file mode 100644 index 36ebe0497c..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/add_outputs +++ /dev/null @@ -1,225 +0,0 @@ -0 -1 -21 -908 -1267650597867046177654064545792 -340282366920938463463374607431768211455 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -1 -2 -22 -909 -1267650597867046177654064545793 -340282366920938463463374607431768211456 -57896044618658097611351864738157061705262361561497619362091104892532012613633 -115792089237105570840234253759177109864155645142784332660520492325483608801281 -231583736816786089484927226016147767929578972263620494977377884571370600267776 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049793 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348288 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780160 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103233 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369217 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046272 -21 -22 -42 -929 -1267650597867046177654064545813 -340282366920938463463374607431768211476 -57896044618658097611351864738157061705262361561497619362091104892532012613653 -115792089237105570840234253759177109864155645142784332660520492325483608801301 -231583736816786089484927226016147767929578972263620494977377884571370600267796 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049813 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348308 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780180 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103253 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369237 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046292 -908 -909 -929 -1816 -1267650597867046177654064546700 -340282366920938463463374607431768212363 -57896044618658097611351864738157061705262361561497619362091104892532012614540 -115792089237105570840234253759177109864155645142784332660520492325483608802188 -231583736816786089484927226016147767929578972263620494977377884571370600268683 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050700 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486349195 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690781067 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387104140 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375370124 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957047179 -1267650597867046177654064545792 -1267650597867046177654064545793 -1267650597867046177654064545813 -1267650597867046177654064546700 -2535301195734092355308129091584 -340282368188589061330420785085832757247 -57896044618658097611351864738157061705262361562765269959958151070186077159424 -115792089237105570840234253759177109864155645144051983258387538503137673347072 -231583736816786089484927226016147767929578972264888145575244930749024664813567 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725447442614227457227324739062595584 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851379715837692741036504647550894079 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756017613817472060411970538755325951 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459868939503685406252196573451649024 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059367521063656309566056683439915008 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571754105966758923615108084021592063 -340282366920938463463374607431768211455 -340282366920938463463374607431768211456 -340282366920938463463374607431768211476 -340282366920938463463374607431768212363 -340282368188589061330420785085832757247 -680564733841876926926749214863536422910 -57896044618658097611351864738157061705602643928418557825554479499963780825087 -115792089237105570840234253759177109864495927509705271123983866932915377012735 -231583736816786089484927226016147767929919254630541433440841259178802368479230 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656433007813095902093053555754516766261247 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352969133745369125558337364934425254559742 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107569038383267105337656740400316458991614 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244438742234592791551002580626351155314687 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359572341733174351521905894486461143580671 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998595854119759254624519943537861725257726 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613633 -57896044618658097611351864738157061705262361561497619362091104892532012613653 -57896044618658097611351864738157061705262361561497619362091104892532012614540 -57896044618658097611351864738157061705262361562765269959958151070186077159424 -57896044618658097611351864738157061705602643928418557825554479499963780825087 -115792089237316195222703729476314123410524723122995238724182209785064025227264 -173688133855763668451586118497334171569418006704281952022611597218015621414912 -289479781435444187096279090754304829634841333825118114339468989463902612881407 -3273390607896141870013189696827599152216642046043064789482248405676250539586401155309462588318799202567027652361355087007672582991681286039617010663424 -5027927973729236057982426364448826617555638513633071601633370220421967636306804394074875752581912318823441521134057891212939945806456965095219525498961919 -13407807929942597099574024997867385471458758537929012740010782671329620832395892888572752773084019014537559577489812491117577843786236284470685416703393791 -26815615859885194199148049995734770942917517075858043397979502765092926124329972428655111861232797616170839264946949360821429169472449630310911451399716864 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356940333001073316904952470628102164776064494420927751032420533624771561387982848 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463796730242309831072892700701204289449703517933314335935523147673822961969659903 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801281 -115792089237105570840234253759177109864155645142784332660520492325483608801301 -115792089237105570840234253759177109864155645142784332660520492325483608802188 -115792089237105570840234253759177109864155645144051983258387538503137673347072 -115792089237105570840234253759177109864495927509705271123983866932915377012735 -173688133855763668451586118497334171569418006704281952022611597218015621414912 -231584178474211141680468507518354219728311290285568665321040984650967217602560 -347375826053891660325161479775324877793734617406404827637898376896854209069055 -3273390607896141870013189696827599152216642046043064789482248405676250539644297199927910061547681591588047700520248370588959296290110673472568606851072 -5027927973729236057982426364448826617555638513633071601633370220421967636306862290119494200055141201212462541182216784496521232519755394482652477095149567 -13407807929942597099574024997867385471458758537929012740010782671329620832395950784617371220557247896926580597537971384401159130499534713858118368299581439 -26815615859885194199148049995734770942917517075858043397979502765092926124330030324699730308706026498559860284995108254105010456185748059698344402995904512 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356998229045691764378181353017123184824223387704509037745718963012204512984170496 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463854626286928278546121583090225309497862411216895622648821577061255913565847551 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267776 -231583736816786089484927226016147767929578972263620494977377884571370600267796 -231583736816786089484927226016147767929578972263620494977377884571370600268683 -231583736816786089484927226016147767929578972264888145575244930749024664813567 -231583736816786089484927226016147767929919254630541433440841259178802368479230 -289479781435444187096279090754304829634841333825118114339468989463902612881407 -347375826053891660325161479775324877793734617406404827637898376896854209069055 -463167473633572178969854452032295535859157944527240989954755769142741200535550 -3273390607896141870013189696827599152216642046043064789482248405676250539760088847507590580192374563845018358585671697709795458606968065718455598317567 -5027927973729236057982426364448826617555638513633071601633370220421967636306978081767073880573785894184719511840282207823642068682072251874898364086616062 -13407807929942597099574024997867385471458758537929012740010782671329620832396066576264950901075892589898837568196036807728279966661851571250364255291047934 -26815615859885194199148049995734770942917517075858043397979502765092926124330146116347309989224671191532117255653173677432131292348064917090590289987371007 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322357114020693271444896826045989380155482288811031629873908035820404450399975636991 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463970417934507959064766276062482280155927834544016458811138434453501800557314046 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049793 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049813 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050700 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725447442614227457227324739062595584 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656433007813095902093053555754516766261247 -3273390607896141870013189696827599152216642046043064789482248405676250539586401155309462588318799202567027652361355087007672582991681286039617010663424 -3273390607896141870013189696827599152216642046043064789482248405676250539644297199927910061547681591588047700520248370588959296290110673472568606851072 -3273390607896141870013189696827599152216642046043064789482248405676250539760088847507590580192374563845018358585671697709795458606968065718455598317567 -6546781215792283740026379393655198304433284092086129578964496811352501079057010221381608981414894675657741181312185450892349927259180362294169996099584 -5031201364337132199852439554145654216707855155679114666422852468827643886846275003140947898975008414296532234663008721576824623150724464171474078484398079 -13411081320550493241444038187564213070610975179975055804800264919735297082935363497638824919477115110010650291018763321481462521130503783546939969688829951 -26818889250493090341018063185431598542069733717904086462768985013498602374869443037721184007625893711643929978475900191185313846816717129387166004385153024 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094859873731529946340457125253265606438475403785866280608383688705623749572896410942067145463298048566101192878305015324784812428376688032701026114373419008 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988312365191525626829590504228669795829923743060941349315981180118158171906003267339308381977465988796174295002978654348297199013279790646750077514955096063 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348288 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348308 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486349195 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851379715837692741036504647550894079 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352969133745369125558337364934425254559742 -5027927973729236057982426364448826617555638513633071601633370220421967636306804394074875752581912318823441521134057891212939945806456965095219525498961919 -5027927973729236057982426364448826617555638513633071601633370220421967636306862290119494200055141201212462541182216784496521232519755394482652477095149567 -5027927973729236057982426364448826617555638513633071601633370220421967636306978081767073880573785894184719511840282207823642068682072251874898364086616062 -5031201364337132199852439554145654216707855155679114666422852468827643886846275003140947898975008414296532234663008721576824623150724464171474078484398079 -10055855947458472115964852728897653235111277027266143203266740440843935272613492996060514188968601933917406728144705257702756896374189747980653986972696574 -18435735903671833157556451362316212089014397051562084341644152891751588468702581490558391209470708629631524784500459857607394794353969067356119878177128446 -31843543833614430257130476360183597560473155589491114999612872985514893760636661030640750297619487231264804471957596727311246120040182413196345912873451519 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604099884528314651286256569538428017605456878825657453309145227576677640040958663628934986711753291642085722067371786711860910744701600153316510206022861717503 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060993337019774646966745702917403421794848327164932528377852825068090174463291770485332227948267459582315795169496460350884423131286503255930559257423443394558 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780160 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780180 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690781067 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756017613817472060411970538755325951 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107569038383267105337656740400316458991614 -13407807929942597099574024997867385471458758537929012740010782671329620832395892888572752773084019014537559577489812491117577843786236284470685416703393791 -13407807929942597099574024997867385471458758537929012740010782671329620832395950784617371220557247896926580597537971384401159130499534713858118368299581439 -13407807929942597099574024997867385471458758537929012740010782671329620832396066576264950901075892589898837568196036807728279966661851571250364255291047934 -13411081320550493241444038187564213070610975179975055804800264919735297082935363497638824919477115110010650291018763321481462521130503783546939969688829951 -18435735903671833157556451362316212089014397051562084341644152891751588468702581490558391209470708629631524784500459857607394794353969067356119878177128446 -26815615859885194199148049995734770942917517075858025480021565342659241664791669985056268229972815325345642840856214457512032692333748386731585769381560318 -40223423789827791298722074993602156414376275613787056137990285436422546956725749525138627318121593926978922528313351327215884018019961732571811804077883391 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604108264408270864647298161137061436164310781945681749250283604989128547694154752717429484588773793748781436185428142466460815382599579932635885671914066149375 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061001716899730860327787294516036840353702230284956824318991202480541082116487859573826725825287961689011509287552816105484327769184483035249934723314647826430 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103233 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103253 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387104140 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459868939503685406252196573451649024 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244438742234592791551002580626351155314687 -26815615859885194199148049995734770942917517075858043397979502765092926124329972428655111861232797616170839264946949360821429169472449630310911451399716864 -26815615859885194199148049995734770942917517075858043397979502765092926124330030324699730308706026498559860284995108254105010456185748059698344402995904512 -26815615859885194199148049995734770942917517075858043397979502765092926124330146116347309989224671191532117255653173677432131292348064917090590289987371007 -26818889250493090341018063185431598542069733717904086462768985013498602374869443037721184007625893711643929978475900191185313846816717129387166004385153024 -31843543833614430257130476360183597560473155589491114999612872985514893760636661030640750297619487231264804471957596727311246120040182413196345912873451519 -40223423789827791298722074993602156414376275613787056137990285436422546956725749525138627318121593926978922528313351327215884018019961732571811804077883391 -53631231719770388398296099991469541885835034151716086795959005530185852248659829065220986406270372528612202215770488196919735343706175078412037838774206464 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604121672216200807244397735162059303549782240704219678280941573709222310999446686796969566947861942527383069465115599603330519233925266145981725897948762472448 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061015124707660802924886868541034707739173689043494753349649171200634845421779793653366808184376110467613142567240273242354031620510169248595774949349344149503 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369217 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369237 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375370124 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059367521063656309566056683439915008 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359572341733174351521905894486461143580671 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356940333001073316904952470628102164776064494420927751032420533624771561387982848 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356998229045691764378181353017123184824223387704509037745718963012204512984170496 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322357114020693271444896826045989380155482288811031629873908035820404450399975636991 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094859873731529946340457125253265606438475403785866280608383688705623749572896410942067145463298048566101192878305015324784812428376688032701026114373419008 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604099884528314651286256569538428017605456878825657453309145227576677640040958663628934986711753291642085722067371786711860910744701600153316510206022861717503 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604108264408270864647298161137061436164310781945681749250283604989128547694154752717429484588773793748781436185428142466460815382599579932635885671914066149375 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604121672216200807244397735162059303549782240704219678280941573709222310999446686796969566947861942527383069465115599603330519233925266145981725897948762472448 -21430172143725344039741447416747135734328099194269775938858685112579378184657786065906763159178676625205218492566825428477050725853377832065983208189713200681844100397174224127137557678646374287640475087188412914436146644713764873912909317614682237526728015428718464118732506826116885039758058750738432 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824629198276151174244463087285660418287344529756165187857520911105123842660034918703411836307538943107430287596439419222071804002615797677806638572665083165692141839780886307603102541747070094713562715543794785904326970568977820621271154145831782622467599830140102357487631119091729219499088809459332415487 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046272 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046292 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957047179 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571754105966758923615108084021592063 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998595854119759254624519943537861725257726 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463796730242309831072892700701204289449703517933314335935523147673822961969659903 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463854626286928278546121583090225309497862411216895622648821577061255913565847551 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463970417934507959064766276062482280155927834544016458811138434453501800557314046 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988312365191525626829590504228669795829923743060941349315981180118158171906003267339308381977465988796174295002978654348297199013279790646750077514955096063 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060993337019774646966745702917403421794848327164932528377852825068090174463291770485332227948267459582315795169496460350884423131286503255930559257423443394558 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061001716899730860327787294516036840353702230284956824318991202480541082116487859573826725825287961689011509287552816105484327769184483035249934723314647826430 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061015124707660802924886868541034707739173689043494753349649171200634845421779793653366808184376110467613142567240273242354031620510169248595774949349344149503 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824629198276151174244463087285660418287344529756165187857520911105123842660034918703411836307538943107430287596439419222071804002615797677806638572665083165692141839780886307603102541747070094713562715543794785904326970568977820621271154145831782622467599830140102357487631119091729219499088809459332415487 -70149324220868077495255175920561715987048031760661657648151596049581927701126644407314161773169938523306300574636470765864723972756401953860971729649236966380158623144886433123904089438954731413136105939102963525135105941885179620757765851918707538235369974386271618715130954505741977781211162121976618183601835461375440982077945936461543052837790612502383395739504991310927477668395382345950562697672932264775996511143505676632322113137860859914092542 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/addmul_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/addmul_outputs deleted file mode 100644 index 397c745168..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/addmul_outputs +++ /dev/null @@ -1,1350 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -1 -21 -908 -1267650597867046177654064545792 -340282366920938463463374607431768211455 -0 -21 -441 -19068 -26620662555207969730735355461632 -7145929705339707732730866756067132440555 -0 -908 -19068 -824464 -1151026742863277929309890607579136 -308976389164212124824744143548045536001140 -0 -1267650597867046177654064545792 -26620662555207969730735355461632 -1151026742863277929309890607579136 -1606938038272679619211255036084048932956190504430095264907264 -431359145870941220571487096504865044588697904564591140391738786447360 -0 -340282366920938463463374607431768211455 -7145929705339707732730866756067132440555 -308976389164212124824744143548045536001140 -431359145870941220571487096504865044588697904564591140391738786447360 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -0 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -0 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -0 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -0 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -0 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -0 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753536 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248128 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139072 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569280 -0 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097971691 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014068 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542341632 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347234305 -1 -1 -1 -1 -1 -1 -1 -2 -22 -909 -1267650597867046177654064545793 -340282366920938463463374607431768211456 -1 -22 -442 -19069 -26620662555207969730735355461633 -7145929705339707732730866756067132440556 -1 -909 -19069 -824465 -1151026742863277929309890607579137 -308976389164212124824744143548045536001141 -1 -1267650597867046177654064545793 -26620662555207969730735355461633 -1151026742863277929309890607579137 -1606938038272679619211255036084048932956190504430095264907265 -431359145870941220571487096504865044588697904564591140391738786447361 -1 -340282366920938463463374607431768211456 -7145929705339707732730866756067132440556 -308976389164212124824744143548045536001141 -431359145870941220571487096504865044588697904564591140391738786447361 -115792089237316195423570985008687907852589419931798687112530834793049593217026 -1 -57896044618658097611351864738157061705262361561497619362091104892532012613633 -1215816936991820049838389159501298295810509592791450006603913202743172264886273 -52569608513741552631107493182246612028378224297839838380778723242419067453177857 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436545 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554561 -1 -115792089237105570840234253759177109864155645142784332660520492325483608801281 -2431633873979216987644919328942719307147268547998470985870930338835155784826881 -105139217027291858322932702413332815756653325789648174055752607031539116791562241 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213761 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662401 -1 -231583736816786089484927226016147767929578972263620494977377884571370600267776 -4863258473152507879183471746339103126521158417536030394524935575998782605623276 -210278033029641769252313921222662173280057706815367409439459119190804505043139701 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452801 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362626 -1 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049793 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045633 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211137 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075265 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767361 -1 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348288 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314028 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244597 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258305 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027586 -1 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780160 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383340 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384373 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540929 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521346 -1 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103233 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167873 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734657 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199745 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922561 -1 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369217 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753537 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248129 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139073 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569281 -1 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046272 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097971692 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014069 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542341633 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347234306 -21 -21 -21 -21 -21 -21 -21 -22 -42 -929 -1267650597867046177654064545813 -340282366920938463463374607431768211476 -21 -42 -462 -19089 -26620662555207969730735355461653 -7145929705339707732730866756067132440576 -21 -929 -19089 -824485 -1151026742863277929309890607579157 -308976389164212124824744143548045536001161 -21 -1267650597867046177654064545813 -26620662555207969730735355461653 -1151026742863277929309890607579157 -1606938038272679619211255036084048932956190504430095264907285 -431359145870941220571487096504865044588697904564591140391738786447381 -21 -340282366920938463463374607431768211476 -7145929705339707732730866756067132440576 -308976389164212124824744143548045536001161 -431359145870941220571487096504865044588697904564591140391738786447381 -115792089237316195423570985008687907852589419931798687112530834793049593217046 -21 -57896044618658097611351864738157061705262361561497619362091104892532012613653 -1215816936991820049838389159501298295810509592791450006603913202743172264886293 -52569608513741552631107493182246612028378224297839838380778723242419067453177877 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436565 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554581 -21 -115792089237105570840234253759177109864155645142784332660520492325483608801301 -2431633873979216987644919328942719307147268547998470985870930338835155784826901 -105139217027291858322932702413332815756653325789648174055752607031539116791562261 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213781 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662421 -21 -231583736816786089484927226016147767929578972263620494977377884571370600267796 -4863258473152507879183471746339103126521158417536030394524935575998782605623296 -210278033029641769252313921222662173280057706815367409439459119190804505043139721 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452821 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362646 -21 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049813 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045653 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211157 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075285 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767381 -21 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348308 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314048 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244617 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258325 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027606 -21 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780180 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383360 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384393 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540949 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521366 -21 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103253 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167893 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734677 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199765 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922581 -21 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369237 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753557 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248149 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139093 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569301 -21 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046292 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097971712 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014089 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542341653 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347234326 -908 -908 -908 -908 -908 -908 -908 -909 -929 -1816 -1267650597867046177654064546700 -340282366920938463463374607431768212363 -908 -929 -1349 -19976 -26620662555207969730735355462540 -7145929705339707732730866756067132441463 -908 -1816 -19976 -825372 -1151026742863277929309890607580044 -308976389164212124824744143548045536002048 -908 -1267650597867046177654064546700 -26620662555207969730735355462540 -1151026742863277929309890607580044 -1606938038272679619211255036084048932956190504430095264908172 -431359145870941220571487096504865044588697904564591140391738786448268 -908 -340282366920938463463374607431768212363 -7145929705339707732730866756067132441463 -308976389164212124824744143548045536002048 -431359145870941220571487096504865044588697904564591140391738786448268 -115792089237316195423570985008687907852589419931798687112530834793049593217933 -908 -57896044618658097611351864738157061705262361561497619362091104892532012614540 -1215816936991820049838389159501298295810509592791450006603913202743172264887180 -52569608513741552631107493182246612028378224297839838380778723242419067453178764 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867437452 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591555468 -908 -115792089237105570840234253759177109864155645142784332660520492325483608802188 -2431633873979216987644919328942719307147268547998470985870930338835155784827788 -105139217027291858322932702413332815756653325789648174055752607031539116791563148 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788214668 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114663308 -908 -231583736816786089484927226016147767929578972263620494977377884571370600268683 -4863258473152507879183471746339103126521158417536030394524935575998782605624183 -210278033029641769252313921222662173280057706815367409439459119190804505043140608 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949453708 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822363533 -908 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050700 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959046540 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229212044 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280076172 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874768268 -908 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486349195 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314935 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604245504 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372259212 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193028493 -908 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690781067 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506384247 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228385280 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460541836 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030522253 -908 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387104140 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129168780 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489735564 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695200652 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089923468 -908 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375370124 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882754444 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835249036 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139980 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585570188 -908 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957047179 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097972599 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014976 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542342540 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347235213 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545793 -1267650597867046177654064545813 -1267650597867046177654064546700 -2535301195734092355308129091584 -340282368188589061330420785085832757247 -1267650597867046177654064545792 -1267650597867046177654064545813 -1267650597867046177654064546233 -1267650597867046177654064564860 -27888313153075015908389420007424 -7145929706607358330597912933721196986347 -1267650597867046177654064545792 -1267650597867046177654064546700 -1267650597867046177654064564860 -1267650597867046177654065370256 -1152294393461144975487544672124928 -308976389165479775422611189725699600546932 -1267650597867046177654064545792 -2535301195734092355308129091584 -27888313153075015908389420007424 -1152294393461144975487544672124928 -1606938038272679619211255036085316583554057550607749329453056 -431359145870941220571487096504865044589965555162458186569392850993152 -1267650597867046177654064545792 -340282368188589061330420785085832757247 -7145929706607358330597912933721196986347 -308976389165479775422611189725699600546932 -431359145870941220571487096504865044589965555162458186569392850993152 -115792089237316195423570985008687907852589419933066337710397880970703657762817 -1267650597867046177654064545792 -57896044618658097611351864738157061705262361562765269959958151070186077159424 -1215816936991820049838389159501298295810509592792717657201780248920826329432064 -52569608513741552631107493182246612028378224297841106031376590288596721517723648 -73391955574979118963811141843059488536193514605218060347731553038824318137414703480524650533996482931982336 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218991387546187984961321042656100352 -1267650597867046177654064545792 -115792089237105570840234253759177109864155645144051983258387538503137673347072 -2431633873979216987644919328942719307147268547999738636468797385012809849372672 -105139217027291858322932702413332815756653325789649441706350474077716770856108032 -146783911149691239803259475393272332038744581223672095908357420057841712239443883445246902170087870852759552 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608021501257222839633185216179208192 -1267650597867046177654064545792 -231583736816786089484927226016147767929578972264888145575244930749024664813567 -4863258473152507879183471746339103126521158417537298045122802622176436670169067 -210278033029641769252313921222662173280057706815368677090056986236982159107685492 -293567262432083559770702660509494961636846893913475830448684457955877331972489652261961673363397303013998592 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002657839837909325811189609886908417 -1267650597867046177654064545792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725447442614227457227324739062595584 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234370941886819260850266439023591424 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705128134626265751527730832293756928 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358031475992617627085616064344621056 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279643857395718351372742508939313152 -1267650597867046177654064545792 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851379715837692741036504647550894079 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878948679579590220843044517277859819 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051632221532743450263087739668790388 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384287907864820397100311543436804096 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193279543543761169002421491257573377 -1267650597867046177654064545792 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756017613817472060411970538755325951 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876344537154955927727828232570929131 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462843587172365443186116953292930164 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353122984901987710668875720525086720 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371205792537007967971890634095067137 -1267650597867046177654064545792 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459868939503685406252196573451649024 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657222376565436190372574961193713664 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559847310254083466111356457554280448 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286811140938899098474580114759745536 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655226896156634458137388928154468352 -1267650597867046177654064545792 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059367521063656309566056683439915008 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246692589324825159963637270947299328 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904559366707663675096336326899793920 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887523916172743842520101389403684864 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290551001100754863607308861876650115072 -1267650597867046177654064545792 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571754105966758923615108084021592063 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006810872289980054993716683162517483 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151578458724837231635008055062559860 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434952888523927958804883965606887424 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350095710449584237364695558411780097 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211456 -340282366920938463463374607431768211476 -340282366920938463463374607431768212363 -340282368188589061330420785085832757247 -680564733841876926926749214863536422910 -340282366920938463463374607431768211455 -340282366920938463463374607431768211476 -340282366920938463463374607431768211896 -340282366920938463463374607431768230523 -340282393541601018671344338167123673087 -7486212072260646196194241363498900652010 -340282366920938463463374607431768211455 -340282366920938463463374607431768212363 -340282366920938463463374607431768230523 -340282366920938463463374607431769035919 -340283517947681326741303917322375790591 -309316671531133063288207518155477304212595 -340282366920938463463374607431768211455 -340282368188589061330420785085832757247 -340282393541601018671344338167123673087 -340283517947681326741303917322375790591 -1606938038272679619211595318450969871419653879037527033118719 -431359145870941220571487096505205326955618843028054514999170554658815 -340282366920938463463374607431768211455 -680564733841876926926749214863536422910 -7486212072260646196194241363498900652010 -309316671531133063288207518155477304212595 -431359145870941220571487096505205326955618843028054514999170554658815 -115792089237316195423570985008687907852929702298719625575994209400481361428480 -340282366920938463463374607431768211455 -57896044618658097611351864738157061705602643928418557825554479499963780825087 -1215816936991820049838389159501298295810849875158370945067376577350604033097727 -52569608513741552631107493182246612028378564580206759319242186617026499221389311 -73391955574979118963811141843059488536193514605218060347731553038824658419780356768390246862426260635647999 -19701003098197239571963727475337245584161626291901231402719522479678259504891017501357040834053581289750820359766015 -340282366920938463463374607431768211455 -115792089237105570840234253759177109864495927509705271123983866932915377012735 -2431633873979216987644919328942719307147608830365391924334393713442587553038335 -105139217027291858322932702413332815756653666072015094994216070406146548559773695 -146783911149691239803259475393272332038744581223672095908357420057842052521809536733112498498517648556425215 -39402006196322807380529480735708200350692396090822410401547663254352517428720089890387154545088435961614993882873855 -340282366920938463463374607431768211455 -231583736816786089484927226016147767929919254630541433440841259178802368479230 -4863258473152507879183471746339103126521498699902951332988398950606214373834730 -210278033029641769252313921222662173280058047097734330377922582565411936811351155 -293567262432083559770702660509494961636846893913475830448684457955877672254855305549827269691827080717664255 -78803862104411649792976274791681038936454943300723566886694079153850261012036930285023493125774922139619387590574080 -340282366920938463463374607431768211455 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656433007813095902093053555754516766261247 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403778287516736595174684857178696216727257087 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732534987493787914131347856160609997422591 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978781640397129280483223414045842048286719 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608957562009510683583947701172286642978815 -340282366920938463463374607431768211455 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352969133745369125558337364934425254559742 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405546161314332867455817171474294981525482 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696187337333997874820609046591517517372456051 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976474666653561152685993428741321140469759 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655681475645196831626765330851268961239040 -340282366920938463463374607431768211455 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107569038383267105337656740400316458991614 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990252144158710190442821524056258010274594794 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721364050745209240460231039514546730996595827 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113336037635488638189853306997305498228752383 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955358653571445824873564300320411798732800 -340282366920938463463374607431768211455 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244438742234592791551002580626351155314687 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126407939588029853301786701004738897379327 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641741842212963541949062439786235257946111 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721540569176794226764694803009892463411199 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446968937592549444500054465818705858134015 -340282366920938463463374607431768211455 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359572341733174351521905894486461143580671 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001544213529058242612690756292067048650964991 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638183050186925019995529271424766104603459583 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051674078169889569460609438848531167107350527 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607630833366754042729203637291654353780735 -340282366920938463463374607431768211455 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998595854119759254624519943537861725257726 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963707289176525577845651322146460866183146 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416399433944112012702827963437832766225523 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846399717318541811793555133313743310553087 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989210281632461363737449833693125336115445760 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613633 -57896044618658097611351864738157061705262361561497619362091104892532012613653 -57896044618658097611351864738157061705262361561497619362091104892532012614540 -57896044618658097611351864738157061705262361562765269959958151070186077159424 -57896044618658097611351864738157061705602643928418557825554479499963780825087 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613653 -57896044618658097611351864738157061705262361561497619362091104892532012614073 -57896044618658097611351864738157061705262361561497619362091104892532012632700 -57896044618658097611351864738157061705262361588118281917299074623267368075264 -57896044618658097611351864738157061712408291266837327094821971648599145054187 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012614540 -57896044618658097611351864738157061705262361561497619362091104892532012632700 -57896044618658097611351864738157061705262361561497619362091104892532013438096 -57896044618658097611351864738157061705262362712524362225369034202422620192768 -57896044618658097611351864738157062014238750725709744186835248440577548614772 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361562765269959958151070186077159424 -57896044618658097611351864738157061705262361588118281917299074623267368075264 -57896044618658097611351864738157061705262362712524362225369034202422620192768 -57896044618658099218289903010836680916517397645546552318281609322627277520896 -57896045050017243482293085309644158210127406150195523926682245284270799060992 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705602643928418557825554479499963780825087 -57896044618658097611351864738157061712408291266837327094821971648599145054187 -57896044618658097611351864738157062014238750725709744186835248440577548614772 -57896045050017243482293085309644158210127406150195523926682245284270799060992 -173688133855974293034922849746844969557851781493296306474621939685581605830657 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -115792089237316195222703729476314123410524723122995238724182209785064025227264 -1273712981610478147449741024239455357515771954352947625966004307635704277499904 -52627504558360210728718845046984769090083486659401336000140814347311599465791488 -73391955574979118963811141843117384580812172702829412212469710100529580498974933449288874592711360880050176 -19701003098197239571963727475337245584219522336519889500330874344416416566595939580551617514952209020035920604168192 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -173688133855763668451586118497334171569418006704281952022611597218015621414912 -2489529918597875085256271193680876368852530909559968605233021443727687797440512 -105197113071910516420544054278070972818358588151209671675114698136431648804175872 -146783911149691239803259475393330228083363239321283447773095577119546974601004113414011126228802748800827392 -39402006196322807380529480735708200350750292135441068499159015119090674490425011969581731225987063691900094127276032 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -289479781435444187096279090754304829634841333825118114339468989463902612881407 -4921154517771165976794823611077260188226420779097528013887026680891314618236907 -210335929074260427349925273087400330341762969176928907058821210295697037055753332 -293567262432083559770702660509552857681465552011087182313422615017582594334049882230725897422112180962066432 -78803862104411649792976274791681038936512839345342224984305431018588418073741852364218069806673549869904487834976257 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -3273390607896141870013189696827599152216642046043064789482248405676250539586401155309462588318799202567027652361355087007672582991681286039617010663424 -68741202765818979270276983633379582196549482966904360579127216519201261330156503369125552402467745959144439465483209595931171855583484908981316971659264 -2972238671969696817971976244719460030212710977807102828849881552354035489891940536551869135659973534613352653377437457066688364595029975586445710241824768 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927942346384547376821709077533682106292683703719591705961381851144330942292688896 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678086476367041524431431063149363331436313879641204087364482575431457386887380992 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -5027927973729236057982426364448826617555638513633071601633370220421967636306804394074875752581912318823441521134057891212939945806456965095219525498961919 -105586487448313957217630953653425358968668408786294503634300774628861320362441734354680017642267931657997508802581110468240508909548354444901759395225927659 -4565358600146146340648043138919534568740519770378829014283100160143146613766525878107518060449842889350367392734757892259413192451501507674321802617616858228 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065320804077788940510139153902441161886681396745848137833584621159026421384871936 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295280603154120813299310555349107912285360603554839773512525393061136369205641217 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -13407807929942597099574024997867385471458758537929012740010782671329620832395892888572752773084019014537559577489812491117577843786236284470685416703393791 -281563966528794539091054524955215094900633929296509267540226436097922037480312592739135435072812172267993987986051957066237904767123720151786543110518996971 -12174289600387878166413214698063586008084552752439543567929790665567295715815418231111590395065755769058786587905783068972824403817141129667244831831240998004 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313671759942785296424671006468134461175040959714683214870751934727590598473154560 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382966030240714335998716952049937439856660280732766022505772192030605512043134977 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -26815615859885194199148049995734770942917517075858043397979502765092926124329972428655111861232797616170839264946949360821429169472449630310911451399716864 -563127933057589078182109049910430189801267858593018911357569558066951448610928263080864975923936522902292861422651831330018782606534200414431289839141781504 -24348579200775756332826429396127172016169105504879103405365388510704376920891562453506372447104846739341804544116863346663921407540222847690170071335502348288 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738791660371025265528379582261277249144426462648371370907663322533294992707813376 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367717603690326984946811716693747142752151891016787126125398682196103806102536192 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356940333001073316904952470628102164776064494420927751032420533624771561387982848 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494589072130166493051774845895382319063249135608252819293589384022352148895367168 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049310652505448855163347188999257161699887972266119596676427899155051204847861760 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836431857608336477913270529964062671113379000249084146141508066578816267351752704 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478380902093015377784392043375068982105312552912561330723627831367576754598182912 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463796730242309831072892700701204289449703517933314335935523147673822961969659903 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738573414196133290578519677430526937209668629368371102258744279052431561110585323 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074919347548203719653076095375986365364121321513138688693601455693722933010627700 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999488783198101915710769922695043258370551321796513118492692182863598843554955264 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433215996336650657107187309066885121050915203711655940418348461423410436359847937 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801281 -115792089237105570840234253759177109864155645142784332660520492325483608801301 -115792089237105570840234253759177109864155645142784332660520492325483608802188 -115792089237105570840234253759177109864155645144051983258387538503137673347072 -115792089237105570840234253759177109864495927509705271123983866932915377012735 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801301 -115792089237105570840234253759177109864155645142784332660520492325483608801721 -115792089237105570840234253759177109864155645142784332660520492325483608820348 -115792089237105570840234253759177109864155645169404995215728462056218964262912 -115792089237105570840234253759177109871301574848124040393251359081550741241835 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608802188 -115792089237105570840234253759177109864155645142784332660520492325483608820348 -115792089237105570840234253759177109864155645142784332660520492325483609625744 -115792089237105570840234253759177109864155646293811075523798421635374216380416 -115792089237105570840234253759177110173132034306996457485264635873529144802420 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645144051983258387538503137673347072 -115792089237105570840234253759177109864155645169404995215728462056218964262912 -115792089237105570840234253759177109864155646293811075523798421635374216380416 -115792089237105572447172292031856729075410681226833265616710996755578873708544 -115792089668464716711175474330664206369020689731482237225111632717222395248640 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864495927509705271123983866932915377012735 -115792089237105570840234253759177109871301574848124040393251359081550741241835 -115792089237105570840234253759177110173132034306996457485264635873529144802420 -115792089668464716711175474330664206369020689731482237225111632717222395248640 -231584178474421766263805238767865017716745065074583019773051327118533202018305 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -173688133855763668451586118497334171569418006704281952022611597218015621414912 -1331609026228925620678623413260475405674665237934234339264433695068655873687552 -52685400602978658201947727436005789138242379942982622713439243734744551061979136 -73391955574979118963811141843175280625430620176058294601490730148688473782556220162587303980144312476237824 -19701003098197239571963727475337245584277418381138336973559756733437436614754832864132904228250638407468872200355840 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -231584178474211141680468507518354219728311290285568665321040984650967217602560 -2547425963216322558485153582701896417011424193141255318531450831160639393628160 -105255009116528963893772936667091992866517481434790958388413127523864600400363520 -146783911149691239803259475393388124127981686794512330162116597167705867884585400127309555616235700397015040 -39402006196322807380529480735708200350808188180059515972387897508111694538583905253163017939285493079333045723463680 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -347375826053891660325161479775324877793734617406404827637898376896854209069055 -4979050562389613450023706000098280236385314062678814727185456068324266214424555 -210393825118878874823154155476421350389921862460510193772119639683129988651940980 -293567262432083559770702660509610753726083999484316064702443635065741487617631168944024326809545132558254080 -78803862104411649792976274791681038936570735389960672457534313407609438121900745647799356519971979257337439431163905 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -3273390607896141870013189696827599152216642046043064789482248405676250539644297199927910061547681591588047700520248370588959296290110673472568606851072 -68741202765818979270276983633379582196549482966904360579127216519201261330214399413743999875696628348165459513642102879512458568881914296414268567846912 -2972238671969696817971976244719460030212710977807102828849881552354035489891998432596487583133202417002373673425596350350269651308328404973878661838012416 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069928000242429165824294937959922703126340842597003172992674680280531763893888876544 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678144372411659971904659945538384351484472772924785374077781004818890338483568640 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -5027927973729236057982426364448826617555638513633071601633370220421967636306862290119494200055141201212462541182216784496521232519755394482652477095149567 -105586487448313957217630953653425358968668408786294503634300774628861320362441792250724636089741160540386529822629269361524090196261652874289192346822115307 -4565358600146146340648043138919534568740519770378829014283100160143146613766525936003562678897316118232756413754806051152696773738214806103709235569213045876 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065378700122407387983368036291462181934840290029429424546883050546459372981059584 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295338499198739260772539437738128932333519496838421060225823822448569320801828865 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -13407807929942597099574024997867385471458758537929012740010782671329620832395950784617371220557247896926580597537971384401159130499534713858118368299581439 -281563966528794539091054524955215094900633929296509267540226436097922037480312650635180053520285401150383009006100115959521486053837018581173976062115184619 -12174289600387878166413214698063586008084552752439543567929790665567295715815418289007635013513228997941175608925831227866107985103854428096632264782837185652 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313729655987403743897899888857155481223199852998264501584050364115023550069342208 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882383023926285332783471945834438958459904819174016347309219070621418038463639322625 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -26815615859885194199148049995734770942917517075858043397979502765092926124330030324699730308706026498559860284995108254105010456185748059698344402995904512 -563127933057589078182109049910430189801267858593018911357569558066951448610928320976909594371409751784681882442699990223302363893247498843818722790737969152 -24348579200775756332826429396127172016169105504879103405365388510704376920891562511402417065552319968224193565136911505557204988826936146119557504287098535936 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738849556415643713001608464650298269192585355931952657620961751920727944304001024 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367775499734945432420040599082768162800310784300368412838697111583536757698723840 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356998229045691764378181353017123184824223387704509037745718963012204512984170496 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494646968174784940525003728284403339111408028891834106006887813409785100491554816 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049368548550067302636576071388278181748046865549700883389726328542484156444049408 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836489753652954925386499412353083691161537893532665432854806495966249218947940352 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478438798137633825257620925764090002153471446196142617436926260755009706194370560 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463854626286928278546121583090225309497862411216895622648821577061255913565847551 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738631310240751738051748559819547957257827522651952388972042708439864512706772971 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074977243592822167126304977765007385412280214796719975406899885081155884606815348 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999546679242720363183998805084064278418710215080094405205990612251031795151142912 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433273892381269104580416191455906141099074096995237227131646890810843387956035585 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267776 -231583736816786089484927226016147767929578972263620494977377884571370600267796 -231583736816786089484927226016147767929578972263620494977377884571370600268683 -231583736816786089484927226016147767929578972264888145575244930749024664813567 -231583736816786089484927226016147767929919254630541433440841259178802368479230 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267796 -231583736816786089484927226016147767929578972263620494977377884571370600268216 -231583736816786089484927226016147767929578972263620494977377884571370600286843 -231583736816786089484927226016147767929578972290241157532585854302105955729407 -231583736816786089484927226016147767936724901968960202710108751327437732708330 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600268683 -231583736816786089484927226016147767929578972263620494977377884571370600286843 -231583736816786089484927226016147767929578972263620494977377884571370601092239 -231583736816786089484927226016147767929578973414647237840655813881261207846911 -231583736816786089484927226016147768238555361427832619802122028119416136268915 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972264888145575244930749024664813567 -231583736816786089484927226016147767929578972290241157532585854302105955729407 -231583736816786089484927226016147767929578973414647237840655813881261207846911 -231583736816786091091865264288827387140834008347669427933568389001465865175039 -231583737248145235355868446587634864434444016852318399541969024963109386715135 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929919254630541433440841259178802368479230 -231583736816786089484927226016147767936724901968960202710108751327437732708330 -231583736816786089484927226016147768238555361427832619802122028119416136268915 -231583737248145235355868446587634864434444016852318399541969024963109386715135 -347375826054102284908498211024835675782168392195419182089908719364420193484800 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -289479781435444187096279090754304829634841333825118114339468989463902612881407 -1447400673808606139323316385517446063740088565055070501581291087314542865154047 -52801192250558338720592420408262759796307803270103458875756101126990438053445631 -73391955574979118963811141843291072273010300694702987573747700806753897109677056324904161372390199467704319 -19701003098197239571963727475337245584393210028718017492204449705694407272820256191253740390567495799714759191822335 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -347375826053891660325161479775324877793734617406404827637898376896854209069055 -2663217610796003077129846554958867075076847520262091480848308223406526385094655 -105370800764108644412417629639348963524582904761911794550729984916110487391830015 -146783911149691239803259475393503915775561367313157023134373567825771291211706236289626413008481587388481535 -39402006196322807380529480735708200350923979827639196491032590480368665196649328580283854101602350471578932714930175 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -463167473633572178969854452032295535859157944527240989954755769142741200535550 -5094842209969293968668398972355250894450737389799650889502313460570153205891050 -210509616766458555341798848448678321047987285787631029934436497075375875643407475 -293567262432083559770702660509726545373663680002960757674700605723806910944752005106341184201791019549720575 -78803862104411649792976274791681038936686527037540352976179006379866408779966168974920192682288836649583326422630400 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -3273390607896141870013189696827599152216642046043064789482248405676250539760088847507590580192374563845018358585671697709795458606968065718455598317567 -68741202765818979270276983633379582196549482966904360579127216519201261330330191061323680394341321320422430171707526206633294731198771688660155559313407 -2972238671969696817971976244719460030212710977807102828849881552354035489892114224244067263651847109974630644083661773677390487470645262366124548829478911 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069928116034076745504813582652894960096998908020330293828836997137924009780880343039 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678260164059239652423304638510641322142538196251906210240097862211136225475035135 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -5027927973729236057982426364448826617555638513633071601633370220421967636306978081767073880573785894184719511840282207823642068682072251874898364086616062 -105586487448313957217630953653425358968668408786294503634300774628861320362441908042372215770259805233358786793287334784851211032423969731681438233813581802 -4565358600146146340648043138919534568740519770378829014283100160143146613766526051795210258577834762925728670725464116576023894574377122961101481456204512371 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065494491769987068502012729263719152592905713356550260709199907938705259972526079 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295454290846318941291184130710385902991584920165541896388140679840815207793295360 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -13407807929942597099574024997867385471458758537929012740010782671329620832396066576264950901075892589898837568196036807728279966661851571250364255291047934 -281563966528794539091054524955215094900633929296509267540226436097922037480312766426827633200804045843355265976758181382848606889999335438566221949106651114 -12174289600387878166413214698063586008084552752439543567929790665567295715815418404799282593193747642634147865896489293289435105940016744954024510669828652147 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313845447634983424416544581829412451881265276325385337746367221507269437060808703 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882383139717932912463990590527411215430562884597343468145381387478810284350630789120 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -26815615859885194199148049995734770942917517075858043397979502765092926124330146116347309989224671191532117255653173677432131292348064917090590289987371007 -563127933057589078182109049910430189801267858593018911357569558066951448610928436768557174051928396477654139413358055646629484729409815701210968677729435647 -24348579200775756332826429396127172016169105504879103405365388510704376920891562627194064645232838612917165822107569570980532109663098462976949750174090002431 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738965348063223393520253157622555239850650779259073493783278609312973831295467519 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367891291382525112938685292055025133458376207627489249001013968975782644690190335 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322357114020693271444896826045989380155482288811031629873908035820404450399975636991 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494762759822364621043648421256660309769473452218954942169204670802030987483021311 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049484340197646983155220764360535152406112288876821719552043185934730043435515903 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836605545300534605905144105325340661819603316859786269017123353358495105939406847 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478554589785213505776265618736346972811536869523263453599243118147255593185837055 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463970417934507959064766276062482280155927834544016458811138434453501800557314046 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738747101888331418570393252791804927915892945979073225134359565832110399698239466 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161075093035240401847644949670737264356070345638123840811569216742473401771598281843 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999662470890300043702643498056321249076775638407215241368307469643277682142609407 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433389684028848785099060884428163111757139520322358063293963748203089274947502080 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049793 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049813 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050700 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725447442614227457227324739062595584 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656433007813095902093053555754516766261247 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049813 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050233 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998068860 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725472795626184798150877820353511424 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590663238655151514671362321047903152130490347 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998050700 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998068860 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998874256 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092726597201706492868110456975605628928 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590965069114610387088454334324695130534050932 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725447442614227457227324739062595584 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725472795626184798150877820353511424 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092726597201706492868110456975605628928 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804492314385376101550209867347761530223896585780685577180262957056 -3273390607896141870013189696827599152216642046043064789482248405676250539528505111122163636578388558400357687160957770034872868194181321538823784497152 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656433007813095902093053555754516766261247 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590663238655151514671362321047903152130490347 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590965069114610387088454334324695130534050932 -3273390607896141870013189696827599152216642046043064789482248405676250539528505111122163636578388558400357687160957770034872868194181321538823784497152 -3273390607896141870013189696827599152216642046043064789482248405676250539644297199928120686131018322837558498508682145377973650742121015940134591266817 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539586401155309462588318799202567027652361355087007672582991681286039617010663424 -3273390607896141870013189696827599152216642046043064789482248405676250540744322047682624540545836497330168886466602318237624970233503383890257262936064 -3273390607896141870013189696827599152216642046043064789482248405676250592098113624432357121814940520075482619034317023286013344408313423566152451227648 -3273390607896141870013189696827599152216642119435020364461367369487392382587993646884319095925507685560423629480410862859610793556373668965913865486336 -3273390607896141870013189696827599171917645144240304361445975881013496123690131402592035893426969817507130095546769944436294859219708096290473589604352 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539644297199927910061547681591588047700520248370588959296290110673472568606851072 -3273390607896141870013189696827599152216642046043064789482248405676250541960138984670021478352366666771589897803361273444645949500520519982240782876672 -3273390607896141870013189696827599152216642046043064789482248405676250644667722137982662813640149751161686347309418515094349019382197212686201789612032 -3273390607896141870013189696827599152216642192826975939173488208935725932800837149435385714379543246186290648497804964888790758278625305057301786263552 -3273390607896141870013189696827599191618648242365872170011729141384450890220901201513214892255110592181388019375842333466408570254562768154647112712192 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539760088847507590580192374563845018358585671697709795458606968065718455598317567 -3273390607896141870013189696827599152216642046043064789482248405676250544391763583843312369890919084167973717177251142982205358154525757145867603673067 -3273390607896141870013189696827599152216642046043064789482248405676250749806538140332573743021368560491043870713799540813584403088709371951590041189492 -3273390607896141870013189696827599152216642339610327221565808176378911049023466747537698404183277786513328546533424697934559574993396498366733947502592 -3273390607896141870013189696827599231020504150454714582458523197357289475983448411414371377401526491679131602692682728102747150941048946159040820412417 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -6546781215792283740026379393655198304433284092086129578964496811352501079057010221381608981414894675657741181312185450892349927259180362294169996099584 -72014593373715121140290173330207181348766125012947425368609464924877511869627112435197698795563841432235152994434039959815849199850983985235869957095424 -2975512062577592959841989434416287629364927619853145893639363800759711740431411145617941282053069630086443366906388287430573041939297474662700263227260928 -4149515561151917970063980879658984329121545601994364142417198664422513839111197699172162345948983320467412955450619523214805173006772819821634534083476383305649350220585495278125056 -1113877103911668754551067286547922686741510866027480451801205771594405262067279965549349805236490784434644432217557085433113670824527158622454044965264710005088764708750074507711939872817152 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -5031201364337132199852439554145654216707855155679114666422852468827643886846275003140947898975008414296532234663008721576824623150724464171474078484398079 -105589760838921853359500966843122186567820625428340546699090256877266996612981204963746089788661027753470599516110061298604393586892621943978013948211363819 -4565361873536754236789913152109231396339671987020875057347889642391552290017065348716584132596235985445840483448286843089777077128845775173398057170602294388 -6373655901930312136397229380018414714219291845533790964022390273310518664835630740828468347180083644862604791413143861086903235249375531875415632227109732815177852120235280974370308096 -1710915231608582551713494414139930175751263819507052019042219365550193794154006946718799527546620132923873503879834751212220192959692406650822198625814311433918724450856792892137390922191077377 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -13411081320550493241444038187564213070610975179975055804800264919735297082935363497638824919477115110010650291018763321481462521130503783546939969688829951 -281567239919402435232924538144911922499786145938555310605015918346327713730852063348201507219205268363467078699580907896601789444467987650862797663504433131 -12174292873778486062555084711253282835683704969081589610994580147815701392065957701720656467212148865154259678619312019803188288494485397166321086384226434164 -16996415738478256005382065682640745424583520600551474607202683943728786624700592414350568583857897019605853142369008857442817767101941225174703991790078567892215019433803845151458590720 -4562440617622195218641171605585119131739518145309275443824727370504040363107841778802650498983287057323120851132922436639306786482391813047523028153385611111096650699850039691106860065028571137 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -26818889250493090341018063185431598542069733717904086462768985013498602374869443037721184007625893711643929978475900191185313846816717129387166004385153024 -563131206448196974323979063100127017400420075235064954422359040315357124861467733689931048070329618997765952136180782160382667283878467913507544392127217664 -24348582474166364228968299409316868843768257721521149448430177992952782597142101924115438519251239835437277634830392297494285292217567115189246325888487784448 -33992831476956512010764131365281487575776433304961101914925762989992063339229796837707649040085893246897278262269437097411921475677734367962673377293012256048251930821609549545693249536 -9124881235244390437282343211170238263479033017227949088672722063740710815798620780741195852214027274750800459122907188212756399131339907812166837856281102721380671803469666181272358359087972352 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094859873731529946340457125253265606438475403785866280608383688705623749572896410942067145463298048566101192878305015324784812428376688032701026114373419008 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991991880549970950312199366524641183224939146662271031480267817849985216020034059681196238639444870941368473032592199965972137496637856883098606701880803328 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129796382947829476458967766910148013704606143230821732648328945402416252950588781261571521001556443284472347875228838802630004274020695398231305757833297920 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934367149423806676145102838607613654154722819645656486389690247571207190879375902466674408624306366625437153384642329830612968823485775565655070820337188864 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913652579960587390491348134625062972314445174807285275675210213986492566598017851511159087524177488138848159695634263383276446008067895330443831307583619072 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988312365191525626829590504228669795829923743060941349315981180118158171906003267339308381977465988796174295002978654348297199013279790646750077514955096063 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754494201209880240584000325008129160445354271438847474339815137513208085015278044023262205436971615772903617650738619459732255779603011778128686114096021483 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384658628623907360592075876577151981139698204998984119146851147983671731411614389956614275866046172190849077078893072151877023366037868954769977485996063860 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862156128917720005745743440993984706875781718224942308267480678359040001816538959392264174062103866018168133971899502152160397795836959681939853396540391424 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998786517303933042382263334991497603092615412736691514529053003863902183881972686605402722803500283404539975834579866034075540617762615960499664989345284097 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348288 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348308 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486349195 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851379715837692741036504647550894079 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352969133745369125558337364934425254559742 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348308 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348728 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486367355 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851405068849650081960057728841809919 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072359774781083787894827604857083060618788842 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486349195 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486367355 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993487172751 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628852529474929958151919636884093927423 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072661605240542660311919618133875039022349427 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851379715837692741036504647550894079 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851405068849650081960057728841809919 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628852529474929958151919636884093927423 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094485907904996976043691563883887462497120051064494757088751255551 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030688453630171908179274851168857493895967146091659465130718732272795647 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352969133745369125558337364934425254559742 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072359774781083787894827604857083060618788842 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072661605240542660311919618133875039022349427 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030688453630171908179274851168857493895967146091659465130718732272795647 -5027927973729236057982426364448826617555638513633071601633370220421967636306862290119494410679724537943712051980205218271310246874207404825120043079565312 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306804394074875752581912318823441521134057891212939945806456965095219525498961919 -5027927973729236057982426364448826617555638513633071601633370220421967636307962314967248914534139356118204662368163138444169898193698787193070165751234559 -5027927973729236057982426364448826617555638513633071601633370220421967636359316106543998647115408460140949976100730853149218286567873597232746060939526143 -5027927973729236057982426364448826617555638513706463557208349339385778778149805986566450609089519027306434917111176946988791884017021657478145822353784831 -5027927973729236057982426364448826617575339516731268841205333947897304881890908124322158325887020489438381623577243306070368568082684991905470382077902847 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306862290119494200055141201212462541182216784496521232519755394482652477095149567 -5027927973729236057982426364448826617555638513633071601633370220421967636309178131904236311471945886287646083379499897399376919172965804329162149271175167 -5027927973729236057982426364448826617555638513633071601633370220421967636411885715057548952807233669372036179829005954641026622242847481021866110277910527 -5027927973729236057982426364448826617555638513779855512783061460225227111700018830069001675707973062867060784130194341090821063981743909114237210274562047 -5027927973729236057982426364448826617595040519829394409013899701157675836657438894121079504885848630213055881501072378459398681793719846577334555601010687 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306978081767073880573785894184719511840282207823642068682072251874898364086616062 -5027927973729236057982426364448826617555638513633071601633370220421967636311609756503409602363484438705042467198873787268914478581619809566325776091971562 -5027927973729236057982426364448826617555638513633071601633370220421967636517024531059898863736614888181365537352410335666745857626553993181131498529487987 -5027927973729236057982426364448826617555638513926638864065453780192670296816241459667103988397776797407387822028229960823866832798458680307546642435801087 -5027927973729236057982426364448826617634442375737483251426346495213648675243201441330980661370995046112553625084389218854035020374406332755338949308710912 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5031201364337132199852439554145654216707855155679114666422852468827643886846275003140947898975008414296532234663008721576824623150724464171474078484398079 -5096669176495055037252703348082206199752187996599975962212497436941168897636845105354763988789157361053109646476130576085748122423316267794415778445393919 -8000166645698932875954402609168286647768349491440174430483251772776003126198629138537507572046663149707317860388084823556505315162762758471880171715559423 -4149515561151917970063980884683638912242885518106777317169197682825935710698226236016050317965274706234630948370185813208398692627647313303331070209408656529114634029765403766423551 -1113877103911668754551067286547922691766165449148820367913618946346404280470701837136378342080378756450935817984775078352679960818120678243328538446961246131021037932215358316891848361115647 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -10055855947458472115964852728897653235111277027266143203266740440843935272613492996060514188968601933917406728144705257702756896374189747980653986972696574 -110614415422043193275613380017874185586224047299927575235934144849283287998748422956665656078654621273091474009591757834730325860116087227787193856699662314 -4570386528119875576706025565283983395358075408892462085884733530363568581402832566709503698886229578965461357941768539625903009402069240457207237079090592883 -6373655901930312136397229380023439368802413185449903377197142272328922086707217769365312235152099936248372009406063427376896828768996406368897328763235665088401317404044460882858606591 -1710915231608582551713494414139930175756288474090173358958331778724945793172410368590386556083464020895889795265601969205139759249686000170443073119296007970044656724080258175946570830679375872 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -18435735903671833157556451362316212089014397051562084341644152891751588468702581490558391209470708629631524784500459857607394794353969067356119878177128446 -286591894502523775149036951319663921518189567810142339141859806318344005116619281341121073509198861883087953193062604432727721717691452934671977571992731626 -12179317528361607402471197124428034834702108390953176639531424035787717683451724919713576033502142458673880553112793716339314220767708862450130266292714732659 -16996415738478256005382065682645770079166641940467587020377435942747190046572179442887412471829913310991620360361928423732811360621562099668185688326204500165438484717613025059946889215 -4562440617622195218641171605585119131744542799892396783740839783678792362126245200674237527520130945295137142518689654632226352772385406567143902646867307647222582973073504974916039973516869632 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -31843543833614430257130476360183597560473155589491114999612872985514893760636661030640750297619487231264804471957596727311246120040182413196345912873451519 -568155861031318314240091476274879016418823497106651982959202928287373416247234951682850614360323212517386826629662478696508599557101933197316724300615516159 -24353607128749485568884411822491620842786661143392736476967021880924798888527869142108358085541233428956898509323873994030411224490790580473055505796976082943 -33992831476956512010764131365286512230359554644877214328100514989010466761101383866244492928057909538283045480262356663701915069197355242456155073829138188321475396105418729454181548031 -9124881235244390437282343211170238263484057671811070428588834476915462814817024202612782880750871162722816750508674406205675965421333501331787712349762799257506604076693131465081538267576270847 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604099884528314651286256569538428017605456878825657453309145227576677640040958663628934986711753291642085722067371786711860910744701600153316510206022861717503 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685997016535133092290228311779699393182243342568533858060017111705822001507405801277674115804929438464460989347526073896502098069769861322166907786610369101823 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518134821037530950816375080180084900012723009565102408761185172833374432544336355999254491087291550036804093222368710535338755936547244160682040485666321596415 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028939391804006928016061215251782365653173126241517243514926534135543223482265143120459593974914299960145058027878124026366738901096709240849464250728825487359 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744918677234543708730407460547799814971332848596678872304212054101958508857983785069504078653814171081658469034189115959919402378281291360614253011216071917567 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060993337019774646966745702917403421794848327164932528377852825068090174463291770485332227948267459582315795169496460350884423131286503255930559257423443394558 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280759518855793001580500112738182881159463757693310434502876659025485224376401045262016181771726965209292524492144220315995858188052826477061937866022584319978 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377389683283207028700508188289751903980158101626870571147683695035955688022797381607949533842156039765710469951572374768688002955639261334238579157394484362355 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269867180783500841345661855854168736705894185140096529336804324566331056293202306177385183740352097459537789008465381198688286330069060424965749033305028689919 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428759003811171887054382298375748166249602111018834608278543065896891835918475267739904598322289093493876924160850328061562570201472890986081244308844897833582592 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780160 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780180 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690781067 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756017613817472060411970538755325951 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107569038383267105337656740400316458991614 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780180 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780600 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690799227 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756042966829429401335523620046241791 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428114374685721685874606924232548951823220714 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690781067 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690799227 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884691604623 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228757167372909737471295102775298359295 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428416205145180558291698937509340930226781299 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756017613817472060411970538755325951 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756042966829429401335523620046241791 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228757167372909737471295102775298359295 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114988014600711094100047318483792100395099830383870222979955687423 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528565474132278603893392907524612093800605044071438784506184623477227519 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107569038383267105337656740400316458991614 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428114374685721685874606924232548951823220714 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428416205145180558291698937509340930226781299 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528565474132278603893392907524612093800605044071438784506184623477227519 -13407807929942597099574024997867385471458758537929012740010782671329620832395950784617371431181831233657830108335959818175948144853986724200585934283997184 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395892888572752773084019014537559577489812491117577843786236284470685416703393791 -13407807929942597099574024997867385471458758537929012740010782671329620832397050809465125935036246051832322718723917738348807796173478106568536056955666431 -13407807929942597099574024997867385471458758537929012740010782671329620832448404601041875667617515155855068032456485453053856184547652916608211952143958015 -13407807929942597099574024997867385471458758538002404695585761790293431974238894481064327629591625723020552973466931546893429781996800976853611713558216703 -13407807929942597099574024997867385471478459541027209979582746398804958077979996618820035346389127185152499679932997905975006466062464311280936273282334719 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395950784617371220557247896926580597537971384401159130499534713858118368299581439 -13407807929942597099574024997867385471458758537929012740010782671329620832398266626402113331974052582001764139735254497304014817152745123704628040475607039 -13407807929942597099574024997867385471458758537929012740010782671329620832500974209555425973309340365086154236184760554545664520222626800397332001482342399 -13407807929942597099574024997867385471458758538075796651160473911132880307789107324566878696210079758581178840485948940995458961961523228489703101478993919 -13407807929942597099574024997867385471498160544125335547391312152065329032746527388618956525387955325927173937856826978364036579773499165952800446805442559 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832396066576264950901075892589898837568196036807728279966661851571250364255291047934 -13407807929942597099574024997867385471458758537929012740010782671329620832400698251001286622865591134419160523554628387173552376561399128941791667296403434 -13407807929942597099574024997867385471458758537929012740010782671329620832606113025557775884238721583895483593708164935571383755606333312556597389733919859 -13407807929942597099574024997867385471458758538222580002442866231100323492905329954164981008899883493121505878383984560728504730778237999683012533640232959 -13407807929942597099574024997867385471537562400033424389803758946121301871332289935828857681873101741826671681440143818758672918354185652130804840513142784 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13411081320550493241444038187564213070610975179975055804800264919735297082935363497638824919477115110010650291018763321481462521130503783546939969688829951 -13476549132708416078844301981500765053655308020895917100589909887848822093725933599852641009291264056767227702831885175990386020403095587169881669649825791 -16380046601912293917546001242586845501671469515736115568860664223683656322287717633035384592548769845421435916743839423461143213142542077847346062919991295 -4149515561151917970063980893063518868456246559698375950587756536729055734994167374393462768872927902323719442868062833710505388341765369659085670114046554508893953405231294970855423 -1113877103911668754551067286547922700146045405362181409505217579764963134373821861432319480457791207358589014073863572850556981320227373957446594802715846035658935911994677692357739565547519 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -18435735903671833157556451362316212089014397051562084341644152891751588468702581490558391209470708629631524784500459857607394794353969067356119878177128446 -118994295378256554317204978651292744440127167324223516374311557300190941194837511451163533099156727968805592065947512434634963758095866547162659747904094186 -4578766408076088937747617163917401954211978528916758027023110942814476234598921655204001575906731685661175475998124294225807647300049019776582702970295024755 -6373655901930312136397229380031819248758626546491494975830560831182825206731513710503689647603007589444461097900561304397398935464710524425253083363140302986381096723419926774063038463 -1710915231608582551713494414139930175764668354046386719999923377358364352026313488614682497221841433346797448461691057699637636270188106866157191175651762569949294622060037495322036721883807744 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -26815615859885194199148049995734770942917517075858025480021565342659241664791669985056268229972815325345642840856214457512032692333748386731585769381560318 -294971774458737136190628549953082480372092687834438280280237218769251658312708369835618950529700968578802071249418359032632359615671232254047443463197163498 -12187697408317820763512788723061453393556011510977472580669801448238625336647814008208073910522644565369594671169149470939218858665688641769505732183919164531 -16996415738478256005382065682654149959122855301509178619010854501601093166596475384025789884280820964187709448856426300753313467317276217724541442926109138063418264036988490951151321087 -4562440617622195218641171605585119131752922679848610144782431382312210920980148320698533468658508357746044795714778743126724229792887513262858020703223062247127220871053284294291505864721301504 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -40223423789827791298722074993602156414376275613787056137990285436422546956725749525138627318121593926978922528313351327215884018019961732571811804077883391 -576535740987531675281683074908297575272726617130947924097580340738281069443324040177348491380825319213100944686018233296413237455081712516692190191819948031 -24361987008705698929926003421125039401640564263417032418105399293375706541723958230602855962561735535652612627380229748630315862388770359792430971688180514815 -33992831476956512010764131365294892110315768005918805926733933547864369881125679807382870340508817191479134568756854540722417175893069360512510828429042826219455175424794195345385979903 -9124881235244390437282343211170238263492437551767283789630426075548881373670927322637078821889248575173724403704763494700173842441835608027501830406118553857411241974672910784457004158780702719 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604108264408270864647298161137061436164310781945681749250283604989128547694154752717429484588773793748781436185428142466460815382599579932635885671914066149375 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823686005396415089305651269903378332811741097245688558154001155489118272909160601890366168613681949940571156703465582429651102002707667841101486283252501573533695 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518143200917487164177416671778718318571576912685126704702323550245825340197532445087748988964312052143499807340425066289938660574445223940001415951557526028287 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028947771683963141377102806850415784212027029361541539456064911547994131135461232208954091851934802066840772145934479780966643538994689020168839716620029919231 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744927057114499922091449052146433233530186751716703168245350431514409416511179874157998576530834673188354183152245471714519307016179271139933628477107276349439 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061001716899730860327787294516036840353702230284956824318991202480541082116487859573826725825287961689011509287552816105484327769184483035249934723314647826430 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280767898735749214941541704336816299718317660813334730444015036437936132029597134350510679648747467315988238610200576070595762825950806256381313331913788751850 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377398063163163242061549779888385322539012004746894867088822072448406595675993470696444031719176541872406184069628730523287907593537241113557954623285688794227 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269875560663457054706703447452802155264748088260120825277942701978781963946398395265879681617372599566233503126521736953288190967967040204285124499196233121791 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428759012191051843267743339967346799668160964921954632574484204274304286826128463828993092820166113995983619874968384417317170106110788965860563684310789038014464 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103233 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103253 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387104140 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459868939503685406252196573451649024 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244438742234592791551002580626351155314687 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103253 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103673 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387122300 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459894292515642747175749654742564864 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885251244389573011560820270072774986519543787 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387104140 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387122300 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387927696 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098461018698595950817135328809994682368 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885553074849031883977912283349566964923104372 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459868939503685406252196573451649024 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459894292515642747175749654742564864 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098461018698595950817135328809994682368 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203136793202344373787504455353495951720786043729710449014652010496 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610924562281057205526672594981748963504456369757652130346410658173550592 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244438742234592791551002580626351155314687 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885251244389573011560820270072774986519543787 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885553074849031883977912283349566964923104372 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610924562281057205526672594981748963504456369757652130346410658173550592 -26815615859885194199148049995734770942917517075858043397979502765092926124330030324699730519330609835291109795793096687879799470540200070040811968980320257 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329972428655111861232797616170839264946949360821429169472449630310911451399716864 -26815615859885194199148049995734770942917517075858043397979502765092926124331130349547485023185024653465602406181054608052659121859691452408762091651989504 -26815615859885194199148049995734770942917517075858043397979502765092926124382484141124234755766293757488347719913622322757707510233866262448437986840281088 -26815615859885194199148049995734770942917517075931435353554481884056737266172974021146686717740404324653832660924068416597281107683014322693837748254539776 -26815615859885194199148049995734770942937218078956240637551466492568263369914076158902394434537905786785779367390134775678857791748677657121162307978657792 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124330030324699730308706026498559860284995108254105010456185748059698344402995904512 -26815615859885194199148049995734770942917517075858043397979502765092926124332346166484472420122831183635043827192391367007866142838958469544854075171930112 -26815615859885194199148049995734770942917517075858043397979502765092926124435053749637785061458118966719433923641897424249515845908840146237558036178665472 -26815615859885194199148049995734770942917517076004827309129194004896185599723186864649237784358858360214458527943085810699310287647736574329929136175316992 -26815615859885194199148049995734770942956919082054366205360032245828634324680606928701315613536733927560453625313963848067887905459712511793026481501765632 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124330146116347309989224671191532117255653173677432131292348064917090590289987371007 -26815615859885194199148049995734770942917517075858043397979502765092926124334777791083645711014369736052440211011765256877403702247612474782017701992726507 -26815615859885194199148049995734770942917517075858043397979502765092926124540192565640134972387500185528763281165301805275235081292546658396823424430242932 -26815615859885194199148049995734770942917517076151610660411586324863628784839409494247340097048662094754785565841121430432356056464451345523238568336556032 -26815615859885194199148049995734770942996320937962455047772479039884607163266369475911216770021880343459951368897280688462524244040398997971030875209465857 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26818889250493090341018063185431598542069733717904086462768985013498602374869443037721184007625893711643929978475900191185313846816717129387166004385153024 -26884357062651013178418326979368150525114066558824947758558629981612127385660013139935000097440042658400507390289022045694237346089308933010107704346148864 -29787854531854891017120026240454230973130228053665146226829384317446961614221797173117743680697548447054715604200976293164994538828755423687572097616314368 -4149515561151917970063980906471326798398843659272400948455142008187814272923198032362182862636233194257798982950421921859283989975045057116222539817897880195107299245457329667178496 -1113877103911668754551067286547922713553853335304778509079242577632348605832580399361350138426511301121894306007943112932916069469005975590726282259852715739510261598208023532583774261870592 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -31843543833614430257130476360183597560473155589491114999612872985514893760636661030640750297619487231264804471957596727311246120040182413196345912873451519 -132402103308199151416779003649160129911585925862152547032280277393954246486771590991245892187305506570438871753404649304338815083782079893002885782600417259 -4592174216006031534847191188915269339683437287454687057681079662908239539890855734744083934994880464262808755685581431095511498625735233122422929004991347828 -6373655901930312136397229380045227056688569143591069000828428216654283965269442741161658367696770894736395177440643663485547714066343804112710220232844154312067310069260152808759361536 -1710915231608582551713494414139930175778076161976329317099497402356231737497772247152611527879810153440560753753625137239719995358336885467790470863108899439653145947746250841162262756580130817 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -40223423789827791298722074993602156414376275613787056137990285436422546956725749525138627318121593926978922528313351327215884018019961732571811804077883391 -308379582388679733290202574950949865843551446372367310938205938863014963604642449375701309617849747180435350936875495902336210941357445599887669497893486571 -12201105216247763360612362748059320779027470269515401611327770168332388641939748087748156269610793343971227950856606607808922709991374855115345958218615487604 -16996415738478256005382065682667557767052797898608752644008721887072551925134404414683758604374584269479643528396508659841462245918909497411998579795812989389104477382828716985847644160 -4562440617622195218641171605585119131766330487778552741882005407310078306451607079236462499316477077839808101006712822666806588881036291864491300390680199116831072196739497640131731899417624577 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -53631231719770388398296099991469541885835034151716086795959005530185852248659829065220986406270372528612202215770488196919735343706175078412037838774206464 -589943548917474272381257099906164960744185375668876954755549060832044374735258119717430850468974097814734224373475370166117088780767925862532416226516271104 -24375394816635641527025577446122906787112023021954961448763368013469469847015892310142938321649884314254245907067686885500019713714456573138271197722876837888 -33992831476956512010764131365308299918245710603018379951731800933335828639663608838040839060602580496771068648296936899810565954494702640199967965298746677545141388770634421380082302976 -9124881235244390437282343211170238263505845359697226386730000100546748759142386081175007852547217295267487708996697574240256201529984386629135110093575690727115093300359124130297230193477025792 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604121672216200807244397735162059303549782240704219678280941573709222310999446686796969566947861942527383069465115599603330519233925266145981725897948762472448 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823686018804223019248248369477403330679126568704447096083031813457838366672465893824445708696041038089349758336745269886787971706558993527314832123478536269856768 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518156608725417106774516245803716185957048371443664633732981518965919103502824379167289071323400200922101440620112523426808364425770910153347256177592222351360 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028961179491893083974202380875413651597498488120079468486722880268087894440753166288494174211022950845442405425621936917836347390320375233514679942654726242304 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744940464922429864688548626171431100915658210475241097276008400234503179816471808237538658889922821966955816431932928851389010867504957353279468703141972672512 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061015124707660802924886868541034707739173689043494753349649171200634845421779793653366808184376110467613142567240273242354031620510169248595774949349344149503 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280781306543679157538641278361814167103789119571872659474673005158029895334889068430050762007835616094589871889888033207465466677276492469727153557948485074923 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377411470971093184658649353913383189924483463505432796119480041168500358981285404775984114078264690651007817349316187660157611444862927326903794849320385117300 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269888968471386997303803021477800022650219547018658754308600670698875727251690329345419763976460748344835136406209194090157894819292726417630964725230929444864 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428759025598859773210340439541371797535546436380713170503514862243024380589433755763072632902525202144762221508248071874454039809962114652073909524536823734337537 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369217 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369237 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375370124 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059367521063656309566056683439915008 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359572341733174351521905894486461143580671 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369237 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369657 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375388284 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059392874075613650489609764730830848 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714366377989071593120791173386635096507809771 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375370124 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375388284 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029376193680 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232060517280155921720449188919982948352 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714668208448530465537883186663427074911370356 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059367521063656309566056683439915008 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059392874075613650489609764730830848 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232060517280155921720449188919982948352 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658808948056801636687333570487095450302346014633024309124640276480 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956886017953212059983935494810864097103954951317623033660270768161816576 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359572341733174351521905894486461143580671 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714366377989071593120791173386635096507809771 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714668208448530465537883186663427074911370356 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956886017953212059983935494810864097103954951317623033660270768161816576 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356998229045691975002764689748372695622211821479298052100170973354672078968586241 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356940333001073316904952470628102164776064494420927751032420533624771561387982848 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322358098253893446478857179507922865306010169741652157703419662355722622201640255488 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322409452045470196211438448611945610619742737456357206091793837165762298096828547072 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143893629499169185576181884464199941925492648173412559179111095560753183550196779689242985226007697858242805760 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839342888146918434783166170184693410567941044063248355890210060641243042267219249909278356373308648560435022417966923776 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356998229045691764378181353017123184824223387704509037745718963012204512984170496 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322359314070830433875794986038092306727021506500607364724398929372858714185160196096 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322462021653983746517130273821176696823471012557849014427468811049551418146166931456 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143967021454743897697021332797750154768995199240031013214671721427772200944298808869207707477643789246163582976 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839362589150016560350974735937953781522707574833047277069208888782017716525143078981667386487019683415106886591490031616 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322357114020693271444896826045989380155482288811031629873908035820404450399975636991 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322361745695429607166686524590509703110840880390476902283807583378095877811980992491 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322567160469986096428059655039986026180994416938874733662852517561710683534418508916 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187144113804806026290016988775982866377398593301552720816949212048465670236564031854638024422248837098678324822016 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839401991005924649193387182732009754361293337380257178225694035197917214268726395822062022825600369901284890985197731841 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094859873731529946340457125253265606438475403785866280608383688705623749572896410942067145463298048566101192878305015324784812428376688032701026114373419008 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094925341543687869177857389047202158421519736626787141904173333673737274583686981044280961553112197512857770290118137179293735927649279836323967814334414848 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604097828839012891747016559088308288238869535898121627340372444088009572108812248765077463705136369703301511978504030091426764493120388726327001432207604580352 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714242674878487840834003055584974512311279435699658711463016289149904593484340885392177976886554761380392284766887296383377531438844432307956945337673417396461755078202559317439655444480 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246284526591342437031681239983319539526781594837816341634508518304645466356502238250467323544284041214993247041504034911017278877525141160830047989182088967849339008843158178926846443884250136576 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604099884528314651286256569538428017605456878825657453309145227576677640040958663628934986711753291642085722067371786711860910744701600153316510206022861717503 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604200443087789236007416218065716994137807991595930114741177894981086079393684798558895591853642977661424896134653233764437938313665342050796316745892588683243 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991608660215200487068390846630250983103347579842957522649251826694366600364687088882702648429896450552619117266018585410546229110997207295204025736789114979613812 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412720612181264857001052430220984113268041169605999590508062896262224550689635337404935307273071388896041934422145344989624941219868920801067012539335366443652893627280972574012918747627520 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609247994327945847107914640183330172921779846117146457366173098936464424065745394177917220573722025424857132685900951652105144065956814009040322247733762938014573252644529306221744476122866568396801 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604108264408270864647298161137061436164310781945681749250283604989128547694154752717429484588773793748781436185428142466460815382599579932635885671914066149375 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604376420566869716589289641637018783873739957116440329505083820642555140110802669417280047271073521902034892613836704611035935709522917416503201529607881752555 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991616269146200728800216611801810127154786923875939583363805473384872024513789137775055652502231066465498825685213756435722942522208572934826018659818328603753588 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412731234941101404944921415057286735598751533834754608191706076555894968957595202366608829373308066709416677670496300854621297134400773366760311827694929412487970664448286142577095835910144 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609250845853331860720581567860521618110735834371472259589597881444469377912314348012749304424693462091781531933248204739790571152550336708446718948563290509314250430570778299468543445592009405890561 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604121672216200807244397735162059303549782240704219678280941573709222310999446686796969566947861942527383069465115599603330519233925266145981725897948762472448 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604657984533398511128380696161973998968640591045736839148901163764524169521933285087621776811924646252669191487273304485299716587362327896765846276336504537088 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991628443435801116678383025016508190740795008428692022923642908982717161594994213919278047284283105556469108703169967516000633619212296016544041585057832865103872 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412748231356839883200926797122969376340902726747459017819013799634941232234309731571032186453764294705643969095616201282861266238109349159903099797080432346176126701359673948281490070568960 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609255408293949482915800209032127203229867573886344178263242729439162614582767038791751242970046692831998959612856194724542144602162985656541483592372993404805860714591881919095033611090303465291776 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -21430172143725344039741447416747135734328099194269775938858685112579378184657786065906763159178676625205218492566825428477050725853377832065983208189713200681844100397174224127137557678646374287640475087188412914436146644713764873912909317614682237526728015428718464118732506826116885039758058750738432 -235731893580978784437155921584218493077609091136967535327445536238373160031235646724974394750965442877257403418235079713247557984387156152725815290086845207500285104368916465398513134465110117164045225959072542058797613091851413613042002493761504612794008169715903105306057575087285735437338646258122752 -9740013239323168866062487850911573191252121083795613164211272383667327384926963766954623855846708526155771804871622157242819554900360224673989368122224649709898143630515684865784019964944777113732595927127133669611228650022406135193417284855873076955897883012352541941963924352470124250570037702210617344 -13582985265193575509847153720301345363547163848715837516812932466783689951768234614081082594902676106779962952908124868514580193484388163974010229906267257277629370088801142633029220476374120830201819937481485605394893790147430680868494971780019587951193256398520172478623000296862688521766032969946888901935204417993802764714508288 -3646154850295010964902624396817474390410105702108556391759286100433934630952342968680077393668206518645350322207926570056063791163256347839621106322437804761540352091937936756225487349008505906910901544548065233498934923554616145309059470154014938195304963669835205443004851378494121810273694832757966522610366086517324182782563251960938496 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824629198276151174244463087285660418287344529756165187857520911105123842660034918703411836307538943107430287596439419222071804002615797677806638572665083165692141839780886307603102541747070094713562715543794785904326970568977820621271154145831782622467599830140102357487631119091729219499088809459332415487 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316998862077737405693327418524701312676891843887526247248550546356474902078886714910989494826726018489783987340302135409223113547673217455618750193884849347528160194394640717423882001111685525241940621668818619861722020482087095397955107969291288249444329152787862322599066175858052440630467418058473340907 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753593451678086770450460508976164978853315225613389231240714869753894387304963876717058650152672811697170570980613598309383691870969784601585900594981479511955574221514648792975451023932379869175500758313625655872192484128483431743888460039720362805862274612216016775291210943444487297807108709430373383284 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444769282811977804250882370944636426195458324080999993435959869852668433762589077466327036503679884932798114847311436326315773506106222447586090692873957009455868034159802460539867856658115952688726716502746285402567852398888356313324109937916420499689593669109023205291494317874286388534278585340917710848 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448790006268973158495533882062541204142975446526119117611259327045262915006773486865554835011098276863061710309734987275751369401000805245194174420363093639844254247196438980433865369554332786383238465709007857728072714580953790040537248486657816917075965510971703569173409460696212044812838396933722603521 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046272 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046292 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957047179 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571754105966758923615108084021592063 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998595854119759254624519943537861725257726 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046292 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046712 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957065339 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571779458978716264538661165312507903 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132388005401501458178023893787435686497089486826 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957047179 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957065339 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957870735 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255572903865059024334498240320564625407 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132388307231960917050440985800712478475493047411 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571754105966758923615108084021592063 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571779458978716264538661165312507903 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255572903865059024334498240320564625407 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172976888286874738812007209510607836887249117247073360525221953535 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834198122532121152290057037619484503120616341536220725647709322168743493631 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998595854119759254624519943537861725257726 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132388005401501458178023893787435686497089486826 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132388307231960917050440985800712478475493047411 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834198122532121152290057037619484503120616341536220725647709322168743493631 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463854626286928489170704919821474820295850844991684637003273587403723479550263296 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463796730242309831072892700701204289449703517933314335935523147673822961969659903 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655464954651134682993025119737995967430683808765164544288322764969771673602221932543 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655516308442711432725606388842018712744416376479869592676696939779811349497410224127 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418968698206766676988716306797306798322733884687580499409184197685426822573709166274146087840056749258824482815 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230791227421993503490763661597227832901047900460489592404378000871316144391892888932790742958211751174484073818548600831 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463854626286928278546121583090225309497862411216895622648821577061255913565847551 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655466170468071670389962926268165408851695145524119751309302031986907765585741873151 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655568878051224983031298214051249798948144651581361401012371913663600469546748608511 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526419042090162341389109555755130857011166236435754198953444744823552445839967811195454110810091692840646745260031 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230810928425091629058572227350488203855814431230288513583376829012090818649816718005179773071922786029155937992071708671 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463970417934507959064766276062482280155927834544016458811138434453501800557314046 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655468602092670843680854464820582805235514519413989288868710685992144929212562669546 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655674016867227332942227595270059128305668055962387120247755620175759734935000185971 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526419188873513623781429523198315973233795834538066888757179285150590343875587544241222927524862886150078906499071 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230850330280999717900984674144544176694400193777498414739861975427990316393400034845574409410503472515333942385779408896 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988312365191525626829590504228669795829923743060941349315981180118158171906003267339308381977465988796174295002978654348297199013279790646750077514955096063 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988377833003683549666990768022606347812968075901862210611770825086271696916793837441522198067280137742930872414791776202806122512552382450373019214916091903 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060991281330472887427505692467283692428260984237396702409080041579422106531145355621474704941650537643531585080628703730450276879705291828941050483608186257407 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357569626768432140808575645041867964802739431380147844841991693339296041823615960460885574377967295802725391623284537619891699379074505410081618976696929783046658180816608368840237121535 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193136923234669388921625539957892128983675047329276337314997651683620870545893686589742398612991638706405781463837141767414520114039309101060121091306762606872851395428061281540895495284831813631 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060993337019774646966745702917403421794848327164932528377852825068090174463291770485332227948267459582315795169496460350884423131286503255930559257423443394558 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061093895579249231687905351444692398327199439935205189809885492472498613816017905415292833090157145601654969236777907403461450700250245153410365797293170360298 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581065553667691947064071335763629958507536971291296797724320534291858012899109421989559045671132964720559347339120710084185252623383792198306639785840515561290867 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809363939133154801301027002810441006720532629601680079641441871666413942137974612480004014870562801430464267529001742230861455387809150874169137212974389956039478530383586623064319329304575 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987194846724589174059804584483304745511236739569637917361853588069843399469934785626256495648790733022348545220323284758961541307193328176980552320835887611653596765031114209324358525174267150073856 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061001716899730860327787294516036840353702230284956824318991202480541082116487859573826725825287961689011509287552816105484327769184483035249934723314647826430 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061269873058329712269778775015994188063131405455715404573791418133967674533135776273677288507587689842264965715961378250059448096107820519117250581008463429610 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581073162598692188795897100935189102558976315324278858438874180982363437048211470881912049743467580633439055758315881109361966034595157837928632708869729185430643 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809374561892991349244895987646743629051242993830435097325085051960084360405934477441677536970799479243839010777352698095857811302341003439862436501333952924874555567550900191628496417587199 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987197698249975187672471512160496190700192727823963719585278370577848353316503739461088579499762169689272944467670537846646968393786850876386949021665415182953273942957363202571157494643409987567616 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061015124707660802924886868541034707739173689043494753349649171200634845421779793653366808184376110467613142567240273242354031620510169248595774949349344149503 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581061551437024858506808869829540949403158032039385011914217608761255936703944266391944019018048438814192899264589397978124323228973947230999379895327737086214143 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581085336888292576674063514149887166144984399877031297998711616580208574129416547026134444525519619724409338776272092189639657131598880919646655634109233446780927 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809391558308729827500901369712426269793394186743139506952392775039130623682649006646100894051255707240066302202472598524097780406049579233005224470719455858562711604462287997332890652246015 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987202260690592809867690153332101775819324467338835638258923218572541589986956430240090518045115400429490372147278527831398541843399499824481713665475118078444884226978466822197647660141704046968831 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824629198276151174244463087285660418287344529756165187857520911105123842660034918703411836307538943107430287596439419222071804002615797677806638572665083165692141839780886307603102541747070094713562715543794785904326970568977820621271154145831782622467599830140102357487631119091729219499088809459332415487 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824843499997588427684860501759827889644687810748107885616909497956249636441881496564070903939130729873682339781365087476356574509874331456127298404746980297698960280784858049844373917323856558456439120294666670033471332035424958270010283239007929444842867110294389542128818444159990388349486390046839799807 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864834347781343330617766485833689155244342862322740766531245793324803678590666777224684300553400226472956960854182818474553886146506790304524648561957579118102201358139311004818244759424154336225453007670995834731161023763072355512991590658521370041017185970985137026180965476310937373226864619089102792294399 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382945944971643394552440333018544909113467554612749139183355056603618393891608739152695663135014913939388630223870254054412894344624289470541920862626549594209167573670063373732089922672967834116510690953316456889794786342129422505749576092463192554010284300112795761408992790940526935790646439671993459275486838307032042854165296185343 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287321881537782656997343103601327303339215017873806116005292182631938677605782562544625650995475250258938656613604058187487401993335543020778644703484269290201404867303981882236730798076805901958398370897225037198612474339112946064484584134538861612429607839386002942061840246087892662062040346796957431605546122752671420426796831614652542615551 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -70149324220868077495255175920561715987048031760661657648151596049581927701126644407314161773169938523306300574636470765864723972756401953860971729649236966380158623144886433123904089438954731413136105939102963525135105941885179620757765851918707538235369974386271618715130954505741977781211162121976618183601835461375440982077945936461543052837790612502383395739504991310927477668395382345950562697672932264775996511143505676632322113137860859914092542 -771642566429548852447806935126178875857528349367278234129667556545401204712393088480455779504869323756369306321001178424511963700320421492470689026141606630181744854593750764362944983828502045544497165330132598776486165360736975828335424371105782920589069718248987805866440499563161755593322783341742800019620190075129850802857405301076973581215696737526217353134554904420202254352349205805456189674402254912535961622578562442955543244516469459055017962 -31882867858384541221593477455895299916113330435220723401084900404534986140162059883124286525905737058842713611172275963085517045617784688029811651125578201219782094219350883854814408650004925427270360149322296922173905650586814137634404579697052576127975653358560450706027018822859728901560473184438372964447034217195137926354426428121771317514775833382333253363605018550816538600285701276234530746092347714340690414314723330029390400421157760830955060339 -44462416394276340862910922260424396243409287509660999151319246748739914958385539746634290306946194641774613806241018935810494168792436919845175634291349159454369843636974567461269377050915985253151305716782880097347068525737970911517541922272679697044439338383672966048059649363603197225258288722959100457996166747560663282330850461947328029840291593918843260847507401028001791571453882893980386821221463169721351174430588439919666771233696844315006704459189491148327636741499387903 -11935289041890653422458560211344367566035064229836658432562760370881500666512962196005301087896114215912896266386318096236327886697921869831329245013996897234320551268335648449529409274273397774372980607395957227900784875294585648183090035692841379465274178288623747782900764555478041293866792687681839672394696352890749545168747009819987092335714242876928113812840773743724234722513540777715455219485249003286896896934489723171984857306038613096377208196921847281115147426887448334304280576 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/bignum_inputs b/evm/src/cpu/kernel/tests/bignum/test_data/bignum_inputs deleted file mode 100644 index bd45bdf96a..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/bignum_inputs +++ /dev/null @@ -1,15 +0,0 @@ -0 -1 -21 -908 -1267650597867046177654064545792 -340282366920938463463374607431768211455 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/cmp_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/cmp_outputs deleted file mode 100644 index e4e4112402..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/cmp_outputs +++ /dev/null @@ -1,225 +0,0 @@ -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -115792089237316195423570985008687907853269984665640564039457584007913129639935 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/iszero_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/iszero_outputs deleted file mode 100644 index e32c9c6c6c..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/iszero_outputs +++ /dev/null @@ -1,15 +0,0 @@ -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs deleted file mode 100644 index e744abfedf..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs +++ /dev/null @@ -1,486 +0,0 @@ -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -21 -21 -21 -21 -21 -21 -0 -0 -0 -309 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -0 -0 -0 -169 -1232997389102826268860991065105 -18618193491623480687965778108529490131 -56424335153278151789523409664996995581162067431087086213061556134977632914449 -28522822713801268588036943398667804191745915805046109428618723188679866502161 -31706899640538131462557950751123123992423743151283050819612655945089903928361 -0 -0 -0 -501 -163832143645086957897416441857 -130403518609156362105805396406315208861 -1950081577919450936472563652220396332042682835950151822696779432336405037057 -89705076639198166424843052158202309145112564081865083245095301525986942648321 -161580799869287829122678259656507339171378764462605635944908315838074450225541 -0 -0 -0 -361 -349605011617414033395053481789 -245857331472368514819046594566028058751 -16196520401421423385786338368215415982698439852342927927980513551465948892989 -25448670785549471741073803593965176921136178751681542787519830340854942519101 -208746275861806526568796522022506082930013066308764940459575687169043802837726 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -5 -0 -908 -908 -908 -908 -908 -0 -0 -20 -0 -444539926163396060618371891200 -265738677898336061407141787570363252093 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -0 -0 -4 -0 -458477653689271929750159360000 -25471671292789986475502298488957923576 -39622934661811821553763744630506213929196932096668444518123439737845028225024 -11662422067291787594026815204283885427659195788081136348012846295902003920896 -196850617542733211416217278410575369737876420896781853126317893743949824168141 -0 -0 -16 -0 -491348889049443876707510517760 -26309649457780133338343455840727251651 -12836249910756408599207651670402691470189457253048470571408310293697570799616 -89957821137359056393801843026144645477720961240687064113356371611922523488256 -186628143002875342454100646575613221588500043779267673627434610024013060416436 -0 -0 -20 -0 -995158961158262555374402928640 -134121906180378853498930390838142450437 -6919681730812989105961680682293875199438003339556752544938537021004054528000 -111415284413375575279069783743044849713138799010818236990829948938800785784832 -2671371225382503991550810752302543668307773911036096851249073522360117594907 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -160 -0 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -0 -0 -1 -312 -0 -305764342221524799935332959201681860527 -43179616752915754651548109831528453372307533305539601542967080491130306953216 -17577324162381258630474635337405333643146914114762554675606397827067886436352 -24302243256145833886805174285503224697924343810958724170274784478667392432942 -0 -0 -1 -104 -0 -308509185836607975341692036936651238716 -55518020932873470218319288289245721296726991639699521321270937184899723952128 -112628490590563884833405386435939751658584809004247538619207709896844158959616 -5922841801814876533914360761483372163277450765372489528717658348886181069591 -0 -0 -1 -820 -0 -92377192736350021881806334564236622586 -30966933304654384056562480896427815565136977421206545694335698204395214733312 -82119970857858981914834549178898483174623261423734314091990446749364630061056 -17335345295685259772267515166522970642719156121663286480022840319361908909536 -0 -0 -1 -556 -0 -132190390248470898896680912765144782208 -26118405364148793884093212339004353321168506206731428575114534916913543249920 -57371932048380839143645765917598495087491748880125189544237269309841806983168 -21907868082798520198592834325003250611892002016702940923441999346792832444518 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -3 -827 -633825295391748780828659810303 -0 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -0 -0 -6 -883 -1151402479073526177054249189375 -0 -46236487016914252810359942606653565611806735985113905556189104784313205915647 -10141063897320778511376522974043618320650062159021091584566880470490557710335 -78190442020038551706136595408815344419350554279639655464038299447602848702950 -0 -0 -9 -257 -148913685258606776538030407681 -0 -15737081446634197346448238543290111090647984834849920547563580853437339795457 -17796349368611048392083206935955698801625485975991150886750776582747396767745 -87042183171820405426823900509602437553981065786933287231059322442248803404950 -0 -0 -18 -33 -501502021891302937219905355777 -0 -3229040287801688211109538153528907359157521678782897421936034456493449281537 -43674379635609232592374808570316762057057239464140599073454048485319171375105 -206574116687852983759940670284339037133974987477578735810964428283965343595925 -0 -0 -6 -515 -140262897948655524553823354879 -0 -51292049059523452610506083592610024599583816874803914322375719502751486443519 -27936184963965071766976378876421641742859886164075202272073313001233759338495 -13449010363647692703508349667378797433516756740069562881064298383864033242575 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -13 -120 -145483342680740634262446800896 -170141183460469231436539398536531279872 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -0 -0 -13 -588 -242981830122332196998498746368 -141543884952374228579411301124356217882 -0 -110892457497452005403581430237946183767136503117162566153549053970000476372992 -189155222634588421211288058609537932434095476696578287565656039885444474631257 -0 -0 -1 -40 -447233008701926529533965500416 -293158325512187360695470417449441151016 -0 -80370191198978495857548168233658974342470488197481929432131097469881528877056 -57378722967569161858531872288267370459698099854972954033385237027489681458401 -0 -0 -1 -412 -442771250107780063583096799232 -154158747942568048877474815599350831266 -0 -83509925808939041219467836468971382374645162274742749382395185649452566183936 -130737775035935455315064283330339683168756409303633081183629254780389959201676 -0 -0 -13 -172 -791101013288846030830655504384 -153476591093627709272587559525863637788 -0 -16620630689278877851873420518904814002737213549417357007588250730268341567488 -135713742926877628027400301750580746786742856540376082400065882631878268256343 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -296 -920477174324206192629261533184 -9284550294641477953059815425 -57896044618447473228882389021020048158893283581286713298429387432951596187648 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -0 -0 -1 -576 -394649121464702711197113253888 -330655358119337089933926862882373999920 -19171508671161797156614710182257630792593514679646789380628699765537557708800 -0 -83428280595193522049620352001877162940388365577102322638744416599597810157500 -0 -0 -1 -36 -459149829216013907374855159808 -212514946886893049623689635030161573600 -30121405346709427309695531890582421791203074728165296179965859962342252478464 -0 -42435402933391614285072618340065308663204835805291910450325661435301593697950 -0 -0 -1 -440 -111480890522555005842506121216 -169821875159927040016519827500463371995 -2219279707535425826019993588723715376924609733291444929791090369095624818688 -0 -125210688265252194710996471277583934881834379414003707914695928587186801398100 -0 -0 -1 -192 -973066898438133470773494415360 -61333559381831070683658258698802878545 -53535005721204496876936515275724889845817409334527168350061458281042045566976 -0 -35340054082346922390131417025011886439703502295343037882213639103251549897900 -0 -0 -1 -1 -1 -1 -1 -1 -1 -0 -0 -4 -771 -891784777631246170114448424959 -1327929446201306785262743695648620545 -57895602960811796650871631801676582813791887579127636891104569893774562426879 -115791647579680518644692972256970658065423327120836162316857392245886991466495 -0 -0 -0 -1 -43 -597446603600272666897934188543 -335986975837048587653674597853034419125 -4960875119562861993119998040929006214931859756877807299872704131313015193599 -83869314384839239184918994159181415456293389193220202757469792852202756243455 -0 -0 -0 -16 -417 -51915870197464503363519381505 -252258564697022016064972069604476995730 -49219110417420231511113764378238860427237875311237523872333835875498405658625 -102059959947892163134217117691190804610012473033509494849327043219849901768705 -0 -0 -0 -4 -857 -1131685075580794109740493832193 -5591051640426086522729162377581522820 -49306867245429292665712135123169680084542437782148851479415184718925278478337 -25169675183049498057756995668265957449819867477380947926503567596715595792385 -0 -0 -0 -1 -339 -813911142051622544908469927935 -4998080979218566999874015281917916555 -36694329931173559945696159169023191810080707655034316545018050435414281420799 -28634157081599137704071405870600522487246030369305940433755254306653996056575 -0 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs_full b/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs_full deleted file mode 100644 index c1626f99c5..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/modexp_outputs_full +++ /dev/null @@ -1,1575 +0,0 @@ -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -0 -0 -0 -309 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -5842587018385982521381124421 -0 -0 -0 -169 -1232997389102826268860991065105 -18618193491623480687965778108529490131 -56424335153278151789523409664996995581162067431087086213061556134977632914449 -28522822713801268588036943398667804191745915805046109428618723188679866502161 -31706899640538131462557950751123123992423743151283050819612655945089903928361 -1626949887851522216050918195251855667936019233629512014857416262882459162506846482867598420124655122796334698879679058256496280388512107842122550266897 -3456644180256483296524124683106942480722843461305992677766332070622386105650335816178720659995652203171430416751945868032819203139048842456194585731900908 -6658621772694490037410881754126282754619191463278934006643576166056374826395321115677201742806056121601730868428249390651828408651815409287407418905560894 -2188719266686453452930405899199235044753192686954839466176162624683199938127424638750308949204554559166758220326224401920489333667474175603924673521175569 -6123795749220870070888957688126499811687439102580957107200324670516588221814874336490966980509479995815372272026912905873172415251713982723428918913460049360806724424126068570078686852874442462644585471618684175096178614463856372447395923736155404199722474001519774161213336837801565210105511353961489 -21733503076822274238215696841643994124465086209245797663103653662160183433375325239843496825811576935617701675430794656070743382504699894663869396198800858049123949931636855462200807567378261808374350727356914491895652225869425999023896449493174464626623879583913773127532285149008185808669981457659175411110681467646497612200879789944122607924142423967190374513947785974855081897919456043288039161362376188824242511311222201467365944959686354630547972 -0 -0 -0 -501 -163832143645086957897416441857 -130403518609156362105805396406315208861 -1950081577919450936472563652220396332042682835950151822696779432336405037057 -89705076639198166424843052158202309145112564081865083245095301525986942648321 -161580799869287829122678259656507339171378764462605635944908315838074450225541 -119111099232869461943255917709559971029471489246820147748389884283220680875449970114508127905572794881898139347855563980545398801715283004632963481601 -2340550614088062323035521333507933205420749928049240408457893198554975629380160875595843975025528241912895931129450956293623201875314056184463318790281141 -9658263392514739927662836103088426769134800307082054844647958520598804818351406352355729785509682250873421347556031006118797390529574721358615425274369903 -4647428455414528310735419222346137778331384012634538551014412602117023622880652353771726881116679285768237724443091476402706202263926819772616832419102721 -4105943439683327149496995432917731734349623330617416765444758819531334403459555965005600275959433826680513222328275949889828341311187920607090355380046540579908947040106371789193081113409035077098486792404385747715667974409598976733840748313362165044117491316370103556122704636978835764926194391187457 -28645769207390246795138025157012310806200949947458739337397211179141156343070918052774971042600619689369921890873698767518883214265612034430865062720501567943382208222029969252942575712244757709457154177152894494535042763023966349374143513752560929258976200745379909865727692592552226677876935892285307870731885285227994511501363645824725317338462104668462291125838004650196777287385052153407326433125418796527586257544647700218132360787318720777100831 -0 -0 -0 -361 -349605011617414033395053481789 -245857331472368514819046594566028058751 -16196520401421423385786338368215415982698439852342927927980513551465948892989 -25448670785549471741073803593965176921136178751681542787519830340854942519101 -208746275861806526568796522022506082930013066308764940459575687169043802837726 -1405046333573254154362728566205440659065789596162373947677922992002779706150301916091557545001891022758808422492046510466274149060187173986171371114301 -4109950494713993893142341968876859588899697995149574488387185284937507905151071268229544837244462536429462991654059099118254521939414590134998340719446475 -844468128021093833521017258842640197099986073495355910940365822372902152436005754957689228609364360833723273104884193579513808541852965164830299937335266 -4841490524461293458775865719630252706317774131797304941603107709106391551373400827563106022147424343461775948749633172375219539070706909069136692738510653 -7273173343143062730116325603650929579289657910222550340667872300408581237430197382410051906687144077202863842569199267502562443871512075909613158282763060439777587356925584672588460920490499485650537891223823337073005846524449165906099576249848470870580763810627114753982636643198831102678792829914941 -28046651509673296558629424584063249731669578718145238261130675472681779110268140721442904856558473621167727640615713345067811595168883183810857635560068796253449136272338112002671317203041266904678503071806393932350113469015095445921306266450082664767442783548486830460889239098543969021176454257806145698621983814164824010062639366791567674987083305882183188142795298379711201433370817683665471805353684719806585009665707704695044458674998071553291930 -0 -0 -0 -625 -268138222663085878899055263745 -24352877979184920020466043837322220276 -34834821705580047870320807590104282154881192418891909778995311440625022271489 -107743694625338367548278288079708411290661245510293131426406157730911501680641 -118236082284243482508848267847953427609046466568857110070954823921881387042791 -314573941438267599881423465112523804707482828315168316649965301003974020258305280672271797781262619454377248842022918324247905069090181693176119558145 -1855138457669725370795603609175257635850787505475418783821961151316324964106211402480574231460442281436102848418947738954752184731638654925034081955953702 -540244355168038810439245856412365092477075663815074675095751794574495975677637107674742162091364766891790053303512224504116561928038681836474597949011431 -22981118609480295020302390537085998300920429526471726567398293023445956296114258286587907654003543675558782486527003079440548040281611791112262372611325953 -6951084942072485004602792642768728173731086340286788608361716458550264564967198485402397629024961093884371645054422216571333979237852755889053521204926773487912585360977600077358240969038752377614031729154197921202596341838056184283438478913472604440906205314054471964590715571751484579200485577195521 -33110177016061425855873050546700017053104395816945007989361323145125706671247445069352845357430211170094709814842881235921295997550815440918489134011703431383697905373158706778267396782608658244538288231998579363509402374934461434235027334113339168112113235815389810790459912467851237692233387577938141749136860402850772218835907235676691685574301095898691939348359786379380091090548413315980192736618829302554133559996331787700989395178469522588716520 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -5 -0 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -0 -0 -20 -0 -444539926163396060618371891200 -265738677898336061407141787570363252093 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -131765835135960660552807451524089028158233849469678584969822208 -0 -0 -4 -0 -458477653689271929750159360000 -25471671292789986475502298488957923576 -39622934661811821553763744630506213929196932096668444518123439737845028225024 -11662422067291787594026815204283885427659195788081136348012846295902003920896 -196850617542733211416217278410575369737876420896781853126317893743949824168141 -2683172422101341871005722746258376340307644953044191751444605309114182348157085796971409744620711498580309302182985308212586435414886374286350975238144 -3484767657771443762420853002633790658478025026595023030399445962591501743459655114168018705061836715787328457528577115561192630399480939905028160741471812 -826369288885297875981086985856579353870084500166446900173411432842694716669231467968748002518734902939451616083284004742033317955616293179658208664891021 -22202087655311450647135419149250531087927146350676835345655785380157598138557194753184373294689489916297473124960377821312544654416163136210039001929744384 -5867290145612847972373997073309915340902862989135230389100858222930181445218524888102172986857355110271349287326639814908840476463002707271202642441777343076399506363351555575169003649515921567023847735339868023560115487675698958077623452675911334000167018425794800395206246266829715066932889888227328 -6613183629137681279087117062720950270493246965665653353814545527865422606963860172688377962562995370224937823909402574900579151120138866568996446233013596125260317722731462888479963743727090415264356847381807593700110142796538796057208396283278444005900325957636550564820775653606338375016287872257899328947954420594292130424139307631549548532332505581708062755002798299585128036685598543159971540741219646857517061331622628267531564244017650020495747 -0 -0 -16 -0 -491348889049443876707510517760 -26309649457780133338343455840727251651 -12836249910756408599207651670402691470189457253048470571408310293697570799616 -89957821137359056393801843026144645477720961240687064113356371611922523488256 -186628143002875342454100646575613221588500043779267673627434610024013060416436 -57810819572866260801492155256759413876483749626656973452292976127618654522846001818070108358802224938885657621123322444739974859616822961140014252032 -3586935919752527353984027215088868451772937854013928716747601409083300019440067660008036473811750553056666280422401084064457379199486614970622678033275267 -11435172354316143741773241295887896087133672859698695578907055835282817586890921066517171600518967766241323412371609567006600523639894522363112407210324509 -15149040910228070805694652326182650730845401534077532391632591405118117144643051256922675471878552887101021773709591676502861584900921169634374950540279808 -4352157484305171917414400736736974322230700579288245632185873247491184195730898991437344106792548923888580847271187361373793947913900504263300740461685586606358268444829299316120497158281612307205034189058550655426163375786508423319573157431875873496129188997627673849109716799860744235677774874935296 -24655669188850607011881278212500400404795975111909544352042335778191251712079013936428534764805902649880208931889745231758199080159268053714657146238568540885026847910322826747799581068534312565470606227666766265055047378610529765358823154325332792516751882522879055713682140011583363538951298044003038164972718406208736221386819603161486554136555558914468701937507614273119563961498468609340048830696861579728696267046957787891673696324284268296592010 -0 -0 -20 -0 -995158961158262555374402928640 -134121906180378853498930390838142450437 -6919681730812989105961680682293875199438003339556752544938537021004054528000 -111415284413375575279069783743044849713138799010818236990829948938800785784832 -2671371225382503991550810752302543668307773911036096851249073522360117594907 -3196717805164369921128204528085730572110803813535280667544983013152498020676968653158980718912649444544541678773084521623843027455344890340658923962368 -1238216679956500444264616028232887647747940490327785007262817775307688862239704307927037312429375849475227131933476467593816834523153314124523020288669761 -11027839566584582470519059073521721670466350661122931561173065258823257321580571220589430064206794766082774897831215317085884716390531116370426677289293467 -2022634134337910980433929460688286697109516578140587883097911933500075648632085115589857581520940210394331242638360018211669767050461136520845948293742592 -10563633569776338863997290139173141475448369840952853889830314442522523629045149771574493906944834062931170774068081579834010118509008087930549452650473545745495642812824093639365842736796360564731533873032609439082530461159518786091690259460722677617446344350444358408993792066302845337558798580056064 -12824372483412385861669199400205004566724858538474303800494747762440269818404951059835352877380356415578282393020015717678870100435248949572316256128400151457357751827937645568676301032707345577683626030686702589840795543017433728119877924868065460364994171679438527788229515928801213836621134264837100427090704057536254578168099404041165838330491183445948965724854110273982983705590693431410760655261855587976233430383255504302646591767978918856150863 -0 -0 -16 -0 -1074929557660986703509960261632 -310308103079793704575757950092990989086 -45830377016999964195415645114843325971494172471228418297876829545637299093504 -101060401631390047762632218125084929475187708866471415222687033363449381912576 -130474939331530224408995396282441784260419370120207375947563617259552477561186 -2355941672152922857080918796873312219472407301725598088408859346047953344326591361786096096705419672944173727217808802505470853131296892573634755821568 -1316454184270635300204354290028403652975569924791362044247281669809677739990181839549155145392827806973474851174980544451539377747682379975465127356763282 -1438923259930704879657001108769207409876637192084506601145086731796010515496862959970405934371385188574753305943073462813023599491372378876317837370133527 -3751312292780009820304966556683277538490745683704566160236438372682961982986048670343223416367070064938538731816396930813426231559274819821553090255912960 -1940812635972614747466109277992726340848630072707912304640929127912329064087768370597314351590495738864766600213011565689114652892971392013558976325205708385347610787529794002191911334996478270010718764337052847791754345043741034784083083057127201125434592632894903882614831512194082202201086343774208 -18021429550902313767357511567759601756356647595430959756983217203030344977480124379576430774082535283607744829695070380577186356624450300463936384113907659808083311741619551253897275613952178963423684191041978549151808016907300923588567042148170623295160006737511147422878843457848402746737171046772562256330802876792353279038296767776587843369343160581335263159088902098345089167246852603087883635586088364841422928542822495523562426663120058625130224 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -160 -0 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -0 -0 -1 -312 -0 -305764342221524799935332959201681860527 -43179616752915754651548109831528453372307533305539601542967080491130306953216 -17577324162381258630474635337405333643146914114762554675606397827067886436352 -24302243256145833886805174285503224697924343810958724170274784478667392432942 -2455073261000987918671945239574537486815749129964150471432702836905807585811396531610253905941293957332101512899044268740293992626007607127083491262464 -1077242566599992264249169571444336551411248487528972966637628471569432402384081668659908518747150256909990581460430565820193233307152827275575539899170758 -13073126603118771003805243197447306343271660677454831683154753707889268850436361661628016500641989777790467968280342772010149753216162050495370020985042625 -20998284069276981893190892536141895412205721732777582241948778013718640245971369156599887283883776856426273476192138787335689583588114031186267728857006080 -9895687224941938536992396259410735585498593485949826080868857632522617767715344081138466212990213054852400162837201310103538489800752934384336855267244623540847988822661005638222750684585548680702427543946047149085737687298331380026993105619948605279977576581565502199750299350287074957960820551581696 -20330471440166965706846550975654713890652417870226965905923146732405275304116721701265925830155132264142742283294001589707864787012405943213404119056019767248830063561795881859861133225475226440778669219796000363700993800365618586534090055278009338085683400928935050079339354878794718829630509418239605013719652286315425501679172072558159972643172621667859406242995437705040600030465945478758984414424005354845660305153595541783513391622742931412397213 -0 -0 -1 -104 -0 -308509185836607975341692036936651238716 -55518020932873470218319288289245721296726991639699521321270937184899723952128 -112628490590563884833405386435939751658584809004247538619207709896844158959616 -5922841801814876533914360761483372163277450765372489528717658348886181069591 -2564817866139487541320504390805327358442199437592476952716613284652304314970931783459808490723737608880437784464822337666800292869999879039669023801344 -398463165885851265042497914635418511071393473395199568610621293995767308754233006664812655610002899538162554806154059048581305437649155727562069976883240 -11845528388829065193137342380492024118612741433871500160171557205928409375950091328760923663375034610212896359622832576619866946421226527978810823517767195 -14358105553296630195074581259093482904061967322039003650287242246847189245898106579740731184467142158444184654938258232190794449000467281976153220954718208 -8724046093635123581045526611633144774030370511834937151107124782757791671626741053594651970417496109952491903631645243345303037829497694196648996824200842520327610130549622341880795147265298435073802163280558766387420771897374896142381275499545438532579692773311139765116038449092364593363996255453184 -7405751147045042170442233630583323728853684512846881489652126352481138580343808254155794109418214211938178940574968048906922983783473775456657957578956020062439863911134378708160968666080910153080712910213618483308281268671163181505833074812656056876069748103205570600234366976045578890131475420080402034328956554170169080621020401502037844882798216390950223171848402002782939936087102304599378409563146797567777945011228369651410924564026853389623899 -0 -0 -1 -820 -0 -92377192736350021881806334564236622586 -30966933304654384056562480896427815565136977421206545694335698204395214733312 -82119970857858981914834549178898483174623261423734314091990446749364630061056 -17335345295685259772267515166522970642719156121663286480022840319361908909536 -2314109225631884097329071519390891278705119245506353961085436228554532183298397816728391508885600113143907388260489256179338412441709786163323932246016 -4631405091549513365618104032938964387036004178571962489969520427453823044584554995003585135178458975230639830757208179222570849484879646249622084312888560 -3444397905407459769678888603161852413323651620442902175963208412202586537665061311535470334927067187735566811052881866689633902610439151941964220310971721 -11022665256141102472940378463699540949238908279778600071282259749712264960911250408144499157327170064963756957223649027433072458668570655608803856874471424 -1149493853622624808951893133000311601994554292143458692563726487021442743023464156391272602913618081421849882373383362805133207036962919354651311719256865291542887462144185133870512659168195548443958858491083766172641138569350640961793151838633119997925593177565959971136032627767125937725101431586816 -33826235027620277221304225387441637043664748422971678909986538422912010422112157161893624219767292791117193745224367096503738217947106120516761159479154541434507081629430040858090956433405889392516115304242345520261752268880565405366762142190916629498630348949119250396008683103444433062400285561745138697466209715485695816216568719770239333912527091815214766386375891923409276947021071960719444492160160428059721106325731604292930332234337842204147864 -0 -0 -1 -556 -0 -132190390248470898896680912765144782208 -26118405364148793884093212339004353321168506206731428575114534916913543249920 -57371932048380839143645765917598495087491748880125189544237269309841806983168 -21907868082798520198592834325003250611892002016702940923441999346792832444518 -1983010347367670533586158746720918426725859238638826101738778473039940830888567731427474683445100376144146431698278643920358008341796317600412673769472 -2606640266791684811701307747992020704987556721798189736831558708134786779427826595516656788456057404569214323026273791700577064277010116095681833143649967 -13236721874325271723025473459287198396620022329708721480674722710511848773677038674125983763343329458361710367424043866874114509274517527946107266648826241 -2194764262264585667059858125941452105865892742975859841108712998947329687982788884647082000066632025028061458492389933022294535208755309187928296471396352 -204309344949056669756457560943541502699162518130681389120968275752795085274348261696673034188427085066909553427932479215637901119300398120607913517490261871972031537818140203177045230095689669755307530773417745785587153318345703950240765166902545254968306769674012995910371703479321012180805203525632 -10264690826749513230358184346151757544435920255861917426522952736046646856221133004608608156420505462277842900802805838197750732768067254277504971522375036317591854497981434809048391519641178668320883274044427240323533445722232511793549340418570864089915044452302975506872587143937799291446332426997988486186420527441812688737993113263455373353444533552218632449194597995340604827994474495437980620590990737309478013598033891846127947111075468737667005 -0 -0 -1 -340 -0 -998905004448329441454522467488976926 -4927413081688125725138099137632369118723706439675800220006419132836783063040 -84352496855087273591478468036086052663893064063467212113548648838828702826496 -107903726561082941806970174768933557491971859433891364382659859844852394460986 -2319608449060187999684790351453024136538626650066803987926996295704247719880909540662904401039557482146640056079214446055684288473572080013990468517888 -3229174536762564836328011009025477898799910197080321024421312234854337921760217907684033379377552188666880062823173769265434474194314757844200157057703739 -1423443399352876324872592588473564301454095472277230446652620290446968276830664095418889947537872334002573975742103104497418388304230155249816071500187318 -13271987893729173924722826693052254951121133786126776376721905614595108288958742926463506237918989779603882913708829786515628223071604050168421092211818496 -4601804950488826494167685270760036664893236871832882663322149997831635784436681349022970436277158840767511957327023782099178816414168280990846944956415328077143836700852459969097230280911772452313608645578171031885121285719728944681182534906015347671757933737842458574792144739909126764366189368967168 -857479341893762723161505496595417165049229108770456259194779683064981414515307526837194732925160817384742675728910002446344631831783238186297049084225178616281383135711434226661298337670136802627018979396403252157951565007991996142431686702381810961834168285921445713777780679851079387977357401452731948432492393075599084080144393344786427912661179463547435908489113931675491545959285551896625752610448451638448866450487107364387582471184159571074175 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -3 -827 -633825295391748780828659810303 -0 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -0 -0 -6 -883 -1151402479073526177054249189375 -0 -46236487016914252810359942606653565611806735985113905556189104784313205915647 -10141063897320778511376522974043618320650062159021091584566880470490557710335 -78190442020038551706136595408815344419350554279639655464038299447602848702950 -5264101363851496851433282540836956541101368807777991293344941388857765825339723752116476539712642325105142882152577944513410405922445245546495 -4274984277410983807532089925150768740931153586735292491383528412859129258433112273099949231101888023707157602612028830310955239728286706980227996295680904 -4270974226494341897280469825731968602385022467387106892501039803249858350601697035152008485439522684257776791254316572547412071137653017182505839419643661 -13396349486729820333578617551105577176007659045388305976467765207187599750765003306881228272040491956095560694041316948995285357929407282941862450720407551 -7272107039633935551591400893562218992924676126105717460127870788013015797432989950658439810996946118473016444182279548084821341534588106843981486932573866517856077154029000374861836839944939375168692527383494491021504979898639557323084128794258468279454347871141949852954622679013053097034606228537343 -4024681230464577896473641062977638052721373802614679861781599094800123563495932827294337297078407941193782141971643023681636515761542479169594195464691208343312685562380387487284859733536618386758347477945214430213026727223935953464725954742778965827480842612641410510935813810716514672572547641952771526945317395473564193078364064275666028733643505934945383834412116242058173431754061071146756970189972698298803191543547137198977609076610984071659602 -0 -0 -9 -257 -148913685258606776538030407681 -0 -15737081446634197346448238543290111090647984834849920547563580853437339795457 -17796349368611048392083206935955698801625485975991150886750776582747396767745 -87042183171820405426823900509602437553981065786933287231059322442248803404950 -1872579959911606123270219006911173139292436983412774739613566899721054729108647438934586909747652198625290890899088832075544795394544595455034418790401 -1412681115817651652595578657596182058840351787817337255523617847758399731969803154217604617416738018019082279140815098858103221944816565427541047989874575 -4658367006324170812599422256080523502806559591300942419231897265664273942632706886970881430455450954648568107656318501922464893981721336262565505150036230 -14176910549874618607407657930219555895908368234965503682114333452984200436110890119648814677868732070859714001297650338292337460853941658691514163789824001 -10406141366637179727319354575345523585198131445101199614041074944321283006099873985593501657875961054722659983805551437203724122242706464697758320434979188004227317962713115722036138880945569626619984608791769942492732939213456452058202944264921993463328233802720857330376187422666409410692321365721089 -10395612500766716889498607750936120296402196259538392550447369284462443521703220089793251095458371323061297217472250908800671528775762556075103359185483070524957305359923584052804148040373365959462663236012867000124244297047788081721144621971838346991977347509145949856725062202597193816565726641315796367190832625962761358471335171016260228986247896685407131636015822155281779300035844200755378496158753851995591046914832248761433986938772195242877368 -0 -0 -18 -33 -501502021891302937219905355777 -0 -3229040287801688211109538153528907359157521678782897421936034456493449281537 -43674379635609232592374808570316762057057239464140599073454048485319171375105 -206574116687852983759940670284339037133974987477578735810964428283965343595925 -992419486452661222145984173974299929589195994084254138342964889584701966790230864615423021215758227245793655600475560820231442393114209491163228930049 -867054567978498531065761454390276935399048293801759280371014301861063097032842313817626802406894612171009202210526526325159367606967184116106222709354994 -1139630468367775490336631812613571548165746938068725127033848437473514644842035232747054391798198457303514508910901267746988126523368144746838663920634335 -23229321847788976756725283113649930370792069582610592470231250035730463613494249923319913356844479204498929051652932802538286625056745478043326957712572417 -9736393711512156676697476147793419950291123704462141453822865116456122510755024608271599163197384361427364997715319701954677607307667967970314752567786721165005692082957947082637259982017311319355343061387874272954404686630047193927081844593179586832512413662625777018863736941974537158057318938574849 -29060966502289446762773527309129862532230651461433207672335349512468314509838153919700009183613587271911577714742576064666717735555966747507902189346161650683338259890112722032957837515796161015553488353420265757652831337573527786397820733107579265093849101520705267722311390949184609843752198524487159831619663983822993580005359239978622465631388243890591655090541561296753761373713020979145034193471076120681878728773637249747647754915387598872616720 -0 -0 -6 -515 -140262897948655524553823354879 -0 -51292049059523452610506083592610024599583816874803914322375719502751486443519 -27936184963965071766976378876421641742859886164075202272073313001233759338495 -13449010363647692703508349667378797433516756740069562881064298383864033242575 -3007865154522879628838942692472346158555510514358321404385880396123667658951604878264737502736736088022367734668256726008347601098598257350026528292863 -4592124389902639952157884879500633941880823326525745966966661569784155220592348399170518240885296517746840773500441608980855453698091666787607843899145829 -2534000532084385254011173048792691142730414486367806853286959714601853302234763393270798424710144792599095332446397093107565422159020315343985501774159892 -15542028513463832582045168836396386001625310864606581833373826691519685781220976702479707834361513658452848273358428791133185408252751328679278502490406911 -8412092699337531037787046468081884678528099171920466557150153840517555441701513747481625075399369031563363890720070512929533552898722630769019938157826781770070834212210476738149888508884995219178808489342005439633046794302879951435958690454691212867490392351796728559169919328744714016812403628441599 -33830106619343559855814362535589211924203444187711545573142435195593086511328847987971132905556265209639736610793615201190028737913698317064723958072416856829114676871668002147741395866264126547513200446705626505358831619201264825403452572963238108829829155602404009157481163421152595415563917960605621467418213148184629070795051181898610631600475141956877575705748780551030079535279390822434842453037166286438591706030638299501460332593582408355701860 -0 -0 -18 -793 -197025322184063122457151668225 -0 -2920290130103280979299916267396345225782651892499153454132172827501589954561 -13539515947035890751742634672716012403055571371579065400912721307212126879745 -158307368218366335347174104263331952261761856873568291783365034272918926713725 -1815215034323212852396070290108011856137567892027865591453387561513196098766849400352437123459701948446227688759510102669983999161585665721657907478529 -1313998427018914497731975785557000192540482097274033470133500010806607846954570954898528158955314391602627353029912678650049483923494107529991028097123237 -3558766711272482011504448167665545486379425587311683496485361619290623375415884515332307272266520310480366471739749365076632262898225547952923293595733728 -12553285567146229581143238572266763310070996960196734618418238189114111487087176302379917708160855599798506766652438515952406574783180790667512374443900929 -7334817924922815993844247909475070087281744359403537502526299289084906210615638233675595384256475356219608076645306747447902878463733829119335042011404584250731670549991672963716637969622660364460577985065151746629898564582075539489021571565895740532432194811300868623563387061791622447110534725632001 -23782765486299170619653377638001085226491596574584464898704365460889585549109410835130873109130617681428671842761358877647827448636728589051462502446489865488918684639573926641287700719325755142839567735191800755724523772975690463187286534957742763295127666200814136161150077106827630178598134515070629372319788894871157470118252128383759870860296840401062420257746959094446951505893201604983152709936368275205715331071029074242829807922867113197544311 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -13 -120 -145483342680740634262446800896 -170141183460469231436539398536531279872 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -0 -0 -13 -588 -242981830122332196998498746368 -141543884952374228579411301124356217882 -0 -110892457497452005403581430237946183767136503117162566153549053970000476372992 -189155222634588421211288058609537932434095476696578287565656039885444474631257 -2273620924019535126443377586964826655364563651525119527623848142652704686460888155148600115764101538394624015930243455367013584122475807191504328327168 -4718242244578560577933620411853546035676043730739668011742858611491785678155178214146108560032528947886096115390359535573786208426628238424552910518225609 -5354055707309912886614410393569876624773886934053676926684306993550773407750553762949175437211369217571333055727536737153756343047932603108461352921558471 -25365469084896632438831937795363726978720559453448504867481253590410035300967847867532559492662554029011867176416570131146842894254418120377083085899956224 -3757578315429890131235493992649120836389777957623924867570462633554550213501972098599500200214418806050260619389176706179192857127356531256489639401245300261047944289856878017480929565003165920312519588880225017152460044735034054827437950584392201188531715789576969371957511327370055692497232100065280 -15253647830407583141547363309274688061512324567554978156617633130357138545424717528865172974543531851717065650176201323923438841684204225724333600965120694028156933393649297273989140526164630013338776321111358526273453216724232977621477413581907629724731139256634361519074694441451017597636834753579301989604484256575167632226812528730857727215779131743352342648896678763239802119364145369163238668734832577049562494126042421431166789603595812731097644 -0 -0 -1 -40 -447233008701926529533965500416 -293158325512187360695470417449441151016 -0 -80370191198978495857548168233658974342470488197481929432131097469881528877056 -57378722967569161858531872288267370459698099854972954033385237027489681458401 -1266685019038549670156131957936105569332778969355417709861995585153701875900859319531983550779488151228898464900331805671022343202096261388828403564544 -4007316844468480444786209097240877242135343232573279363036730296408578470321526904806238868824798952651885510964017728127623908601645102032546751410140438 -10809419853862469069424671860661466104583061712836069230246390212316112130066125544420392691125603322947467815628376833408555549515946984480656880021651380 -1850019664799561828446887689859810128523634383437118940053115370804552962470182093340221950762057683211340120378944062888849213207787569638787758996586496 -10601990121875444106767485014957569656736374682760746624229897705443065241696873816254196776531479775231519679842263077569364013222740741226981954932170832015958461515448934122357561377336766895492078707171525168572538566552146347786295929420700117193632141413315523494643262931844585426006586863648768 -28508439658852678107665155972792842206287661263432505699726272954607239927480675380840437519995918207158667265234219975518214394765767765060644972734142492559569055673270149098731414835043509095821925810223271993800625817447425850539568468486314744453634053786991467939161412382560926663037539162059349437789487898945599562092125838900291727653251158883415512092048307124528711465011479916269612248338449949978973514357464893125428777863416659679547856 -0 -0 -1 -412 -442771250107780063583096799232 -154158747942568048877474815599350831266 -0 -83509925808939041219467836468971382374645162274742749382395185649452566183936 -130737775035935455315064283330339683168756409303633081183629254780389959201676 -1434569753988477104238938016621393717922537334575046291954564311951618243596601987643360144150294172150115473086916074540367171462792383909655650238464 -1628053801365026402929640374014765553703071179544257678606217838338537677419257865674757673512650450839264317003577960931228650240349967706184064403237564 -9528330235712431695508651348872596587940214388314172729001944199117139203073649460506789237728120154262303033791074115687461447137304611451019062450033166 -4143685306341548231504887338288660968707624803700705709343351955022110657856432837240377395875927501705908299984063250507069218496972459859740452129341440 -7458796787007837004712235977661789416985633116148918114280732607368491371782197880736806507445402383850766124164590251775004743970419719155659219708540227384445648790605201658248003617820791276119099869395011354078111731072541949883562883648840249249510616061917989297687935201755531527627290779320320 -32562067415072239193840246016716096268985518768069377976952382448190013331748839889139882498448289400400757853808819969655477710609459023410834554011819177127950022030097667847290699738778525902043243023637500887645421123275436342796803206335835309725086671594130477830499317959884237607103504253659374293236168368765551370942719124768927841271066850440350466143929412219292019549741670650796223584473716652795660385114766444013731211424652116447263871 -0 -0 -13 -172 -791101013288846030830655504384 -153476591093627709272587559525863637788 -0 -16620630689278877851873420518904814002737213549417357007588250730268341567488 -135713742926877628027400301750580746786742856540376082400065882631878268256343 -2763703332326056666675771568612443973251095331788999459280007079557401109082037332230846287789955462113928953782310527076648915665904957779729505583104 -2824436745521083250434559952136747236272578919613634664272602203887075383079538439468738841849895882276490589513884561192461063598286468221544075155317742 -5573805721606072201024464091196637283572352485715830551875562715416314364897955988150179092473897788638830452265884540199145584775259028641318262487547968 -4057677748936070618844094810747404761610038515502652344029344965685330894561953958031306249055086692013728933315233447834639061360828462307224389794398208 -5784796441501303882265576458065577676796946906096587650440461985290957058829054847129274276669244130440701590711328886296578934710291199277222000695005904568910944144582690667672945892139302539970077461280831973659874645207539604298268246794000404198399473879519441764495134256354634643900506642055168 -27926326319599679689321531260972663838094871826824944182829437453915195129091090522882499755811750977000485175430688110045958476439969398119085050907218285133398254355136892348358892640344189516520338960373270419222146816179324538294913966113727777599048121837592524129719217066985421105982471108005619786655759673241565670064548335049859971927753583710716463045164399494432220069188699341199218886010208466355717064435978272444257082416306333129756637 -0 -0 -1 -316 -71681696331238174643235848192 -237948749361170373273798232100232473131 -0 -83778207020986728476145484258908144082269934037053254715152909632561590829056 -221509419269785716227233899606918240392102327497753358712085651922766861983551 -79315321146264542418544101067771002872575912085812381774279752184308118738724006743714953988893825146071572131840361116250475308904572550776268783616 -3141911224089662508005439140547996848411545433412668670184330989833051246389721569721933639157984163331306326586989550527910321306108944965118472936437373 -7056836934790078890629897242686908388561326030807269602244791722619134700685270860402176255162872248124156286310896170748542196945067054699971451991857017 -19653627676393504480845906820110557077219860495796321885850081681411361274754697849285945541928921052518914208933686885858752894441356909201046802838585344 -9683163169799910671354293907568672381107851254294002957127298074004784481037839670632521780649121780401926430183897832969166328037150107405373952334255388250551814999528496252119738140300795402325170176550137471892883221550198049424126188627968546323114246254076277484385665176149283033551751023165440 -16624156320060803479105655673708019093724859687856669901597323414016554716036267842673966767033477560761233327486461318339447602081002795923903537066139452947433702150477385673218681847304679734427371239598965310386307842392299911576146706135987880533258592628104184356529185963203696378503916725011966153849031868468134727041450767305498104186987445591841485782186937410888737747186818628076918609180767095586934198055427262999789790553748249680495030 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -296 -920477174324206192629261533184 -9284550294641477953059815425 -57896044618447473228882389021020048158893283581286713298429387432951596187648 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -0 -0 -1 -576 -394649121464702711197113253888 -330655358119337089933926862882373999920 -19171508671161797156614710182257630792593514679646789380628699765537557708800 -0 -83428280595193522049620352001877162940388365577102322638744416599597810157500 -2383427170914810336472146115059610133938440685431786051901133872674812685256214433004766571822808713024702089068992214313240618263661712821448505032704 -3543131743813569641335946273683240931826614024019546282265784836005828417893383322228810942263163410646159776819689834231870373605456177912242267705525812 -3201888256116239379504709172928241207634570507519243666267842079354934745091409880730742354222322722404797424377578771802961012486596039415971972097760189 -4721437724763666216912653616574178996088489462586407187533602132486864388960603129501403388719024042140502457650359696753171122610737975161314352895623168 -4837005771188282971935407089657043487019168586830902406943047384547231889109685650839979581515180335872804870881921663586570464571734302300526117404207560436370497430406529093043461008746914986949167798918225893839078999027089145630276892534029320307629830535688150128766532774699466359036038881476608 -14649538351459906058741226567249552783659862553950272384082762918585814063949755926925837749924987768515640556340283222177993507623584971708636450851372760248421615193323790004038285247571226256925988926564345592560305885533717028697892048480537531768156992921912334188996185887209116041579498773878589854193709529378983879403351880522010001304035516272695800246073052875655027404371065192473535905888797416623200692032732055359224565770757807776402248 -0 -0 -1 -36 -459149829216013907374855159808 -212514946886893049623689635030161573600 -30121405346709427309695531890582421791203074728165296179965859962342252478464 -0 -42435402933391614285072618340065308663204835805291910450325661435301593697950 -2723474109035793261196285209480520006576094880312369272827686699655845402902440783445003022414244095338650103254192634946547862282922709256827062714368 -2366345409847035124374650448074879585773647708631341355179263252964806807879670158353217088899380315556678858307117619065167384939975455280949447515369567 -13191762144369989130760985435688757812828881793396707694471183793997116204175863881278765015080350072806582669305591779418584954819749146010893985699341570 -13301718048073450430547712527884367748161420561064642294015103641915503834019416126543999762601710015216018615418920000282926562454589067268249095837319168 -9391084627976509998795459430240744148278054341930566001679916095435726828423076214248815541128823696135595365039592549569884022180775653333743792501900544596968163878667412922650434328739761497861462359470427289461508215509253714046614049256486535514319610185904255475081934449011049104472856091164672 -7354519734624006876179435352514087233297298809165790925922795673494116116655759843604125047003991533731435264281192336103815636919140214933167210447579405287498010619153112075099093006295245714725499708200955637344617781547142646463544824911616543471784407538549422907647454757217835547791373749159887064306770673576013161274312701154428403688789455533688700894782342280314573076414570285083813175010660748552095076178542404200729953294305533949916369 -0 -0 -1 -440 -111480890522555005842506121216 -169821875159927040016519827500463371995 -2219279707535425826019993588723715376924609733291444929791090369095624818688 -0 -125210688265252194710996471277583934881834379414003707914695928587186801398100 -2487150548049686483577986346486345896197470598544823109842683510353284256291612496904302366730404150860292761990843784069916754136788389949069787660288 -4665596396220733640168649174535027159196397070342267963244347648666006311146611981668674060621225920926771133113452576288300091774874699771692345174655000 -3321497911172409553001804903171022673679565313946048256955779288266740305599386551480119034076080803092083102364750364882135550956683614114559617436903767 -12793018574047350137248079672509016968931556367756809163171704828712247163091906244764359814474025125944855607579655662889196220595429789208504167192068096 -53376332098238942237058265874185808937843825750519948116793606680215516009579250363703665595580229234392378617881585484051555178971860102930997202131537779988926409286598509132127582039147943382149262722075187323524856262926753691304631056792968823860069761025941851259838112955696975940302445477888 -19923611385063419051384812211007230050060207617704483913568487020386612675099470773370717657374861044992939563075540978208277455783654511878538950291808744228334204522554009216806163343973548097843046243549174022328700851586804159313601461216565391487177684815873717679059019796761667669491458419882506144601791158476320022142436392910265642137030624038589331580844312683788761159106773836049440285100183481918534526738945630197824116914691687618255349 -0 -0 -1 -192 -973066898438133470773494415360 -61333559381831070683658258698802878545 -53535005721204496876936515275724889845817409334527168350061458281042045566976 -0 -35340054082346922390131417025011886439703502295343037882213639103251549897900 -1009118888395535762047061721180456806194166614565541999396274651526674300640202434640874659775936931364083317600665242264448090018723165449003588386816 -2392526284157188505188392690013448960986950491375028571859495919060981215979514074503662055648827708202314679184721591212604079491336362235307206391833426 -4218798631160832202661168847672965043016773529025064083427701368151868437320716279368190139450621334670500868570495563594616568204374854957138784941027870 -483959849385694416988953239395437763233014877575377690428602005170645490229330810160206389562974522706669622381302406569004289640692782430642495811485696 -5378187878649244049653816422966231411527819255785030355895748698860605699359864142748498411935665392420582096163286983444186194198233107257177496094195107895074872712441771334893808547355079651652051885242856910759791626223884287793085982216148604993192657336251775590261767563683353789243163725004800 -32818464130850205246467682789169918339482637627874185171064672905340879331577799944668821853359894806536530578739275748702344346513314541480359830789085211784100573835252933274724858360731614753176374779056545876405547001284878778829435612577133895639650041742079621450894836820613872672377867785376695923853094317140137484881752383755066269670040095376107209776772131790659671001065420513961226520376690403010625950002651054751819424823637290081472387 -0 -0 -1 -532 -1054096356549212854433844559872 -295167673194252499689312720648542539285 -18569052254320792886536065199102429830591425911674802887337142847422563614720 -0 -97896720708781798801687131335879227111506109430789223376551280139659913587350 -1634009508410887242647804687779058371050929112656850452045994095398157878806417902701340267267910135426243622542827304902620893278343438781078703177728 -862159323782353319562779470318789629705961090965297342320245925680889441369443314188948203254697918300615698372425403937959274851949640461887703447193841 -7173051967940195554257280536479042043827601659905799457098258573897554700956880589901442356513658795342086705483095145011273200575189131337241532322688171 -14778026829432832620717758207920676238440861869481283376192906681351851826811501922740289231253685732115671799215873523794595875524613727991318894568538112 -9743687940329798123999941212290764982687335446528161500746427614131976390988057307286128146303396660476408358788140586545951034783015956845304363388199684637129690259093248015713809461202981476094156748203762337562483089253099990690430154929727318541968412839173701447769414174975320032013797193416704 -11665082978646551184396197271822834154968679459301162080769250076458812934480608734582177152703187939426687223961976274547227366150916137584109379949067630208858792329939845114289322648585580755616610302258512584989884644997352747262852786631806253344305668062922039579812843505258327167494097412319071183014601014647063034669866001123270058546336028496149434886023941746932012392894414262845484462335477059377413559348424920746843432610014073597213636 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -4 -771 -891784777631246170114448424959 -1327929446201306785262743695648620545 -57895602960811796650871631801676582813791887579127636891104569893774562426879 -115791647579680518644692972256970658065423327120836162316857392245886991466495 -0 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -0 -0 -1 -43 -597446603600272666897934188543 -335986975837048587653674597853034419125 -4960875119562861993119998040929006214931859756877807299872704131313015193599 -83869314384839239184918994159181415456293389193220202757469792852202756243455 -0 -446851339107039186419289111745794461503166996177271801631416983378126123568718875224988495884755883200400258616355472950696139342262717961597794385919 -2457416883750494183832570853589468397031071769924709837931117427688509339888648917495080941417107639148560803684251898538275596934697939251488325371868124 -4104295260045454815975181617904063417540583687524096503951973213007375671429493052370291608008405377354342405201451646421020861407202042797591936611216757 -5398900427668751217850873803494875937404578551752982184373461962988643267134263496913051856036153441820443871129747572123078186712219756428262779956232191 -1007218583726087024268446200406511915539475815058077584580036382202886749107233498655574150289262240696650047786995708501433958989054102047792490904760349592902261758650729430923763743863222004063022441814082480599888346140582423150899204609625387915667741403853025291774074147108688932055983390195711 -23380145599663018538794334849478795685367611191421527490464557496763254711125422173670092394177895709559935294769976819952372348766078401488089491379204849818544542158134496224979987816433965631446790943462305235568068590323956450149969681186862262029930095126068867520361231472357215498730835672433665458819876311608479695607535070166435960898021320714467950701155806239822554229659277947203115399804129178780683995750006285527204806890220566457893817 -0 -0 -16 -417 -51915870197464503363519381505 -252258564697022016064972069604476995730 -49219110417420231511113764378238860427237875311237523872333835875498405658625 -102059959947892163134217117691190804610012473033509494849327043219849901768705 -0 -294741958829571476136926871256484717091819171292816858794352010629523393545452840612410539842221513608737007525936314033110034418127477704314176143361 -3610022075817249902604614007613923579547402226882542852635039392814912818785402413253274269359316403441885507203823787712636067696281764519975647585808036 -3482683400560148150354019097545843271463498486574340530929160498845628623564983594731900657921818042087045491261078134766035389271551015123919424955561779 -2250865723942111784039421608251851432625547218473942867505887413831061852377508665133755951631672797483190995230665622102487996363019684093932899173138433 -10365393947735305013406914790529970011789937562186538656086799243490315444313309044458058836146192026472268081854266767322163579707977712683519957790124192466318755078159491340837738702367012687896445047003782661660364048868352982928925323993821529472941240126796375997530714377580986794001906926616577 -14333822648693704865076879128906075720499136539069158690685854059407099164825884209184916036182302301395671457986593271682838667112004613576105840990149035878508820984562415792148035482874225110645395684214966452961820472792442602064243527154948068695736851229344379321130087114111462740896607157184206550890192589389891097286967351231550279425607394341507476319668354155045448922078367672145026743791767604387213710928364307934410387263608077250700922 -0 -0 -4 -857 -1131685075580794109740493832193 -5591051640426086522729162377581522820 -49306867245429292665712135123169680084542437782148851479415184718925278478337 -25169675183049498057756995668265957449819867477380947926503567596715595792385 -0 -1542102799772537760963096697799266983934002048485565532539213661192226994429815251647197396526306187090160124800082409862575273365902118268755683311617 -2878686104109625229862354165402805665509620736230260577444969531934998053988812440085505116849926944621437581036715525742444317675163909046866434002617548 -1408524625048880495289336975458683532829516256219036238078109087870567897704916529748103852417398524574297919263661719753670147382638802928308999469587009 -4742693590966914277133482345622645931210627503968707620206733070482049794927860629080377711683265908227819055370520162972241526589942916768449868513411073 -5553130642134937791530098743023191297911732972994212582055485264717254209908250004950803382001574291176411334745038178232430168856024215688613524818989709760819986306959630308890022984882592785783047391490235914166320765169341522983194375620252621448545812049279764384555916944408353668680790336077825 -4723927638618295083690092755463316838498918330240796682728223697622545893312048720733281849001727094418300588431465926349278460120916795941753250274668142622894086165783902042659225253028435298371078758268910795425148305403592272806883261778790469890737429382002305486407398555872939312784693343967014010596526975636760014820772215746889139520167274303379900890377661917715485803657804374133841224806618394107058611468658108088260131560551896063029806 -0 -0 -1 -339 -813911142051622544908469927935 -4998080979218566999874015281917916555 -36694329931173559945696159169023191810080707655034316545018050435414281420799 -28634157081599137704071405870600522487246030369305940433755254306653996056575 -0 -1843618193774705784137956843790595835260885788482623306691425639970821561161960610834735278405087149627169615102857943696661773073162375892056240291839 -1170950137596410394079556452052007744667922551742356085549311726107104621063174324321159198410238181081409377899289339696505183364108241398143346683397274 -6266577599180715272643179829794667116364415231701874317566806045550798989424764451453171897199722686363150830763178386494247478884029147366386550475745174 -19499454418662965629802092201099350207580257918576731805737028058666848388525076659636862212534884659279639848323707620894715396779028548909594207393415167 -7491310775427379205451275241696534198775207876533479087995103252323137907742560676206102526955127596768143236592364563919391365130622631807244861360530642258721353802216367646769792844931749654022069462592705831533949527241530587573297737411586728906086303832296331156380881819739166686878092144672767 -18796502162760160226250152204618694839807906286113092800003110777883789218842243583036064249900793594494008242546403509188567430371285061255161460873411492857908336448904619340775306266718509033743784612836222680866203517611201799111354771581380221437368528919952566186512228630174278700984373922250123310919722657324347467475261738175114760435522588005390001769791388961028828314032397203388180386644894795950381375883062194583758183135747713996158476 -0 -0 -4 -729 -828471727968036044453779079169 -246763246873648168750114309847746005595 -14797796466515082470342263148884814370442076847061299435927018837432355258369 -53523753874056411494904927181451501266586517086289761739581598242912772030465 -0 -1610256322768663008681400364814073486033869865214932600830364116109232945229182624979010068028626424299039929917408136658184480247613110744087848288257 -4982989173773022649022397963184917180752190882657662748886552370901797910421968943370653256257967628629024664151919676967847090084950251857702290490980262 -12148110333218384648436139282566844904693423767379435822469549432550659540325752661673352721491851141598081552703324243494124595306618572675953315479573797 -11028869767950066853862903870251721369434866526464337153519928986032553183047292589177288689002878431824162808354999536746934113300550015294401283448897537 -5564677243506565432840796326485830517108613266274939211083439123715117854386689205611896780808790236784879267345611559615161743402643098893029929273758889773892627347825037932396714085133601074675261736569510119123888317868706628104410262093816075912392494216258946925258732634257705061191794611453953 -12832448199509312748037695466110344603691973686346158577275697531431038398255234604164046550781119126216534644040340178530673012471457078814611960997016443056894042854161746711315668268536386219382705407726308690881678952673969590544355499320160817266809347245168990155961021732027075310147074025336573553132150745598676058388830602694665869887746332618578258133919549090512076129869049818309689542631433818773293308811803158552451157660610127239822650 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -10 -348 -734763819450510610632565325824 -83076749736557242049732541826465792 -56086793224325083479877516880625996376700331315327416568653997679101469523968 -43066253535244233026187978142387001755402803069145978156050831917314998272 -201496559500976655734260440277137434576370058288390243702942488773257463333742 -0 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -0 -0 -13 -684 -887995200883511538239500451840 -185744035486811956755421374818265305947 -28022724762678085694347421423886262818477620187692930983847526359050708058112 -60066058720019429804858835809980860963604546002890479581904231770820593057792 -140726188198724864302983488206145216978022023086026367060204697866126599261742 -0 -1306435882293703054785864617754127645947981008635081515972982328252373992773175682602366698784049948578438144756464452476167658757197266011674411590215624 -3418924933199934873321329948743314146142040692630731923496865500679236033474734183436540120875605867521473258874285532970466381831296084581446997731696468 -3109273453047763295057753183837237869395959180214923717206774456268927599702732452171437824488600294111292564013762251960507352853270412970588497512497152 -286731836186446095857785724442739726555989697273304519023165182400415887707383423935925307951900182657313805119211164740403675553245412634677881978590896153075228565724249371402865963565486212287922871904886718220392004596796756145262329924195978312198219275306469037658944155791679821415216932978688 -23705763208580774588928458369265619438916460021945211232193047627926603512005878931377912938311419163243403002660952903410656586223538662133420783063393034282975381898012298576963467515440532649689328334345780681671723172171010972029952896075198416960048234210840439701334495379764884464832012212719290831505340271260696067947540265478850247171084561966330905870702345126530017405343636921534310353745046761481665427795564834777302251191884558790364186 -0 -0 -16 -284 -923020903585570222185243475968 -134595118892137229728955299113857603251 -12517788472856075617475369492923779675790060603142796585343152963486180442112 -21278317616479394781203625492118767183546382422255138071596449473207327522816 -37333224946615587454531558429668776609410791968643457941551824417764439077616 -0 -4365216629959533172982077795378474518196312747569830926192625581814797043834797277039496659650371970445843961712911189437934023902885370155197127447599587 -7786275362280256985096504919805326940788214494037837259488750879599254096754641518066648702992692840392471672645638933505566197966897842107066681649920912 -8618362445903795498495298150872761763162723533326999016941555427533106887247180867507912332927251479902784257469217137485980295377407380260430728522629120 -7982146058423899625168899480055973629935142133954608148082398488598972256455265091746814757927002125120326401286119392678755814780700585901254416951099507381124434116044162971886485060919796269087997434118497014919733636378546760466317072097028651097132741041399433702808164954962506455701888258015232 -21989330961427482320873414443063015928257919942443821711178892798696487896352353009088507622971045148342817299121972243674879166058756712149437499808420544292407293376143293804754263654378017796839550542953209140796361828649433680420247205870246799908001987021274080200394165262473622337823240257874872815088714497275294574219657771744609557582141606487864476140423350129460552762119376149626806864803509401361323064126501267414575167852478576633701103 -0 -0 -4 -192 -132781846299563114295034118144 -287795638097315923177133982791898021661 -41260134549646540530567879497450034797936197027784201139228793125077872803840 -26725487537631090553213382951117408090775065651640692466752663021330299355136 -17991000660342902270761827603753917202037974525700770708412111282505637002686 -0 -3944008675763809747015020665178678882677044257366818833894853057097872891661745300374363390097768393910140535804055885471681266094856681311889793405842943 -10432168002169925309120503644722046609224368185326214525537866102657473676610157919783033661419495704812662343601179229565468596161053474859566232729971168 -4434125550755468286273915949559378521845137875362780075693690876557539422008078027298618389135234122502995076657800647221775819541243724097410850717958144 -10544028428859860647631953921720386323000119376125885681800950423087768127297571058267839503395689317829039753434382369495774320720145371495400808557366564483951610047346873818659355869536197251947067485092374284236784198628790721882722429857933128554802811282519317048898934233902279839985805232701440 -18440345941730146769481994533733567141044993718475504938583318912857102648847047027910392382495051573769813528456979411954962447707741851707281536897396641672916772895898381890381674089051912642497004147872377088851984352979390381063683748636451983558056315085668918214320300476071304869797011014426609032047214117423653962127333957708782154126828418118548172708012943197384144043383315600410200355075913830695186216882152046505722142344944887793337854 -0 -0 -13 -848 -582024649450436441833425862656 -96569028480694229617495306964621095513 -51864362273957721702450812674007399695894707256635407766080228745350205145088 -42338700632985114555032701713036006818320082283349136714641228033327395504128 -215954730055182411688222939177706107427072066045627310771072503279957579162468 -0 -564120810776501871547030068641973310984074415468950923614458445915309234305596816720344025395793468749961903973331424982658487492675732875715746581881639 -9512030017005233270085163286414408214444876161604376761868292912053847917535106665754020297449298958739630340441868032179720694634048588859831514895174004 -3424474661482523359099400350868558228999347406743051468541630110333927244078257761445483195343636488588952518919760460591527948463291377508337491634028544 -1908744027142267160255992222033230928186240062062564360139414061079951535468660083714712566829573866331135382316682210055579103865954047482673249785878901884126518352007886346248743951305036458287883359238321077240214116300549133893047140060961188058073853208070901174162050948796606119057608052572160 -533753182221943739411515817506559992144353474201345586358967700583566469936103300520749166184587887860573873190047587658861707366381179609208438444812890854907402119224883354291361848266886930865694292240182736475266290660159879661714355945901578730736360959787897025448543671196789177297617755152629881870393271660514425363164245734715462258579427757199028980848704112940977349320451917649697374845997311260177033885249436939940782382383929118269489 -0 -0 -4 -524 -580845881975944426787514089472 -247672215531302623220114886854143351291 -37388457217608581848439794961125547405280755409544281555421884293853562798080 -6867524968084435742227914739535272191184841314026237436198416499210501226496 -37084585770552631634036071894836390875870489411136211932476363892010270509161 -0 -2060718003102261095469378358151405280518660285538331953442371394551388847723544748074563153067316177877347237081738459763835643097859427740490945401065896 -2427597878315108052588416153186371822454229878691660809974423528713640874637568365876246285814256304894224389434236414966957500210729605332124313726402355 -17899117585006203601483637166090154016991728785619486538375724587636009980913873117460186227173247676970981855052572465455831400785016798907992702502043648 -1871601166577895913665863813882743743306248246207644360499054669218177312807383799762174801630805340092267502315367248573627278277545825513989809282952455822056852517152157679111824332559476177533337546335447972701488371422446112963735366800940695179512495443184058856851596498196420939445759478071296 -10550306778399659975487897356875358924973318622498728253620155978576923389364488120390171602550388451242602890206431322088607185746985283921168954364764596976217073705682936664824865112089874261838527911965608302408800742105060068062502398384764473486663935558909957592004243003066730197686300227260257298269425658277997389598475973630768006422518890534069851801433726348188900604318019697404931564408067035791107685741964005839508900413176154475821036 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -1 -455 -12835319081690733822122917887 -127603331931914074262646774143133941767 -57894298449951398757252466847559442906950400042978305056517893187912714944511 -82474589191995733226059000413673369947276401483368126437014470136553367666687 -1725367873614246661397681099761093360336884063153660970896243052915304594387 -762145642166990121634319750876330910924084988636669303246807590962648009181396757661856053558136824594202566053704052044355748404436481867775 -0 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -0 -0 -1 -455 -346299262836863942390852878335 -124566106711135523024142034815609358702 -44045214583669272699005754870852537422943743579723467162109618579770773078015 -47191182087471614723727659086024420134442459808826251007744893899083683463167 -149345394318679384540349467866290540580664685942593858325103856164763257832712 -328659738499559851395369286572775271201389799649696769367887874387602713649950541005767198083760164244451135081042951771979928777420726556841593536511 -0 -6499574949616620637580116906143754514426259529884439117780273025054849187451370731789917263550769050214853609122367946205434263410894715910936878759479861 -10124369117566771149603816821574722189892029783289052707167197978669109873476852672835440349606345261291423564961708638702389275798144171277206026719330303 -5383941699543092305623443842804493263947292181555684893992973883142751498900796716464867906615276957493629319704781718428057228609756150862864742004037399095952297373496665788422781648005837085478113966408640459815231657886564133913536806437981143729147690610590905694337501640066370356251688307785727 -6310918068059471764026319883889981132676900864298663494645342741409042787564204104397616974568887642296873516370780274089671686771704841938229887196137460632598595783254274133337225772320128927921843666172747542030026661734330465262950674563868305274037519191385934219987327624738988091242143029337066541173427000341834616390107777280313836624864077607937735030229348205663463578720868333899592726387334113240438244508441849656170478117348105420506878 -0 -0 -1 -1 -1081180894199368861240340250625 -276904095316794738679647588766540076161 -18514213011776190595773191080849496970028811091221965026218765069756819046401 -95073983698370028065196578355547349954385319767423967546472360718799690792961 -6693017956846532764960681557697229863590470801886832055562479247632768202596 -2629164625513944344764393375912545052754690348620826079674933659671918850737622014550902801880361819290452858809500518180554683024947040651570430607361 -0 -4137577240563885180314014646510639308253117303602681600936018963727274657401877961996913350454564200277844605264306113013779943962820037004955398417119598 -6012634743225204725211101132320048969685865768619552174466530181728480133292301029875548285114373185900664580122272341980596861811413330588763908353294337 -254535129586580270548521975414349752244526806343151862080007760571223205791187472902044998439332623334230660834529731711989369170257700618020245017747639011641677093780420045542350067272071826391827216607645254644193288995966198490603503031365625040118877912466523642200661116519635298806043651342337 -8385908846676081908370043980559351677127770716607791744592576475690297800091571658800512758412511961004663163941376701719082333294800426334317916965716031020176657167014768130949406498916053374967901129501825532754676652724685176412371614884225185706503174134155152063944407927152995929183429819648489312242513817944158673167844009257444561330167132349514611454316120198401977968656265677200605323359155355731762973826116236680898629609918324923846369 -0 -0 -1 -1 -324956523760712896376838553601 -47627363697928134504927977366887218436 -33434307187530355896632894137563272179790850372650630159758741607677205413889 -104315921941200306811166882397908923183277873670404242656007610443530523115521 -205533708257112897495155981027938658151590718760836138711462112141640293797081 -1107909686739446306967869518645944435031236156727422752526394105581762893921951275569530192146029955926564787705997591876292833451590952315825059528705 -0 -308755345928398507077827126197262399264414124207949539753718673573534243084848112587571000480595914906260398850382596175138781946603416961836963870626141 -18244350893087399850665188670118991037975220133032205465990746245721929325028153136705422106519390226837480732385191993725441112016188775082810504002928641 -3665588193601842217160462892006519794441699353581412583510916962644156398320556835171594452576616053963810443333586339255795973680000246325868129939290031865642638045425971867009672427500503914468057445618838753197439907968265469989645596428647594500319536081932047598179754671610950943455199214174209 -22186396831654097227986496390728371980330693530332007779166541519242688898973938381447063194690736625552685027691280623961530978041318772892810396870809949339891780432739513792778708087762348742046586160063394433346940941101035727747372800903757578028991776450199349428475680885800121505072160076081825165777670772329510069207190667831629973354820426821121553497181841257104073871622072935426694421062659417600761519902665044177543487193516620418169098 -0 -0 -1 -455 -1168225574662879022832991338495 -57044890746169261651982104206166026733 -24779217388362851886903530808112954986166879528618678581940026232185554993151 -55591006844159235471682881367005599231961414654778243528698592315005808410623 -196546087471840559446390920880856282064404926864490466841380522742929941658568 -1917639872072259012937469746034134309612115999398820982521714520910586889626920621215182691543587130631234794290116996439471618261125269461687047553023 -0 -560629320288680383605194363023384807151678143098424596613421718363041709472766750167486947243276747104145109416759625120625677741081131727837587049341019 -21759673470171209133072662941401074809641596506044027427359195245599749269615278218888544963788731822407161900052925229605030503595641862078189438914002943 -8222674955398877316557535473602868565827602242812253563419515952040766275770537334852962079852348771177051126858608391846792829268299315517383515972974712379197466243571886838842455021350337318953242740755305732748699335134286099636570483789761728214215928903659103496254292223405306563391980052676607 -6704878406080177707560203655054736070477248576489688306407842672251903096919006312415980371154994403260429964471609192482935456676801001230508899453034708688807610209409717279310365716437632450197121589568242633422750297260272378736694314947182768068352565076127017290514776717356480481674630872849763970700450069226388331363736946106411055783778057065098928098706626638969150760900186745176895660013716070551573583271188993227435662677589978033750659 -0 -0 -1 -1 -87563840629716667648756940801 -32581458921182292884541920892394116196 -36836091762090661553341041736371299478161889753078991612037085885874369986561 -103386798190572160839245264014111757730316335866975469301775408799589041438721 -230979096632485379690874166782108500616204584679462971821585472432375012098081 -1175849592681947186381704220573745682736611171788630769140595898826048744669637879095677970676093415500222347357756142334095120061003014283574860840961 -0 -11889537611764019713474395414522017738805331131412830425961734413527746223621889642851856767739924676858324754442498238690013743634420772752231361918208575 -10100837533498068062116682421711269237464416549039194254318934545331492474511664814492105364025928254622293940722169331634657785497327849544082846163927041 -1369148440215267019286304149733761118056665924873961991411630969747965209899998790716499635578218937487656801015714762781120580385002698505340071100124982713842184248347860416733386896580942622009293934979960183456583184237569483817916647944103549712738217905254990813029611090034738596009778303991809 -14470891946382593133261871975556948917962250576470186541215002308627316792841475378647804951570288535703522370652296694295712464047904010907340184090003647914959531499893292863053894273874528256748420135416037596722727653113848255103464219786106388140414129037919023044763659100696777268559628822671794913188044005920185870352459166957980274030642096976079120265889000691672667107327976040012293388052690990946304488345186187456257606086924331607962389 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -20 -595 -182776107722364003135434784767 -281466386776064 -57894277771803943810359491595023458960167522338927730828258057680034478424063 -89555305827206481420887328961785203324625996138257453019240897699860153630719 -31072229483910585358509303280765563029411398133731077240605874038996845471709 -3273390607896141870013189358366943131609359382662427080975450085374873026606564249289725539410814263596351691407518053929870103702401568479817676881919 -3351951982484124983609172268969732236347481510662869536744042230485685559782341996467619926017805728755414692283401971053259449792684445385138897718083585 -0 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -0 -0 -20 -99 -959889322504300279480128110591 -226336563431145028687618637822844548759 -8191728281812111000214169178820381725838839982899041198234467802159092072447 -89010858809825214210137289255494412179471657721652615002925377757309556490239 -28296599574001589588954577695238704244097106391390255507549917926893049677184 -1143685232425095012373289446686042380253702819534317091382577237068099768813347351859388607019602275279637030972001139645555685678576527645905260642303 -2104386984118747727716517063913738816025890811467756992449715776590348174368338808503581044664946651173257522380822233775261192222343142632013731451133938 -0 -3245766926972643552330628549049705392854964955772154252338528686239084242071182385973867724746592501989847967320150395945141828002524173519974765401473023 -6907648306248975720047253807553315878303563975921476580151692887533957733952838913222178155862261045104101262891677093360305327510435202370854866877221225386828546336285443261625103246327521271905514699410432996107397381514692809796698965099058352936055010684764360915671953963860003695683345510825983 -26447750664679572972311511491346762878392092007822795854735091849389623944255827031514453721868656369736370805514656452589804235525013818434397127292257036933491312808959509859350482891134824058764033024690355796359079598096947363393151627873770672958867270537674600576785634138230894329775351387231396268146781903236645159728891454367132969379219264931998221585031609847512363429272555066238888068880222596086485264677276997340726172359858551867347999 -0 -0 -1 -853 -801006859139436247401554247681 -142225711339612348039388698959539597716 -35410378673742450051313678883687618704562356389379983514233123876029687922689 -38091574202374902858714012832961735959144676797295175057041826999843956981761 -35509638606720435404002212308149594900713246317816589861892489724156349420746 -947006889763449443971526094184752460089916877997363377410340189611125125701316093754306776646584059092783200306323088027637258553889934093760610697217 -4220939006243733889354999574941943865608293932174863319944209124029612039884827143990223224550002012724043690083235647740267932838159837905350721905177008 -0 -9819440398814080363130785191409752685155104538075120057014007750225191492119731772827023591993721591147390320845851382883568011993500439254883415399333889 -9655632408759825233958611797456496067063588648590435065602226373676034823927066598142456746083088436655720971023229982832504728539123316354948596655920694029523232211427760301619988760938379731840724545418059474244039925314243108598463317618776112331037188490273232491842485947576029350960119525933057 -19168469482771522387361980195401090898697209536327388812153014901529211406007624393592650590928364242770484062532946351666565045957764391288622530585296240046291185627706970869937489824896647625260613504074528292679224168776865581606886288126591395731294608037126460284763239262254718080804765957557128258706900873267005249160304719047478911756155253168469506131790620297150118426293519712473869983955423743730285845904324583142963587786011293140146359 -0 -0 -1 -673 -715561516884788010108823011329 -286119718739330631910267662147206086291 -4077367256260361378876724861054846658815066802262232907668922921382880215041 -100388196731105783095258894359755309057443209346921752384716203296701221437441 -161978619973690354040532742933073207830461397640286489633556474311275198068706 -295808498796001041580957434812431144336907268953236122391222657342664931238894722072411794573977944314328249736260072780418263762201867190982817087489 -97407145078903878701182151565338220720952838018650765330450334535905937357373611965592469739325221569980257894281512858606923275277875824669884901395310 -0 -25619912338425179981237074026942021799265536991310528535894660424287880718210785582129672576271686161615856398250702958965821900733894974363009685894725633 -7671522909468965446688811864997649775732139334764200965671339202409525004787427756456574114546598384573443159084990772200209956297641268137668960261240194749599773379609102037525886640588454985746187171015912121459949370747421414640087855414458925160365198194096646399383154836512890554710890718953473 -10293884923205428378666956554848182142244715690619212169171170546179865224723565444900771453248701102868483061985574173559709761183179067864659447677846955231286308377671525004854738789280605580119191885659203370511449399894078589757295087079227410959307116809855467581150399434097595488801159507371338585214103760300181569615667928415747705672332480417684601788700985353510796076482470500651763753290405004222433976439506565358025254851862958596926224 -0 -0 -20 -319 -798574734973800499883498536959 -119319092661737199044961145581817753859 -41294191017998785012725086702072135678398504724859270409452353997253994610687 -110624822424825875387330825570332783320826449688572761060969648171618999992319 -60004897054289201390963390115257285329375358987352500183853788324442296431274 -2150478234121502140094205664847123566690038384648319032584399902322112031029827710253123600637285764878597164636106830261747369442317038978555055177727 -2286371016578508441121921294709678407693328940913329697268533575124016682365838296403222603898047913087019704067878310158113313849417316810388464204629572 -0 -25671226303654754146969894507457657987204391426702541645624778236755811429128127378887919394161592482970532540809070418766878887011243610093297304929828863 -4785525495332916713108346669310355883208597124913721653495570548939574646651963675833362058107793696545444101958156033302876198568608530737409310207901760770957083322440752303097212064072393248697788251227338207068162143919875155956848187111766304706403350196933167632508223394324726953817363397476351 -32094431582633234807734495384819130272063712020829901878525215417797644447839755174060744776726644205182311880852974878748451402374419467270518797046912443530456212016605470991508907045276371141670695248538943422952924891445929100158107096854447459717681547826391098044944416207597160685051923871604127853310380648807504003049978615375745963324507225789653536595005326797804437482047876186051782235982636836244442075633553513047827811973923415770476767 -0 -0 -1 -337 -1188247491006021359695801876481 -11358522768134003317334293217035462861 -6973218261549576848638199934276587054115075947565903430900153448684127780865 -45591291715308727736165915509600830342618838112033396612024233841157189140481 -40977506359948200822020517638847374989428006593590887799032148830447784766831 -3158526125081113331509898514788313887509900397230108802808242305275074329114995683632326747175671847567894522724549111521860848666266947552987433664513 -4584447565436503172316825244033692918247702864479587471557460110462846155092582971475613387484731203139053010262859517492416070493565968167184179196803079 -0 -17855886664960195299137683256225599460539695469189441741138167148311674787837063586188982923974069115198316431121434733749422291458227574255634138536083457 -1374356246318253574025982110729726842105081842171225422931088363015788466146368528015009583509706677602458662747215994780981399824925913453782201779377138318045172999325810798864144189015648497899487658934661306497156780183553405477807924063800170898266248650878103340616913829333509130538752749338625 -1640563579891112614924709605958688587289742793630758509110339884654417246046905788570037457484399424416760895124676877531011891636277126802773558750473620855831931500866708684883937438953118514443126192930877064490752020898747438980089537975395182532494013272495476219349907646220024050095700438285617434068609057901263757996869152424637406600611987744170919997651080917069478242122542753946615128758176926986841759042912203967255896726167421198572976 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -3 -780 -899245128272588436190215012352 -79382824738944801807794176002 -57892510978869681736730162220291002611042557329170362915611609916721313349632 -63318549916452990938470040291368379201286383559458191806845672058759835287552 -174649700752953765434988902296252491183916410461948522823307150782111144039507 -3273390607896141870013189019906287111002076719299707330406074198757955051929170942113619750485120149822099821188584330248544763114365430245700361256960 -1675975991239013909235918173490637855139324507692685389812651662983087942796182042459207730713681429512584287523480954202975430917613169254383951955361797 -17917957937422433684459538244547554224973162370938960458267029029640947834979519339152474433150005542914 -0 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -0 -0 -6 -704 -1177162594896717752709689638912 -96077425054455585888525548032358255307 -32860001485564213363245573215878063920603649509172460851204817722599339458560 -60109254545096573330255392159285415923549233358667844961269905663499962417152 -3073211245680220492517146661351308979695018043393915095083610251485928253557 -1196437601768571072681596644798276004178149983088278508148600069149523733201731793704103372368213233390444497494452110276684749064047644093326087421952 -2038636880556966905914764363156480651959910168504797327228316486248128297682164213768446666903998086070536989636186162862522556977670168122610463846604180 -10761508670374291972420269797715021028414464448240558938464987728552613552119689435329519427607648808039987095579973579961133046464353196386286909020663497 -0 -40984697395123061397847385885955617731982529976694509690496120168455541366383914951564162858485528121548558319374726064139914899078481865279546572464299479360080901581918627814605543248487499283343493082927078480873799428963538897690536507270633025904660571444960128829640263091486837879144125038592 -8909946412269299572792850981460782799532037067275283441188867265579333027637974855653738811158117573684759311560589042257190296847643010765802475698723405320867966475350089536733110413273085947267298794710610764685212306019986582674496969299883756072077594732387268624852957690791194886450004295782387045096350267854332914068470074912878717285439467272539570799182006042059497909029848272463040589609458879066125823035788194404021371601097559221551987 -0 -0 -9 -692 -76435178552732951009197817856 -231982479817395379926203139743231669376 -47577898445412852300055548317787946557302802230354890877227185969553614045184 -94970241355010768267008081913617218797759253919929280496382103858739366854656 -14056327310363451370485055691408505290446120500764073466549198485574965172326 -2766964662182057386500329725111256292868978381151632013212396637813257618816895179949288796855976654014890661438562190344471725761340854297277946134528 -1329225061763580875918533781650822376275620990297295670554223160059148641553029627129765204098380667668821227128123235735809087554397139852326517131173276 -11097141842847614394079602992178709247589894415852402133624794662338776078636709858667481909108996684238974927213647046033793578164937980468332310219323018 -0 -8071735877529897797164814472711670751575699750650911010717417970127274646674179517149969617423521215633934503699616678817716843715742637948946050252837432727686220974468561142882832356080149399758341834298569043889563654149450777405007560189562518343814488242871896319575202397519320214473602582446080 -6694616185800266287626591614740434614172450545677003882600376830829359762042179214842368391192674719407443643400544441630973206517018113283369936295245057174712933556994499956071283288989464799795553730151588182776157104143243273082778860642399792205046014812501849329466277700807982917596823152488508302015315461955865789728769054751228692423119905880893937895064193158759226982592756051890511086607781198053342618530824939847343739509034244875431247 -0 -0 -18 -856 -672949534961130560651949768704 -232952537159279159427935901397589147661 -53562723566230157237791876909273765997707933576810908258367261806129122902016 -31550994824562540598335530352687292695019428977242991470692369974431355764736 -152032362650815553704368562568087238407459967027457421226225849525712097091101 -45958219062793756897818223297329408371898301258718170247988696901754793869347716750803095228248960915336985565984017868792052415880945925312017858560 -2138965005429346794405216904139045984058362385566693925431000008613749092589355018622253069902180893858672397449645385554064735573191661559383999731945459 -5599082768478558353474373621584847716101862099962719911376003810521845817678124356992801556560959828813284665370289254359505555309567567683413487963442538 -0 -8505562198751426520419753868937928657815376861050869644650730877638862873089958249260761602153572619531088220914388128107806730704564885365227693238095778773347289827488140170092404396918489146978219366997692666431664422833499810391011971777952027507988195640695475217721220156144534273026766902657024 -13258845752058319557761867621646364193350685009107763072613748715545508884518792435165640060432501692620709571169303323832489898753595064750109814688579187658973115795780901167999045757923357842908980476028181042805278133908688854546483149229843751393689435259677649157449342132761603805685178078740303455039238350459994854784015714248007648831119423734010949858462504093063563879993972734732657191983776886556653813278840564588343475007599515963220807 -0 -0 -6 -528 -754710143931888045283579265024 -287963055808859601191421471911256637338 -31470892304589511166080140750820995558894235651868644947081847455352142954496 -20368885260017261654021615802767195047009744205269123887896635371822470135808 -142720835682631691219581295413272156653298972135487346569310533961626415868843 -634034310188921143352280112839185376851989590898296042925196037660929073594178058676330909116314511976492745895031863182439397264248057241967936929792 -3536260655104985228570598047629889621504635464480007413999695872026244668858612074261286775235489798011926649959578184502755418831731290347207977473778761 -2287281553939530956352194620419013279616295380484047525310562106105104280173822770723063903213369365468416110828105561770407010047970222328014275531881062 -0 -8975406365256287079502146141544330687925839285578604542063444104478512627003244297195048898182515489030820157009675126108192822666606148894492389023997343919294064572180355360315826219274413911422788253153439868196680254069763329891225781141566431534338291820281857844001159772664584722309392818503680 -7627895627796776336034103253504663513973127224674837000702424659700877395432210192501491531145025624704509245843631295415207650789564811514832835249177414395254574557066114110734069582082204595410051869299928733226428505155385671730599432801289079265733997957718465603964310879479766099831516659589939704922842510148318960095699074620514977940993795465684303075866898594805143270298319852548800656967084658804532267409910838781960922542128682794059262 -0 -0 -18 -884 -269006938810274604690777833472 -101610588522779797032951766648409694306 -40658360805639445426216385690775372728200090304603522756100519340351932071936 -91802138973420634587973914099224098988335694649088995732701581040017689542656 -160395547580151362819010763928022387697275046750539372389069219756723503990401 -2233124152381198796145991717823834504772859747063952845191626176084833539989086207836671472551515196170555448158422751623837827519669626403644933931008 -1919252284176976160011289332010981490746074225906227007498726993112622893994450491566696784468278809972885041734059388168593055735985280406694325759910450 -2289668776080774022092664264319203559647600991451909561040327250000009538496148645073308529369889500343496260137733562205719817974360067449226154885838315 -0 -8759003555859924455289792006122150340086642892965675685066523367563039345030166298894450331668432839835781802150906003659255941165890838655350310430355795467012027286822624520715979773959322702857888683940335230551215550420172560712753155144372559458294216901317555971294843609695973013254603739758592 -10233986988627486160165893250925551838419499341074966949334890607408070394472982377098453235058445034249828511040640776640636214044010669184255119316736950277091273009417324454157384181382413000016000601683912644163946480527308355567900081185611244964219361614299468279593357197464317206904586553970296516293220670021691347612227687198102745489374326883191094857906024566921950771760071555266307078912566589261856641951389747392777294648176011723094938 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -11 -372 -21559839277448701880456708096 -5316932265549267143283400319839567616 -16282337714987120045892153486177165723161016453744476162789802658371874062336 -88537159989150876890408569427587371627685568624275408353614000822051296772096 -117153649598454680903779921606971322936867216714956963589387307178373077018966 -2451846285358576582757929577719947826359368522202829009982370031469538161149259226929244677345525601309596834316982200231651181236759995889790497062912 -3757219424385931343692363754866841943466352992489416825487678472853020775555494604110835156534238535884545156565469882173219151445924025758587329876446051 -1637494313049913248505508525988843145443777840250318553676797222770022673451141317105487157651955800518089884835975451904017985829916792949984404185120 -10058766516776922387083424546997939506014390161211981301999104219673036875504700741486695698194962976600388399803269774949866748354955056527082238634885120 -0 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -0 -0 -8 -788 -946214455905379764424855781376 -249640706902979523796329170594729366801 -16258878864784814636110793294682776772576083739386883456710601266131116228608 -94078769493157252401658832572786963194593512892920809466055866983486739972096 -195490272915595318817582071325338439133008656712282569735706902622916446459266 -774359154233456237796402928872914317257521270747766957039349342659625972382672493574510375099070233552293990548180408226281598743426255631871050252288 -3324275818746059767372921081282057650219072022078335429667273973120625111543946446086475227802464005339069654858828935205186959839930518363496383948716964 -9690305591136628284730559742274731850602666195501871310082095979151178872403510613019650261986889225542227473459970356781573066183889915340294060566934540 -18854688550460679120040977419238299860098014817785306576788948091134630080082228033258246996685817245124205310980844728342008317740737273944965604408557568 -0 -21535908620540850229409686341283386471970416340429967737238370022240247758101364064839301635897318433287340370763594753912136240228661108354045783804012930997208048017799043921041828011421457343668782820520536104076035026053782745857307697762578688266768524636747474268145327146906132830376635108519593754580661809442020459403107922707419305832717345589771811762862349427813533987428011905051344117484841279401140909699065164456004599224024146858711124 -0 -0 -16 -132 -980393085630777633795127377920 -43821595176334581224008514546980603456 -52058795012750249675444812857939886156709846064922449115753994933360369074176 -14561123691201700806224839696324937581522964314772521776090379875080644591616 -175349175863053101686937135303271674300289639455959650994302374762982917881921 -1131906497036762609639522228534153459919865408591834031873180959858521631664870050151451799898073343006753689509175537114042334594659610848703981551616 -1350536895369391632569184447114500324766670158350291610958967597949326028999359576936432279782089126385784555900383659506910365560510605065726285256239859 -9451728608924473583394143032579162808176109643324961089109032935808751128466005303662886867241622116000970585797391055402890609702666479416682290459195799 -9869891012844468185531924549366273911251756943322915384954636117410087675117010026464336911857990363996496352787552233421136612436976661888444063719358464 -0 -29160913186613439708872461352513122879764789952607298290885068203114782142216922372207081593038688735241042977627651978016800109665004648282579021506383793652512992091245080903136500261598244223396601485010319512598372184283395628393048826477026318554363070339145452721174808395510502917485184825084502201409114714736758473035605478534635042521922391709556430295750595675343883087393107990838077832220859144502660929828804609103025888309324882798051740 -0 -0 -4 -484 -1089134261820156880321423343616 -145538748117878092352760460206159211126 -33511658511802278583722924194874647566922276789108093402830459143321542459392 -101058486350009815877613019026909063961257758922502987153303461721695947063296 -55893599922312747075336422508333078260508029486824190273117160196130533025956 -2458978570716352272715788097961829640376943254320968964331207836957962374258773872920562816268264474274565514907090130109741585328325854023827972947968 -2334497061062585013441347827924568919650652349786907365392347487262095082662967762597898425035389335443705851967166882197635071826354135997079594483994794 -4739636748710839604647340865523976068039985893121701111166740470358505579292913487988032483975449672818248784390230013345327930989784690681998862554218456 -9268358303535012104892996424126494308898855298485453509135477774190853103484671018397846744502851397110775756567946279667738536399988345534648240817831936 -0 -1823333014513972248453863033569237311230812126130506337878885326530740596368720446811271380284232373400475839583564476423453964756030828084801793759122506966503712459173121792772565318978814203256490836178101826827909880676810922045422401824859785223968311017274407457720180425066972434143458217681896098086428204645340725330308523442939629221459319570158112385542686278442818549344024940355939362383436777208099871277202253431959816109988249138938651 -0 -0 -8 -268 -345705300287849490539146641408 -28619945476059463819047280294135199591 -50097187803714017428498902612256509194829161297911658170445563991107297083392 -85421690222206289410508936520273729553730897899163659208114955446435945906176 -23541147492225259737409530414900909030720627244427625246343510574225863591276 -811358695156145842831903450818927915525548089569942629196406351050494125902340038663677284364719310507462367292228961985914579995483031930887980711936 -2753993259413215653375383555168520262036721258923608032707398699340884867309124328134824761429544678847308130542242013461361725575679679273117130844404184 -6291784738152052628532475385001151529792773731941954632670769788526762258081592087250332058353673371670605911117825763975584603160350868362121314372190208 -8246304950196854775397532690840382052681504659869650088188493089155770401666344769741621508722408724512112431250395089366004560986597746949833784645648384 -0 -33536828490723711025794383275603096340930562958525566355063509107668073876964224976171882248532778409771216997252213433993697133630589947644361952834437975121876157813462137576683247086121046784363938117600983461647174331186404053947864440728674994199031341263900784628270176815603173354730641751334159968196129982738215961466145470666104403167324840397876006086216517663581877927694551780431366423367588590449610474198496508322115190229617947779491804 -0 -0 -4 -744 -285597678780049650206757617664 -183060435302525962072268027628016111021 -7217761771929207302500684142267832220669089126781480397245769148255541657600 -79477460217247376097886641006546273302212937742057604494280155910320628432896 -158450537078399418888641617303234395161356907531049363529305739478391665140056 -560479410674256955928059381764241238133905670084218664847601259116454401923250286188028355182665809615792144602857372046658783886046707955042334277632 -3683866358503387723371342100656401419267927238139401708364406415400694153274197402996715954111156399475059634029565264927949827386724995677572872987734714 -12590844567997964881164942256818297297627752677819872355996517914899426206584352337319457485189429758905990389059981412286350729613199131538807367459067873 -14260854143752552717856629622964902246009611015390566846603377483401216738344769373127606724470825630558473764635392820940186579136492501381274523880390656 -0 -21800320263537007047472351208796015562335481192952840431265409041550951526374116630066499902549548119697525627775845229207308993284066362779188144732297218576386880741913758964084484707758910160792538754632482272801417299710417022721348611414238310143561872419421028837234898971823910500714741024986033307017463050796451118315775556231911389073082154651438223368297410117325974472883309213628390772734914681877034242596184674973753688803388293273496715 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -0 -303 -1157847834746177602004396802047 -339617671798359351079692875821060456446 -57867775067306645753843239588701162944294645608219998114893217643713234206719 -26369455828243150957066923597651481411694301395220676885210503666274163228671 -11817720390503827648781894443752171196964219125141796458180108788425643579396 -7804371375807015860240250407429415282553584425143433436919818048882206801446222314107580658597196283229772192970700820967091727381696957189718015 -941081632068259047813718693871170463934954812652267338340003979702879896717377687462914527089361639645292415086148993612559161291073345891219533759743345 -13407795142668967188561258511782701242040321017549139392778624385515507058390936234830335193232662892196414947249258255251754028773259240421675666352640010 -18030219496951436955703469321804144806589781292361309272091751131804808417784938468039344100394760314581363716192040882359255669821551713959394630176866303 -20764409093138106714694669222174000783080657450980639056024872831998438690744740872244449057340558976478438798965361453061982147397763104606367603575189606869434278459569124428885756214636676791959099839519692515219337740131125546138808227180751694999895383762073154332479036368513723304712662417407 -0 -0 -0 -0 -131 -1049832718450901370153113485311 -123743968547971240881793449350717343096 -54247495121211160361188037929436149205783261053517263337868798039971357261823 -43780720696046181429968922798344646336098289195769104725971763934578188746751 -8569638876184228017621190554815028317573269823425357131497263831998166914171 -2590756693431741009163041429286398894656679441703939344261083891915266425352426310916864582943630782192228522635328133722758329149109900984640654540799 -1922792972978790663157142765037754247364399993465703829196794167418278912188407110331170404215500826901691954309618913442459626285713809675553469719932485 -2247688179853902828346517371756160939505222414700682406758025105256274372769418491926470116598950917484231339173818993910715883012571037685660436398094565 -14071886071206496987190907766460338856608548573141424962453698818757002916637223518158300237915282267942872621643500179521389478132153075114062917306155007 -6980263492707976602733081771294983285805560539203752519918453497642718603261557999924227728869489680883742630490486041372040492077056076650919876897423133050548479425912646008193088180932733170859088326636344779595580028567634175375390724080269085084386954408009560789015111093719791523327272483291135 -0 -0 -0 -0 -213 -624277807757669988326564691969 -308806362863810512481109604679086238721 -7575495667463650924420531623779877444872078518414758729895107613392445112321 -71383655898197942633634404834290712896942744228408684751177316467115735121921 -47122958114890946067870921450228790803155389424276201823055498825762056188186 -1994424534807216528160042697004302546045534768650260352155667756798588514332350607215428085715884919952309860849179354235231346459029751145877699821569 -3146301561202581022604671318145679315132240673197187143392208577019128708207517211248787901932425426040980775091796171281106110880987357700982231152858994 -13344055815468537346229177929721616555294581393221545616480099523908865321613366395170905103332895603014771880154289842243792973144146321750132365948266313 -25653689183378345635977168873503678049300684399452321461457658485062084019322850001321503409853791883406838755199097647132019767454780407950907465283928065 -229130338950058715207075928314567718431940966481184952335048896861640341691504660128894140126073639123936931770837384336344695028165941321264091705246687393301105660309858229907321110227251032801854835879822879826073555284111211015122519313114936258162164556856278990251776549227306961333644609716225 -0 -0 -0 -0 -141 -119107286127314199610661011457 -208956661746708815346102549108455455236 -9304183766217687257009318351289386378056338413009755635813258086246793609217 -37346116786925829034687685788004865887841441332555982208115369087891606077441 -142686669406791276574941284020696419378508583519717415488223479096553530067216 -1022103513376169960952043514852766564406369544025973264953283529967298973963692347373601150129718093616182401830320452020826518193269025639232195002369 -1255461492445777082599853012891109353852057873636592494368775587155748628015369099916821369435584805024717479183629939627196544920025710945751532635492024 -1189975803267431345957974690102770165991401096160981798863264688637086156816693253154715236752646520545321786646254224097289874096394507609482395432557936 -2038989167013368656643873390716729359930711113157962588962483025137830789336536213929010174321600155153011333126258568329562186707136497102974740273823745 -9084986068063143759355885696834360372551159629320665871137722600252611748073183150082604901064288041886108395689572588307893872058553059179847191635406482453723540265118257106957652545879445444515005992515527078762521603272600432053705760074646655367876806955197570127991703691485644599174058580377601 -0 -0 -0 -0 -243 -699092340274526400217322881023 -38592925135126088279952503537704324536 -24069098860725904074101863281316659899407559169599337507189559069499782594559 -31845123066004062045091085308083188950704500797090491164647969353944050696191 -118305855347934305358536379357165472350660178373988900770159391774566491848801 -2008713282085666284652556551621962437401356361830493232520690122524894627206383197505895885671920146557265344301589773021846167435240002500658026512383 -323133594322353610393135271506551759249338578228327732110434346721529507354177413565941533352573023280770193553578703771044066939448141422832699655754215 -8866309339762256938205367118241359468285084241706741644373897192876383324845891938408677922953893394868024823096760328985859074426231649039808204582908518 -10592335116562605574859104705966049302824382333355134511227046941609708791279611163990078774097256277263081299114817404233848814642481057406588005371609087 -7409638199867804511590556491020804147437821443902201231464956650918710757587485835871076220513184441280932875327658339055507870288640330135470374231086564968802510793220658631244921907134557333508582119960035628956742572301380943132749810700571732870307187030378399921152414041403031737491047579647999 -0 -0 -0 -0 -361 -1098223571363824020337471782913 -73118673265328369333869451006373159426 -12612570963041677221068251937087603519725683816332068400361871614991377891329 -44757643964492500248872741260097920363273353321461345189639693578247602176001 -3942245179751587580113754740041160627070588205547869974769586858647333898741 -2612294559420208791815554010100531022826670009294237097218906452805851976960451833971257091500484048552506300644536870798907627346813539746460119072769 -4886652767199484464651425905879582096234981128410546139257549524800652233964932550117395659883947428238070691107034177061699574595056075593731862734910922 -476948687153533538165001293464509838331769398407122530531483520520031422134660870238181672367938291384232556352408026637315514098467464396275313797141550 -4019828793218805646023303231154957746362032987800527539184202417766187717897646394634196457153911354739604912893653289014782783880263859608454097188945921 -4075360670358372722795335962773428342675661975784760871564112696474315403655585860035546105060954687834105799318439691150952278271480480434794674741133762365172004076850361918268011359368504306942773658004160938910939812513459072944161883845284706441576028699349927725653931250854074869857900046581761 -0 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/modmul_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/modmul_outputs deleted file mode 100644 index 5233592c5e..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/modmul_outputs +++ /dev/null @@ -1,3150 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -0 -0 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -0 -5 -0 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -0 -1 -160 -0 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -0 -3 -827 -633825295391748780828659810303 -0 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -0 -13 -120 -145483342680740634262446800896 -170141183460469231436539398536531279872 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -0 -1 -296 -920477174324206192629261533184 -9284550294641477953059815425 -57896044618447473228882389021020048158893283581286713298429387432951596187648 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -0 -4 -771 -891784777631246170114448424959 -1327929446201306785262743695648620545 -57895602960811796650871631801676582813791887579127636891104569893774562426879 -115791647579680518644692972256970658065423327120836162316857392245886991466495 -0 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -0 -10 -348 -734763819450510610632565325824 -83076749736557242049732541826465792 -56086793224325083479877516880625996376700331315327416568653997679101469523968 -43066253535244233026187978142387001755402803069145978156050831917314998272 -201496559500976655734260440277137434576370058288390243702942488773257463333742 -0 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -0 -1 -455 -12835319081690733822122917887 -127603331931914074262646774143133941767 -57894298449951398757252466847559442906950400042978305056517893187912714944511 -82474589191995733226059000413673369947276401483368126437014470136553367666687 -1725367873614246661397681099761093360336884063153660970896243052915304594387 -762145642166990121634319750876330910924084988636669303246807590962648009181396757661856053558136824594202566053704052044355748404436481867775 -0 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -0 -20 -595 -182776107722364003135434784767 -281466386776064 -57894277771803943810359491595023458960167522338927730828258057680034478424063 -89555305827206481420887328961785203324625996138257453019240897699860153630719 -31072229483910585358509303280765563029411398133731077240605874038996845471709 -3273390607896141870013189358366943131609359382662427080975450085374873026606564249289725539410814263596351691407518053929870103702401568479817676881919 -3351951982484124983609172268969732236347481510662869536744042230485685559782341996467619926017805728755414692283401971053259449792684445385138897718083585 -0 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -0 -3 -780 -899245128272588436190215012352 -79382824738944801807794176002 -57892510978869681736730162220291002611042557329170362915611609916721313349632 -63318549916452990938470040291368379201286383559458191806845672058759835287552 -174649700752953765434988902296252491183916410461948522823307150782111144039507 -3273390607896141870013189019906287111002076719299707330406074198757955051929170942113619750485120149822099821188584330248544763114365430245700361256960 -1675975991239013909235918173490637855139324507692685389812651662983087942796182042459207730713681429512584287523480954202975430917613169254383951955361797 -17917957937422433684459538244547554224973162370938960458267029029640947834979519339152474433150005542914 -0 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -0 -11 -372 -21559839277448701880456708096 -5316932265549267143283400319839567616 -16282337714987120045892153486177165723161016453744476162789802658371874062336 -88537159989150876890408569427587371627685568624275408353614000822051296772096 -117153649598454680903779921606971322936867216714956963589387307178373077018966 -2451846285358576582757929577719947826359368522202829009982370031469538161149259226929244677345525601309596834316982200231651181236759995889790497062912 -3757219424385931343692363754866841943466352992489416825487678472853020775555494604110835156534238535884545156565469882173219151445924025758587329876446051 -1637494313049913248505508525988843145443777840250318553676797222770022673451141317105487157651955800518089884835975451904017985829916792949984404185120 -10058766516776922387083424546997939506014390161211981301999104219673036875504700741486695698194962976600388399803269774949866748354955056527082238634885120 -0 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -0 -0 -303 -1157847834746177602004396802047 -339617671798359351079692875821060456446 -57867775067306645753843239588701162944294645608219998114893217643713234206719 -26369455828243150957066923597651481411694301395220676885210503666274163228671 -11817720390503827648781894443752171196964219125141796458180108788425643579396 -7804371375807015860240250407429415282553584425143433436919818048882206801446222314107580658597196283229772192970700820967091727381696957189718015 -941081632068259047813718693871170463934954812652267338340003979702879896717377687462914527089361639645292415086148993612559161291073345891219533759743345 -13407795142668967188561258511782701242040321017549139392778624385515507058390936234830335193232662892196414947249258255251754028773259240421675666352640010 -18030219496951436955703469321804144806589781292361309272091751131804808417784938468039344100394760314581363716192040882359255669821551713959394630176866303 -20764409093138106714694669222174000783080657450980639056024872831998438690744740872244449057340558976478438798965361453061982147397763104606367603575189606869434278459569124428885756214636676791959099839519692515219337740131125546138808227180751694999895383762073154332479036368513723304712662417407 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -21 -0 -0 -441 -441 -441 -441 -441 -441 -441 -441 -441 -441 -441 -441 -0 -0 -0 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -0 -0 -636 -0 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -0 -0 -115 -633825224556262620861210558443 -0 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -0 -0 -704 -519849000561460964203253727232 -170141183460469225533581294949474762762 -0 -57896044620764341436046621909527197168953141363606679998708279488336176873472 -57898252907889602413753029420559456162614731473347531717023779886319263547397 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -0 -0 -768 -315261692802637380403524009984 -194975556187471037014256123925 -57896044614234985579492874678279777231511723977068592025195038241343267667968 -0 -115796505811356092795647068781241627851478825362266036097151493121449782149130 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -0 -0 -755 -980371960117523085246513283051 -27886518370227442490517617608621031445 -57886769803885777441266973072067004984382407931727987471373869918625558691819 -115782814431179474733867342212841622090776966681872755443595390653954644770795 -0 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -0 -0 -44 -218233034056168691435097292800 -1744611744467702083044383378355781632 -19901765337664800850390559730004689805459726391923360699911853410490607730688 -904391324240128893549947540990127036863458864452065541277067470263614963712 -62920486818360159690779177529226303371349723311026208168990341953735925188632 -0 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -0 -0 -475 -269541700715505410264581275627 -297693402123626315271960004983435296922 -57859375075817421675264509035607066940711169672592018945053659095526761562091 -110877123712432405983959456058661230794625399151749997930016980310850197782507 -36232725345899179889351303094982960567074565326226880388821104111221396482127 -16005058485506792554320714768402949129405784761370055368182959410215608192809331910898977124720873316478253887127785092931470716493166119223275 -0 -11731831938715777520612778668353660668457099020791414454225295929553974535670831510938460179265466667423020702522654604586832988760873000236316670377852914 -25139639868658374620186803666221046139915857558720373440362266333582541989451932860803919374764761513214467321863672910499344396369729736178810105052004331 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -0 -0 -691 -35346468568505532881936842731 -5910794122297344 -57858940834720867790512028732351404058270737887529960151597113430083794632683 -27987994577646976394885848050655511990655596618857190835730974489325485424619 -189349345528550113558840916863781287758481416281111632097967585676192554370339 -3273390607896141870013182589153822719463706115049672910839483679347322768167747021268146513478152778945973706436024623603772905158629315134471253524459 -5027927973686555902021074910529630935073811046690329450391073974713817483441477451426676218078007733400564805010857217050528619214139991213665936757227554 -0 -13407807929942597099574024997867385471458758537928833560431408446992776237013389516985884383362698273068238750137810819277666550973482668621461384635351019 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -0 -0 -36 -1137039323585710672837611618304 -1667039319517840837963677696042 -57821838183101364244296111862969820726646472682625233986021710400507328069632 -55976566637351530465294054767787754721301958177994368678033697653636844224512 -193887663560237731860858557979085795918560035746611554628781897853775020813022 -3273390607896141870013175481480046286710770184432558148882590060392045299942487570569924946038576389686684431838416426295940752809870412218007625400320 -5027927973643875746059723456610435252591983579747963576265463600113040980879343903461819778081504218012049853558984265154213360147307310400101030144508015 -376277116685871107373650303135498638724436409789718169623607609622459904534569906122201963096150116401194 -0 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -0 -0 -548 -452756624826422739489590870016 -111655577576534610008951406716630919936 -52448868921439032906975899518935171660069537721145902608130331363149292240896 -6606931978479281254831897832501046354906618825234252857566140055339491393536 -144389273399687404130106093584920102378421828377891285603354605032128614720536 -2387912874087980187718675679704917070297108275613437367396044575716543291206867105152070863644327560068474660815235323172050351528107196479325467574272 -3482688306166017347803243385471281549458835137781679310740694623583921742064189215873681869954494749194897826789578092866925457557981431075429025110142766 -34387380574048178218615679045765706054319334645256689627212741678170476142473967659215230310691071810879887581555484489984377702428252651949672487887520 -23524785833119010734715565516813333025879573854445303556124669257483291515289313842947157240147918658465448640671956584728128012482443412626594575622864896 -0 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753536 -0 -0 -7 -229443170195852266665106472939 -326323769346777103406058243606905356266 -57302384040277608603670736599583187724940326542667573170935472667337666068459 -90590215444683886737468380513972670188957748728496883947338607689822992596971 -16588391383794291139492557302647827206669629364357230644404399985567914899541 -163891798891947333065045258556017720933625272928012102175316179026526342830370668596259193830541121947825216052384717240308926275015636100984078315 -4678930352245731830140813477948099889967135524798399300239972912494574922144691942630433785423691531675030624592070979309607042551255641744629228495565384 -13407539397196368968305928790089016653671570609951672448135458669233231578292961080874356758157767482668285483672278785166507680900960181539331299589837030 -3215987397587457281700155817600247737540168077574887142213735056600011032864904372279321264397358905923222529639441151106221660309360444263022362294747115 -436052590955900241008588053665654016444693806470593420176522329471967212505639558317133430204151738506047214778272590514301625095353025196733719675078981744258119847650951613006600880507370212631141096629913542819606092542753636468914972770795785594997803059003536240982059763738788189398965910765547 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -5 -0 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -908 -0 -0 -0 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -19068 -0 -4 -0 -824464 -824464 -824464 -824464 -824464 -824464 -824464 -824464 -824464 -824464 -824464 -0 -5 -0 -0 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -0 -15 -0 -1267647381935974515131868511348 -0 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -0 -2 -0 -263212975939693434278982451200 -340282366920938195469076704579402334661 -0 -95623469641141975580204150051561403015751352902419726649509057396736 -100256331110318029012876581068708363797593997986020913943444717941192392931 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -0 -5 -0 -411530291995791833340936454144 -8430371667534461981378312405900 -57896044427411158329067913577748761602139555529994913557251651593513897820160 -0 -200512470973696775741802001729116624472381964469336023047436136864269992390 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -0 -20 -0 -979496649996061120625989647476 -184912836387971170628447453353642820495 -57495019294216825495300358413882228250071985569553535706317326020767243041908 -115391064295158177288750649755718876615210881213845660614425620051755068816500 -0 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -0 -8 -0 -381333582997345008331364761600 -75433688760793975781157147978430939136 -36185027886707999350496222768347471118285021760886825059748692088495240380416 -39104158210001763587778684153287397593905745186784548165694155380922018430976 -7723941625792713615971218884053930976624837598150250143250994735002495495486 -0 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -0 -5 -0 -245614345371770711601028529268 -167820641058901852935904395164427229736 -56310523432975538089098580075519192838001302745960229901614877098209729051764 -85237339161927006470244447187006939882425784659579906112900841728046555724916 -177131608341019431639531082486186163608416895761801191709521384618872970096746 -692028243087627030443962333795708467119069169682095727348101292594084392336708255956965296630788236731535929976763279256275019551228325535939700 -0 -6703903965663326792874639644623508444541867482964682679434051891075530751941922751907842696366672689743371632139729220006073257144919838847329290738990536 -6703903965663326792874639644623508444541867482961636626584690077349172630440349667689597258763613066465466237204690258874126738857263918193693789796695156 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -0 -16 -0 -1166128089190511751946393615476 -255571479192666112 -56291747675086446310277050772845769159148347468038830641684235944770968484980 -30171046655374400321248558358633494123141603303165813785349498983526118194292 -191952216559694677850253030981251311226493861529738242207409594273293055910997 -3273390607896141870012882374551932440803983696424025465309373572025468806406202958511116713364615934701710072950290988641362149742329879268357377621108 -1675975989397670037760469732976766982351910933877220375401361925712068316784901484293350661165516699901005329555649261275619258565077645558288068779181545 -0 -26815615859885194199148049995734770942917517075849908645075912980200181493966889942992355387418779976258047876705787108142786970073112315813368816870620276 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -0 -15 -0 -147591445132561651497663725568 -72079604862961880041477111809816 -54687499690776483454845978515775404153680118608348765958709666856417080901632 -60367062534952635374606720010641822146836281167009160997706035914060475662336 -178652301000333807279700689952188731164083671132840159036419863339428204714256 -3273390607896141870012575052276265729391325363074491948316068523863947799333080042607060368834360627681011914158469885997952895805516362689834790158336 -3351951978795340075520939465953533964703821867770710256609903421209625894295852147822976953763845975898117121470212503185399920690105737901876342591329002 -16269505807179569785489260726049179236275631432812576096106462358913980634161403559950446785300205032965912 -0 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -0 -13 -0 -561575095917728642643722764288 -63821360225596077614082986369572434958 -20871267450490110775349857218815741788300741818091418479909066205998432124928 -32031339597730053368408931380419192214478581749743918680291072536952960974848 -78578636492035187050572077718135746998684508179115744547225899703648410312403 -370813736211065535231062726945202826990026850884684216063072714490283444143902782007113348673056265481923913676784506940297294856753087911972659265536 -2620071154003612760581214322788037964725604937167931635387043904448806788414974868124012072732534985166121320391570652048401642048690823355593944068875722 -1486844836249321229643001741597869576062950278947289246738531878275180587493636315931782339147975866870425615431065710328848331133564447998585839000088960 -16050604872479499761412490124306950869110460588744266902155691331522600686097332182352004895063052889078290340385962178123999076249427996544240088860590080 -0 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248128 -0 -0 -0 -443488317747981344772787797108 -77021562540042882543736912340894874738 -32227291991539810993520229032200986746576275985417526906409482965081219136628 -90295509201033475928510352277060489802362767446802083703915909927316798569588 -77638221005315388787307758184174122082878241502208415068156089607436757773918 -7086369209232770401098147369945909076558654658030237560723194788385043775713169861209683238006254225172633151217396345438119288462580837128263957620 -4782294357738321615826518443171082886036061084269642536684046318902415683538782049212941629293505381904644369995691924320760708675564364862074754654096757 -13396197085486637899982055632974105159517490033004013453211059152114314035947763002926713162586156070095743774033239286885832152764496920107358633659524867 -13913628701936294298439646799953209203835997190662346291813340972081107507476264087323589245978744413156578492374221121684868367585557359458783374461631604 -8138997384706728877072035945360424843873187368355532293441241975164893238867331679044578164475889238039813183177135485141754426910479982949590179951415562696524274642701652917859487803566915383278625110689674346601085345682179558937583211472781420296541000741603192074524711609552018240800068099636340 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -160 -0 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -1267650597867046177654064545792 -0 -0 -636 -0 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -26620662555207969730735355461632 -0 -5 -0 -0 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -1151026742863277929309890607579136 -0 -1 -176 -0 -334882378198275252702386771897964707839 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -1606938038272679619211255036084048932956190504430095264907264 -0 -3 -660 -0 -0 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -0 -13 -132 -0 -340277175258524197588911854800409200639 -0 -110649228791921829438857058184014440939889398729492104024303347725965983744 -83242444436937686798839045020185798417768391449818324689293403196404662888244 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -0 -1 -144 -0 -336939847389361696912547761965915701695 -56769680485161825172645874508895991003378952756364273939185366956838435684352 -0 -152120161136883904630679410835682314109137252156430170124918963201108868258135 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -0 -4 -780 -0 -86062321544974934655405918178028521480 -1701915421036549486423916534098894939525282066843008931871172149931497488384 -320684042551872460005602592720729811431853324554571705767199610066341724160 -0 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -0 -10 -292 -0 -15950735830886245879793511262675009024 -55295183545873315171593510499304639359309142595148333408350307564081396056064 -88673301847573357691787887535109587141732823026243004811042692202944186548224 -71382334957950784434776455823589545246296460825554303150898409735430888754614 -0 -503374556654131282931862970076766895496736542723035164142032759422296925049432254807648984573579824482106516024331037426413253887593584118716997617959386 -104748499257567256899349831135169132621460671660236086705487934064427347429028587580187955384662408370456791930169521510043606360268932440897712509943296 -104748499257567256899347058465480176306318788546142086208502905424473137964171226003463092917526586968944371705089437305260822371159371705523810370322432 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -0 -1 -160 -0 -255379205689110469036436140398487013344 -21712727978259726293356856503923709634858594384551721609867559528598392012800 -78804868437368052447826264773726337899091993565064061055519163000168722202624 -126692487956834088798699213663796104822239968371597515995972949581479497637654 -3272191856449843397936528109279065189423120989381483148252736570244975246791533222321103893532128937121700575680421527659060354684671413348913870012416 -0 -13407806431527680864118905249453282443943463537493942574619644941644874674867613388388672408903771364000715917576061318747309897108671036700271413040513020 -13407806431527680864114646428811044898323062754972248613389335474203674829637362806742733003747804478536315412888860690712090962708053524257498831612018688 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -0 -20 -768 -0 -339617773210407217629729401159633862623 -7010406519791895781934865720833530372990739797465297891865306283193669779456 -12209728235921518710323559633526468837955820087843857124387593282079453872128 -169951871966800283810649419399139038928050251176624833301343281456431633787453 -799167622926631891612934308861836795138629576941338685100903095647779127140015260950429312778514706312501025604542433181196096945393694423654596608 -3723667608053767842867675023364612649103089456109822250875653249116275769787555601877076944892223098697379930243317445603459059303034099481845912857059652 -0 -26815615859885194199136693140688805876096363840529017362328825455840484156274161514345456537746861803711227865957219195669584452460326767114515755500044288 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -0 -3 -404 -0 -338702491610940821868309420475587008199 -14328023572559317685526112591674235629243509140269133282451074114125069025280 -86578464609153953273064023043464932375781820914327168919289930680154219085824 -45279483869291221953284779682805788559295893933395790607583424358602969434844 -1598335245853286496935960547857315896747917205953978724820311075231669760316560595231635274478219159108858101014665432801177715434970395175083311104 -2419407242378299627775637392372328814292846869244624971419290896315467839379870742253970126076794151625802980270332067936106109204494869156371160001888905 -22713710091930133642306470658052071301354618504883936111506036530073330776648921189746483856049805580566438785521544183006327774117888 -0 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -0 -11 -500 -0 -303146897509458239699003654989925023487 -32736756639827299933225685485072629321729229567663511068893875767449657278464 -108940097746668219126218354657538950829059055375326097462906774176671100567552 -105141957406996918481926167015587865122928475911973508855892157417175502573997 -2455149808749324871737694767528984736302289775261104798859058407114388639278815192018708121934602436285743845105491713074908633063448335548472405000192 -4562499427405661443491300822283693488291153790729079292153765540644759467686485522209256976888988737125973966009916644323573026335699548740771640065918781 -11974395290736899579322825765453901754951747606481982721750002227664784996135932884515664533409327198618661731940390586307041084433465121132950282273424559 -10582878391618641992149234773030997029303083829182787525505648743266561935310197607787383974646115897854100240644934138582092413692188764048451304742191104 -0 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139072 -0 -0 -356 -0 -159497014274388579608780698593857114352 -107785315756833360443652745599246659761450128680158928423717212164001366016 -8707697307478053793157872089996555339211642752348089356505163635680694239232 -117438470015262408821519431736839939113907975050128689419137692043750973261407 -2221209391926212534974417566134914959972451519200164319238141998788375520151939771003428560858041773117069334356919987508503730468005507757378633728 -2622195879240643840430956614410100176111727871855021220330072279789821003236833457966150025063799142237083045026927065925049487875866674616289389230367401 -10838798868213081376093889742061103552907660140058537670650158514072587789313088268351190762885376907745601175335770832945166703110256088823029782185916220 -18496502392531382272454270596836064792028917016576313981708381711553340991353189878658648246899858104559267375534283098234252896441286568201405948139405312 -8023149053251434330424390239980489075400259799640003038245956978737225036782755130046389361466959635606250835455657093887634358047146742300058148458943937296682605801163384099346471767498054974948940568984229726746253562492021189947972342847243200153751700399237590637919220716300341227271998607982592 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -827 -633825295391748780828659810303 -0 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -340282366920938463463374607431768211455 -0 -0 -115 -633825224556262620861210558443 -0 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -7145929705339707732730866756067132440555 -0 -15 -0 -1267647381935974515131868511348 -0 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -308976389164212124824744143548045536001140 -0 -3 -660 -0 -0 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -431359145870941220571487096504865044588697904564591140391738786447360 -0 -9 -205 -292216721734567061880326586369 -0 -200867255532373784442064696808803448388348625007985567989761 -210624583336731249510797988433774789014354452010342467565984415745 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -0 -18 -268 -352965838883581655720191328256 -0 -0 -57896154828972001316191871561208954842030688974154477763010076354291706101760 -224522243723977870473546939655667008360304405123189009020455613292517041926410 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -0 -3 -540 -355044044325391863154206572544 -0 -226156212087570969245957375363711374917747764205331043944363811571683557376 -0 -58939719349598034788834787989153374973533704931055104033507141435219301294150 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -0 -12 -201 -530142478699265899132312616961 -0 -496656137810479889911783346996430981790696540282828777020061097092186113 -1385979954003213188845730955395660891569986416749169143986031551791165865985 -0 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -0 -9 -868 -5739147702547097575372095488 -0 -2713877091552199840178420005509414594154173361691187048697847208972757499904 -81362303935415148417232364154000808137283096524262229321247229438793141452800 -200007298521575553572893786526415964968717615038369762603224568413766370350860 -0 -365274778563818870578410673942542327115651568182545616551841135130206091716597190978235713154149431886591172348565386179014379218648408362061584266709978 -13404534539334700957704366709891405336608567932898539989935340808342845456223809807251372098853358563127263002410967890911834839205657225176731688399536127 -26812342469277297312995538029057334885559747174722586807584708484779441945819860598056445363795896769119485060050221372726166946816056038393356849495670784 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -0 -3 -373 -663267250817584834358806577153 -0 -56110155743260739980845965626188839982990513354097487512099126177554948423681 -66666258429912423795302025160998297406283412204636264833390604129473579188225 -177994438507773838104674369561918072421924948198026690191960823057415806012035 -3273390607133996772975242796981079624647701753259790782144449701319437329708326713265031806779102708651838281996474323697106010993858402738833536843777 -0 -8386426737429153324968205650664410607248107158089641410510843662012750503977158430826944378515156355394129659501044340493857762955532974869923505951997952 -21794234667370607206078979989803643224514176633634282995495125018424131531561390135283100088614877484687692308031922838398470089333320084110908709465489409 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -0 -18 -837 -1055049663028717702603910152193 -0 -56145099392931667494602725117889809978473099780543110806328723994744381767681 -94020940359982983746102974658635634485631399798433967904585529336909218709505 -122813165867929679368114907950462646511665206961139246511547700775532269629820 -1453677448929674437119254898402693728557812421065819075020451306758949997680185293878407872152173525219742439043086557977078210366537729 -1212366891818018736612622703605109128833655236089151046090687816762735515567219375580031566019793843481784966322613342296478137028286856502402053516577749 -0 -13407807929939548517005357036703926880414038648958573042868430206064246508364035546678488116044098549923745343892353753273525546559935574983116802679111681 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -0 -9 -380 -521589998193582783072001064960 -0 -54281131142068578829492120775689398115089911863432918901729887943966322589696 -87063228931437267679054882381527280567353791974986020151597093229234539724800 -26411440510656131983229190397498319604014001314162142030505989681964889965410 -6097168044690820186266056327949574746284769358267195096071257642746052771416966407359752215426164404883642352301456819389505193941734761955328 -2424733783642134638362581329537135439756750250119199404424017986489904138736282190413580103086576851422180352745763276505772852789790102244589421062035368 -6097165137335922326917182089439777940897312242642352964433107601843439253516971046989164458610420100536591912816578733216389239785314028879870 -0 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -0 -12 -740 -1105766944361955339797897674752 -0 -40666663073457098908560285583894926858884570977736712496628899075489882177536 -6937574406821791937994980945260712046755937992433195708087316793731338731520 -80322259934434364526645140508220590735304755095944269027477694810351667453580 -823941825448211091053403141520018121212799809786471885049437482634825107662178709702891406359238800860434148555858011304517982766915436234049188790272 -1429667404699267204948871302671076616887599344321904849215552824418556681505872488697278049467681761406353434390315540634737585531078670948788251814191550 -11625439539104520658533890711797855194868386980241970994181859680105840264577003778987586821796875152410687590090669235843895015251319782115307313685324767 -4012611011364738290027830363884880786616340613311963081947244935301921679145302776974417725708110952074146887700764495196805422393345600132552727271047168 -0 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569280 -0 -0 -881 -82111759186571175044015194113 -0 -28976293586122847380028336844772629234948658991570145015189074743489934131201 -72183022301677682291580123397013999783183881280283071356191234547195098169345 -2406637046295717734201709310168745488608959709590841627534368777523827570155 -3270192376506343102611379566519773667881704351629044304231346291458365652232113923427745246534084152592966234784884272774908997999646616577428097597441 -2215358573235998131874848884702814898312700633459869320330807378394889665966339455958575987405219519810470844163424203838448880979342258184452542694566999 -11572673791131936765291373228428070441513344356977752630227033953477527214083770832877643183241668103444587247814952464182468268813420651873183304946511860 -24753412486464952127871838303672650263739621747289578351238067167728759524343749186993890880512639565343382059853348185532476853433055754161685198366834689 -10715075847328263987810529469149461320386431997019495731790863105054566969900033555506363209309697962458956036917241364630652892035661693225326421884469385462600854566136062395614311464777955889879559949117718229449226545818148296831892519665180916040043014060359216829885348018201091171951617402470401 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -13 -120 -145483342680740634262446800896 -170141183460469231436539398536531279872 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -0 -0 -704 -519849000561460964203253727232 -170141183460469225533581294949474762762 -0 -57896044620764341436046621909527197168953141363606679998708279488336176873472 -57898252907889602413753029420559456162614731473347531717023779886319263547397 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -0 -2 -0 -263212975939693434278982451200 -340282366920938195469076704579402334661 -0 -95623469641141975580204150051561403015751352902419726649509057396736 -100256331110318029012876581068708363797593997986020913943444717941192392931 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -0 -13 -132 -0 -340277175258524197588911854800409200639 -0 -110649228791921829438857058184014440939889398729492104024303347725965983744 -83242444436937686798839045020185798417768391449818324689293403196404662888244 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -0 -18 -268 -352965838883581655720191328256 -0 -0 -57896154828972001316191871561208954842030688974154477763010076354291706101760 -224522243723977870473546939655667008360304405123189009020455613292517041926410 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -0 -1 -780 -265778775641081389046569107456 -85070591730234615570695746678589227264 -0 -47774745969722232427758234867243528441217837383627556618044000593694009851904 -114934625462288849574699003098335431120497043396654151768412121195303165265599 -3273390607896130240593600967117360449949302114957193475722736563312875177296082312701317728403568591421312388507522131853298460195444283083598707294208 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -0 -13 -108 -641005755209691132345429524480 -169808879001621220577003661267706839040 -0 -0 -44595748639529370249161292540321979253045428357143603299233263197536162361810 -3273390595701799965943937691979414704769916280630990616888772670076650363814963712508765166579199284291458024917011124434848164775695410421420299124736 -1675975991229868149900516882649248261736597670897713013053681038667393868471918427266328463160422932861286193025586191261376550527451922228177417820700673 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -0 -10 -812 -236535920218075649755406401536 -170805148163762844199452890793586458880 -0 -77235627923455501669801480950324656599482213698527571810732044824498206146560 -0 -3247820365520474504141328509378188149081215054974241258868837174158013735931353195652588398451957403531162347968567326435270510679101746930629801410560 -3351926412241749316243300408120743481364953366335181350921935617574468700491666785413982788876846871895349502939963020325764850199661145563589709842612226 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -0 -4 -900 -875111310488789975911421181952 -2035380368545652430308519267295821824 -0 -82888806482566675458054593503965201862559685110251188150871115527867852652544 -217838252915371173020109452768367308381480947082975358825320754741163297824569 -0 -3844818501136460352282206997400046016138484010779875391722274972838788863919430361851387711731023416592441621480864497086478738067067002557314134469493887 -13303059430491444851018025632227000836103109403803071419066794694357191598196401857213029739867435168357113142017532687949685848431397385176300458413654015 -26710860967093010903451328707767612779801770867376275932886445619834101280933749715932620733029274206270726261110489293726429458417343909725911482831470592 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -0 -13 -120 -564218626380911088370628689920 -233942201656969550061072941959067205379 -0 -25031101341889169304446127873801694869672305027436646693413954045557168144384 -165064675467119978329806304469495034196158211217327340109988910890598366587009 -3223038340265066352421850814896502694405790913890534440785162024059085821303576802379715024281065670685423211275932766116756343825199840534435352018944 -0 -13407757579211451638802076068759612924846096912682157704482048789996310624570651395256602779668977880231523760330028517458758478729840969627215870130389003 -26812343265274399502606909495718245988987084717537620990700517306408588619638088444509530024658803496505053000329815974901893831224981448235838474180100096 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -0 -8 -576 -465758099028613903974183993344 -340199292706503106677777063217100488703 -0 -63322527565292422495135184712771504401404079317527680938906069764207697461248 -33634219328043204849947836966835886827823990925961402040502023813129318293738 -3273384364399040874614920955958929289600820575248783786959254747067900097257458270481819637251484144665389340236271253096323308912816041648439255105536 -1330293784850007390428494387229269593003967094877098901329361383107711576399124678199758357643942725207512746641251003704936924381303175630906145151786085 -0 -26802522297453609678208395193923639182831298274557295628307616531706906562408033942581487447261461746690211924285806443346587426535386688183750358252650496 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -0 -18 -76 -684365645831470144534108700672 -340282230054365050593775836264159842304 -0 -15963897412190306650237099369975576841343765721507556435820338296445483352064 -132759447225495983324090510251744519265863982169783448911677742254978994593249 -3273378120901846843848851698962475229131019779252045777777029295839115754571447880452966684129870618993409677065064471065432673898932044455452686155776 -2509556964148715486845629632058434150976495148022092181360043667351572633930311684969654834869500136098220017267480404777629280818717809976634762654456925 -26187124863169041879310296789687050376151294751239194912085646467180246245385039287091804854399398837876868573183873563217432331431922121057537489895424 -0 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -0 -17 -148 -450240345242768217289160392704 -3323100413057427899902215874225930112 -0 -32128284792822784994903339199193643879812144651157443822976047334286323351552 -21399706425311438129470274645609834512526586881469866045657503407040080328762 -159433945212719143010790645441202787170674053048920862244647201067119362630573280745854349563469472787838188810917861838889021573237764523468984942592 -2613373875808771403998672140838135919995120994584909205876832476647953072519054679140019492621878245598201737849936937907935929346666864089763133225143876 -12559952028652149332649304876493520745078520101115730204663594383913123396138444109637941720895076538473855709838631312376520291329776343398099754614544377 -8017856871157406767405434525273062325252187193195991171406074702436922609351041617384360871147372313079815808387436833569595297983737028164810164383252480 -0 -620361101309323186180458062143306311203234091808422651940994483172973212453462540277947848500764537416293185308049243121091077089522237714087216426627812178598932718744797175293795424927554129248204812001597295596246882084347634304130710520242400817201806901439842621734294451519310289297352148130231477204202016235965703658712648032709044435339313088432114418262153009154752512 -0 -0 -40 -43745959819122117852054683648 -168479607903394759962667067146355343357 -0 -38715528233353617672333399869957801244330607838176871550029916258713271795712 -176969206917672212723902333860838760994531076051623743859174420245776832201772 -817958506506309444344089149754156868721831281527900674165814694260381207165130129275912203117045009595571041339148072007269936892193254382249592225792 -2495012392157735608513384785636121563536148297029125165554961898046274459648386947163697616808537824603964419169205882992327425601924558527830506385578472 -12578080335425898937631034914453481580403735226890616361473931872760592191355069944068852702365841074365524729550127783372599268817147362827445181721803028 -4414537338963156055930766392922676968349794825277298431003769994668621271197649418638348577968549041479152488344038511751351114279431399223340286968070144 -3810222062618068365811717150182972227866160775529219066268690269078204923607636500488737226639216135781691347449728957827779459090610135784916509209491391739525780725489429856708513412863019815772987039494965127359929026869978738901475381609951397576604571746823630328801160385531009949957361678417920 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -296 -920477174324206192629261533184 -9284550294641477953059815425 -57896044618447473228882389021020048158893283581286713298429387432951596187648 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -0 -0 -768 -315261692802637380403524009984 -194975556187471037014256123925 -57896044614234985579492874678279777231511723977068592025195038241343267667968 -0 -115796505811356092795647068781241627851478825362266036097151493121449782149130 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -0 -5 -0 -411530291995791833340936454144 -8430371667534461981378312405900 -57896044427411158329067913577748761602139555529994913557251651593513897820160 -0 -200512470973696775741802001729116624472381964469336023047436136864269992390 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -0 -1 -144 -0 -336939847389361696912547761965915701695 -56769680485161825172645874508895991003378952756364273939185366956838435684352 -0 -152120161136883904630679410835682314109137252156430170124918963201108868258135 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -0 -3 -540 -355044044325391863154206572544 -0 -226156212087570969245957375363711374917747764205331043944363811571683557376 -0 -58939719349598034788834787989153374973533704931055104033507141435219301294150 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -0 -13 -108 -641005755209691132345429524480 -169808879001621220577003661267706839040 -0 -0 -44595748639529370249161292540321979253045428357143603299233263197536162361810 -3273390595701799965943937691979414704769916280630990616888772670076650363814963712508765166579199284291458024917011124434848164775695410421420299124736 -1675975991229868149900516882649248261736597670897713013053681038667393868471918427266328463160422932861286193025586191261376550527451922228177417820700673 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -0 -1 -448 -336018158432268970572233310208 -180765273156339162810970479384136314880 -54500223008692715409863131082228309265686884403715505545748529854974007443456 -0 -159093831874227732340878009766662025143743110432323288610814215572584776947150 -3273390559118820771458899226766464161291486732778797295989297446034440432258612678334470043791768358041505796818841677335566510399466027362411515740160 -3351951982435347662510617978838131757377163638012985906959056077846345127187994044896664670522186682849859846388813294676665146199381509844021491556941826 -13407807929893819778475470707735784992488440665279129110225796518690280399801487040957178859490788616767266574533518552379422042573571257824675478529638400 -13407807929893819778475470707735784992488440665279129110225796518690280399801487040957178859490788616767266574533518552379422042573571257824675478529638400 -13407807929893819778475470707735784992488440665279129110225796518690280399801487040957178859490788616767266574533518552379422042573571257824675478529638400 -13407807929893819778475470707735784992488440665279129110225796518690280399801487040957178859490788616767266574533518552379422042573571257824675478529638400 -0 -4 -308 -1166518355282583652280114872320 -1867930864265730015797553584102471695 -29102955259148582154627179488033442743910184463810481360860886532717730070528 -0 -0 -3222250074367625581260309078639225792086900018469753120634976171874008672482650382620744449542500671237666592813739796235842586689401153234495736905728 -1675924850705485392947165293549370793820409330991855435602880564956203996416735521899714855412738810033999854295106109668962728741188204977372747331010565 -13407756789409068583285272117926118410139843361228200703758948995736421345554633019522866212847835982154695254228762025169838623509788381563214830071971841 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -0 -10 -404 -188392123662362022344342372352 -83069143893254589701465618805239680 -53770873442306182229265486377342933660613179904900977100539981823135251955712 -0 -105392125987055312696658210115090338353428803379328229864117878030966024438835 -0 -752977616850105076380076940633351406414760104254761864889884078510546188226624617850820251430303360555185030512473520496936074745273976121366821835935470 -818747238836872411925555128391585707728035178353612801143869743193625443209700942547827054604164470018266730235382896565849803206905706026031866953728 -25978433825210775250603471147163640182647037703575393852672170191437823190873334386823991616355401393588599822282838311791233080986102609403250730944430080 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -0 -1 -296 -937851138887707228635723202560 -71983759896126428559029594843447948425 -8631296002177605477020671384515986058586860067875426006234029207453268180992 -0 -126524937455879451586139547446289490007618298090847498853547995262066917814910 -461909328852694177172628852102963427766642499620477636423836056046062222974423229303007292519570049771036188762256954253356039472445656007369836986368 -0 -12981654982074150949064673360867568394481876121003306153656885506667214694877409921526304923306457555280612555616595184427145127254267331837419389850746631 -26382916729981577107547759771891963394403114106409408238867427511943648425024917427272522438481202960509647220636524403278529999467034622910615416018567168 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -0 -20 -876 -178816467053815670676679294976 -260528687490744071814225196668552224255 -1692970775754977972396027533715957606705876841991018145249546038233761054720 -0 -181828467860701888405812140333208298279745113568339986475984061945003903302670 -1636682816191726232111652774583460805243935699557095064129709690597186770076614594127682973063911518410637341738923286963278231026430050646302719475712 -3045552175859305524398839349214018181219011839758787491368043134180477082575118427510368420050628050508519285324861985507713808320174592548749510991674891 -0 -26789430333357330553113882063869622691267025750963372291024681554553700868085895351207370848391420617790680534043234808601716445354096156366798348609388544 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -0 -3 -248 -212502350661610110150999277568 -131552641820690640981991613732830315020 -46852064353518235919274685523532198947963594743216103431489805826506451058688 -0 -7645097472420881927533172642225448567010395265033633905391291186112176472435 -3270168961867742817620220960232150749713460118789595424509833186804266725716461911398439168187233942133765790497556243236366961654908249264223007277056 -1319225067735212639136382308035057590059309856444213959056608122715470491794696873946870339629603616630101618120768022446070919013133368825305188791863556 -52371053055727292068337250065143561186684042431774613097615715910894227837903200691375492620062343989956747038583170958041377999304092089877801155428864 -0 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -0 -11 -244 -487467278610825971461915672576 -37219007724616326815534795495032483070 -54261526529889546426837093235106983177444977555403308095742429498729088155648 -0 -107016117482346548945132799722790195082654004276904907765013628055123622641280 -532756921246156839774884522958957381993726902395061115447006962643864665611333830690037070998783636394390002400377153551361959604547283639352680251392 -648914867617031739979557694643818049979203668057216504375276139438794885369435464053038064827480502814067006804498359154626423603962100263088399856229752 -5364093535730936147463541649596432444678219926077667883734454151057107749354453510341798218396870848298991695001682180874640712029651920116811669171520874 -1991156344814561149591525479707443429666289210206171383484367556587262040991283414287056032203739853332216309866181308381599630640701720000238883574906880 -0 -1240722202616389513973922768685786106091078788555724681396110088894901580356174059693107801547999717844793849118253907517164294570375212313898259715085141105940658891307423763778538377665051892100124228941562467259128201610076266056373405707086736336167605239314782881108716366473706926696989024480092947548903006603393526774842227227605392632574744471346260936099413707173396480 -0 -0 -704 -909145740350063529575317504000 -339617676839812128055403992765682035710 -14413099430244372787741726071480578266606634696124905686464507820541353132032 -0 -146524398017507879126999928537531295106484509790504566263346294215577739873005 -3254975840579470187367063835491635394057759998624067184757863431671954891319873665317138530989747197877058152019092707613957271443147158112429244678144 -3843711574626452538685381156156840092205919446460373936017331874304198217565605768350118518588418750150969106731407527286192635659176975773727687264432087 -4205957252106146285324136572167758103773911995097166007755650365293107620480255479665969264400061595898062331701334890614260926224653154131473051276770435 -8408484607981740863725931685258026020089701582650612125407183571331861026600781248142155638797478057210678107527105854861551567600339635902678924701728768 -7247432637346354371163106580695035004917324395155467725275783809664161137077212420767747102195383275127295776660124857944728848308128852665484048296413410766845286744067292360940573825374902097450095259142985756793464941933627257907444867237270935916576555993785848630242076423028942395099630612250624 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -4 -771 -891784777631246170114448424959 -1327929446201306785262743695648620545 -57895602960811796650871631801676582813791887579127636891104569893774562426879 -115791647579680518644692972256970658065423327120836162316857392245886991466495 -0 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -0 -0 -755 -980371960117523085246513283051 -27886518370227442490517617608621031445 -57886769803885777441266973072067004984382407931727987471373869918625558691819 -115782814431179474733867342212841622090776966681872755443595390653954644770795 -0 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -0 -20 -0 -979496649996061120625989647476 -184912836387971170628447453353642820495 -57495019294216825495300358413882228250071985569553535706317326020767243041908 -115391064295158177288750649755718876615210881213845660614425620051755068816500 -0 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -0 -4 -780 -0 -86062321544974934655405918178028521480 -1701915421036549486423916534098894939525282066843008931871172149931497488384 -320684042551872460005602592720729811431853324554571705767199610066341724160 -0 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -0 -12 -201 -530142478699265899132312616961 -0 -496656137810479889911783346996430981790696540282828777020061097092186113 -1385979954003213188845730955395660891569986416749169143986031551791165865985 -0 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -0 -10 -812 -236535920218075649755406401536 -170805148163762844199452890793586458880 -0 -77235627923455501669801480950324656599482213698527571810732044824498206146560 -0 -3247820365520474504141328509378188149081215054974241258868837174158013735931353195652588398451957403531162347968567326435270510679101746930629801410560 -3351926412241749316243300408120743481364953366335181350921935617574468700491666785413982788876846871895349502939963020325764850199661145563589709842612226 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -0 -4 -308 -1166518355282583652280114872320 -1867930864265730015797553584102471695 -29102955259148582154627179488033442743910184463810481360860886532717730070528 -0 -0 -3222250074367625581260309078639225792086900018469753120634976171874008672482650382620744449542500671237666592813739796235842586689401153234495736905728 -1675924850705485392947165293549370793820409330991855435602880564956203996416735521899714855412738810033999854295106109668962728741188204977372747331010565 -13407756789409068583285272117926118410139843361228200703758948995736421345554633019522866212847835982154695254228762025169838623509788381563214830071971841 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -0 -16 -609 -545306965334305070535428538369 -279140464132946334357706429686369222400 -7406843684534142392231674926942970103518516953277563631729298779229034381313 -99504290823297638334108694763431302859273071431201477680525780928285863051265 -0 -3068828863952642212954550824781949471706925652624740282997645633157401134297482162686614226600559094354652040572382157503998076312688824831378653708289 -3351747420734084318814777709463072684680568429645439170036971071607493627162331730307565640643700625157707898035813214628403945348939886654254966839967755 -13407603368198653599916966360349182445860677952189116966338325261838307493042291733025734240527487306726277277475017816874139388719266046460146247631110148 -26815411298141250699490991358216567917319436490118111788391170510734243865899882177999643382351524030438640430874095404682320755366801087351505982316347393 -53631027158026444898639041353951338860236953565976155186370673275827169990229796710610136585486710294744741538759339503142188427219888626557524901703450625 -53631027158026444898639041353951338860236953565976155186370673275827169990229796710610136585486710294744741538759339503142188427219888626557524901703450625 -0 -19 -448 -419503718805658875387206172672 -2077242944938429195137432070841712640 -15547379883828184557614956585300039279332926776310673064160034921917517398016 -76060301533772398864920633133842399024062776386463688241907828176392730705920 -0 -0 -3100499777879036046354242314465281000311356809812322648908432701931354148039845530687059083481027219222267328994049763996642543097608483388405392685683664 -13247411790161783301453076470506974877180133239863599739180652432861657397429491299831302452529607962871535551825200558366188613745877614837598770220261375 -26655194146789027576392684858551684343747483866279183444222986385128527302433530201438611541127422007099216753946031614405036472347136502580285951140954112 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -0 -4 -317 -998761803804737242509890027521 -132039001590898075997904159680621457685 -2677345058813886421031534284112569580584095914017301741535579811855705571329 -60086096674039095602687907949217152706690659894880673128709412004817402855425 -0 -2714004844124354489262183477493191831493012005747958882863179807930524116686711719016251086947965968979909339785418727709907819948220584644496513302529 -0 -8405533274884546926710394653557214629954286686035689894640417053261039449975179806395580875447085555592435119970176210181270653403403863429651960501895178 -21800298814094737305815960841066827715322148914667052956623818275284251694892649734484894880644976907985921519360277878586310362568695504456731719639236609 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -0 -17 -205 -314141171791122356976004104193 -340199292706503125566666675774975279115 -28964696041878609460721583118083346598086669607678005131861204748455792082945 -99957388910786315412288528953468780503360427998738617462677827481821872390145 -0 -3273365633955366159313212088482024572601269794428675433404928819417293489209545301349145972651249428525225361135543177673426309845521311956629494693889 -2190371668160050365756300375697808168184891704732002727464497057741609895764023359151357865247649911895528229907788684972491076160970752924555757567389999 -0 -13355433780100006387064008929644857372863390914834597336542712946266197713041680465273314799460842675250982197484946421191657518371278691462674567665811457 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -0 -12 -284 -469455506976059516207830663168 -7983013255172082095516649800004406650 -3278348336177517342236710812605326558782498594141487069958521276783524839424 -49934227818811015946301187530148750948812049358364108430512532097940534067200 -0 -3273340660038035347812612675467311420171630095312401315302288177974137674243108506349429817414905569256600781798349855567184974503808521851120146972672 -982327148533126242072022252525591418809619347163444615270839351763368878975643860701971724697659683917532263718739457150238974028247429927345334167713733 -104748299685181425020034909109462515817741155084762579290698462017633201711683603583490202643438294700869447910072736485865669638419855454497265191747582 -0 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -0 -2 -792 -908463780656071624777999056896 -261515365625536058211954562321762025570 -34363418467612574510754913120306634286074855670046492773113408281091304849408 -10298831750975224705374170392070256950455108341101642844973509149630901780480 -0 -2574028491173218120625899532721726274186853463121682997388228482942119986058323148061481093086189118448821055880517089549298918525641370835815728414720 -4120102032912485584810225493556057268029400391693494480932846592203494031076348985539608962819052881655035473708663582142166452080933667740632631826057007 -856003575715523076496369484540354031854736721527329146406504552401922587486406713440294096206322908227412081709722944295562761974115865043527354259339729 -21764966850770680808129698311404458926940461873933266305652877546331848854154464975008335516009248494361468975669230614809453688953293987972822568271872000 -0 -2481439672835455316435450222961490952467054688477443973252759241836914049418059193066289201770080478782010589768308256548437208615056252516149995383823877308087652590010038324508787501850632011084265376115191445269164718609708653219057772287156297315205415965661016801043979428655461218374679406517792258741473669803979329510535342890018189384338466515427440201885258439091814400 -0 -0 -257 -298786413494195337926333693953 -337290227426691062405101930191574138860 -1721557327184276218905921841580400523329018723867073430309901742933509406721 -43140760179084762019079083403357933038043634800552651991719281264271550316545 -0 -1722565222021586222240835395091318715753470054907533129614806861693763466404200515482815025582326808788762316564603538107574831856655144128570072236033 -2234952031666178846254667297858171395150952361837877098087851428105640476175757319358218484222203579051989090125885331901125929168491732957000222166220648 -1518988687987889548258752669937919719163485722572881415548098968240657129317428928409749883901857358027538087530548460431243184110285208539870701198734266 -7561054567803749177499530087746897215634212883033990021094138935566693847154583699959527092384199840443587272455015656435728392875020886765971239530921985 -2731552852909560361740271235103848633585430399772966278368610671996079656149041832681481179874533365237667879503570748309035396591549050138743255246745020083145958259577107779208560547563441300194243546659169188197945241464932070395298656044644609029877051203642896482946894197008303189122778822344705 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -10 -348 -734763819450510610632565325824 -83076749736557242049732541826465792 -56086793224325083479877516880625996376700331315327416568653997679101469523968 -43066253535244233026187978142387001755402803069145978156050831917314998272 -201496559500976655734260440277137434576370058288390243702942488773257463333742 -0 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -0 -0 -44 -218233034056168691435097292800 -1744611744467702083044383378355781632 -19901765337664800850390559730004689805459726391923360699911853410490607730688 -904391324240128893549947540990127036863458864452065541277067470263614963712 -62920486818360159690779177529226303371349723311026208168990341953735925188632 -0 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -0 -8 -0 -381333582997345008331364761600 -75433688760793975781157147978430939136 -36185027886707999350496222768347471118285021760886825059748692088495240380416 -39104158210001763587778684153287397593905745186784548165694155380922018430976 -7723941625792713615971218884053930976624837598150250143250994735002495495486 -0 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -0 -10 -292 -0 -15950735830886245879793511262675009024 -55295183545873315171593510499304639359309142595148333408350307564081396056064 -88673301847573357691787887535109587141732823026243004811042692202944186548224 -71382334957950784434776455823589545246296460825554303150898409735430888754614 -0 -503374556654131282931862970076766895496736542723035164142032759422296925049432254807648984573579824482106516024331037426413253887593584118716997617959386 -104748499257567256899349831135169132621460671660236086705487934064427347429028587580187955384662408370456791930169521510043606360268932440897712509943296 -104748499257567256899347058465480176306318788546142086208502905424473137964171226003463092917526586968944371705089437305260822371159371705523810370322432 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -0 -9 -868 -5739147702547097575372095488 -0 -2713877091552199840178420005509414594154173361691187048697847208972757499904 -81362303935415148417232364154000808137283096524262229321247229438793141452800 -200007298521575553572893786526415964968717615038369762603224568413766370350860 -0 -365274778563818870578410673942542327115651568182545616551841135130206091716597190978235713154149431886591172348565386179014379218648408362061584266709978 -13404534539334700957704366709891405336608567932898539989935340808342845456223809807251372098853358563127263002410967890911834839205657225176731688399536127 -26812342469277297312995538029057334885559747174722586807584708484779441945819860598056445363795896769119485060050221372726166946816056038393356849495670784 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -0 -4 -900 -875111310488789975911421181952 -2035380368545652430308519267295821824 -0 -82888806482566675458054593503965201862559685110251188150871115527867852652544 -217838252915371173020109452768367308381480947082975358825320754741163297824569 -0 -3844818501136460352282206997400046016138484010779875391722274972838788863919430361851387711731023416592441621480864497086478738067067002557314134469493887 -13303059430491444851018025632227000836103109403803071419066794694357191598196401857213029739867435168357113142017532687949685848431397385176300458413654015 -26710860967093010903451328707767612779801770867376275932886445619834101280933749715932620733029274206270726261110489293726429458417343909725911482831470592 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -0 -10 -404 -188392123662362022344342372352 -83069143893254589701465618805239680 -53770873442306182229265486377342933660613179904900977100539981823135251955712 -0 -105392125987055312696658210115090338353428803379328229864117878030966024438835 -0 -752977616850105076380076940633351406414760104254761864889884078510546188226624617850820251430303360555185030512473520496936074745273976121366821835935470 -818747238836872411925555128391585707728035178353612801143869743193625443209700942547827054604164470018266730235382896565849803206905706026031866953728 -25978433825210775250603471147163640182647037703575393852672170191437823190873334386823991616355401393588599822282838311791233080986102609403250730944430080 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -0 -19 -448 -419503718805658875387206172672 -2077242944938429195137432070841712640 -15547379883828184557614956585300039279332926776310673064160034921917517398016 -76060301533772398864920633133842399024062776386463688241907828176392730705920 -0 -0 -3100499777879036046354242314465281000311356809812322648908432701931354148039845530687059083481027219222267328994049763996642543097608483388405392685683664 -13247411790161783301453076470506974877180133239863599739180652432861657397429491299831302452529607962871535551825200558366188613745877614837598770220261375 -26655194146789027576392684858551684343747483866279183444222986385128527302433530201438611541127422007099216753946031614405036472347136502580285951140954112 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -0 -16 -340 -489999298658060606770815959040 -65917831211867928874530031796224 -28969231374168613263842556329545499540109231248092360272492724547015497744384 -68303323936774140202719196442124407349064491640344301413273835245323528699904 -220582077585708131409418305801668729509895157179171750108892384250126492963439 -0 -4804945288123442010222401382109230365079788294360419184263639495544851827236040774654927416132970446792680843281670840974007791035147359089441307393725140 -3995740589390131255265030474810945587346695732799634773144861961076152108877615344840794991624367988213509285047106094674019831607362782913160546304 -20112528644247378748485207678778230522376838717178079586666155777961060521345313959113396257799770147881566663604597031528679108324303709306335886028308480 -1189613526782226450238449998519920448105001333280634552448143810423744903640583354069064306219173184942982331075095637132227052133984240969520801427024276971961154677814797680960299256016991306350726117506035295751028264478585708050339890134372768016601971372719322793491437413935874048 -10715086071862673209484250490600018105614048117055336074430675836924241540472703456698285220172692381666915465456597657220856438022326048260043738079097569861723477222864084024723456654120868104119493560585512807944190828392178187984719137393049169103254142087127248661337626132381236011316443311243264 -0 -10 -348 -523874002024118652564917977088 -338901215317800664056147458209075441279 -20015404885278470017469976725210570559655906804843243074581372998796865175552 -43274941068732512819275538703072682628533785069604029470934065279649011728384 -151466924635469551055256360824023267312984281713620934309319977921329196239204 -0 -0 -13404087018778832492634401257245418742650562079557921670746800976248311228584588308262299650079813922944058763900421125187070437820594035236042565924945791 -26698406546920616841305199515713884093627804673942214629126931437741327626828248539905171617610372257154484914410867070819728868223045327378733339376615424 -2496627633295536710441065870382453446241561054214863710921665326529331643172374721617027428678254874230352879588117492841911257615156115956910959191976693536620732271898886706126399033775592559421285971973156294900372189579993713535075264782220371887837493529329161088095671325291250112790528 -16458372206383560850154727152772241309834362634645429482097685030171884111143709030259566480970840675586286057165552281949963074948236086284290260005656649082848245798566424861913543183906541851941660459520122404258933779435071795354694349641611033685309336221143617936715894403545863381859480370679906304 -0 -11 -36 -59097216112512413701662310400 -340282365019520591560372007455816679423 -29895497284693382961837918038726436373289999003535570507137440248052571963392 -539397372001143345279173783832913095317038465503343488764756916815116894208 -43994058047115475121282498486196678252570359157229388277889413858394010898203 -0 -3274891444621295245553525375878222425626418688341503128076239321506909158791609365713742593237016516146106629422990668626267243448311183620042075794130081 -0 -608850787303864097127237682881155896480025728423916605455510274914880477289308210564971661455576995032557297687074419380448068658475227845925355707695104 -4872657005698891626244130971294875853353482202322888855972256338282628395923949326743092700819820592141121207789967023511190748555596067213433893308801964320495837037320565222597343423626118652097789780615868838966704829900803241659660193801529518775840320495759898504684544115156495368192 -43888992550349509466047490008389760228034918444740354476264789433451422494435484145605446873947256471512988292597450618642207676514741311261882165968599848430310026415377131508214755446432997138431116604680521746555008944242629428478468183278110882114932777127534190355484669739785885245968619478007676928 -0 -9 -856 -104786732573771294575085748224 -202208808855666172013483050871179179009 -18208247384094454656528310293180405432371044123060662434648593997496387108864 -105494033770685441277383922803075785930338167649609913078213878653210490568704 -70973503770577650219006804111060955590039707453059535237966473815460451376894 -0 -844360569033703606248598340834091269063135277987118651898622507491431258386488521835163613652641657086294536373967783421434176997645745172742040950152481 -11325931297285258457955557624614589968683841815173942490148987555849011164310979692749741711850895657264350824049096301947145237348941454577709986315503615 -0 -9745314011397783252488261942589751765359439629682498258594182172731326020846015217955153176479771037023708782206427041980054199788372006158023795063990197799705875528943477976666060127441354860040345913426334531070250301774693553668261371384134782207849454728022523237602477129993155510272 -87777985100699018932094980016779520456069836889480767605004803903623391638540464457280122746011077411993751425324753978750781979522477580196467009117071428016628499217023421730630965195213525748235513398478599337876370083082111993797578339643292113170849335330813036879783075982297998725326138636180127744 -0 -5 -520 -604099872677461461719763124224 -319016032279817633173454602139076657167 -25937207995432447928130425230903848811380238160577523896265360534027632640000 -28845060685139153243157308053688441659186794603444860805606738986483991969792 -153301215783503611051396286471481032939260048039658940814506725148996787175097 -0 -3149305835778308075879605336418583050818943987437860348514400116385895791740711322789057830278628831079477475379487412794197710343939864774337162678064305 -3659421921346067805224249323884195392692805978261828687014108315869866733026982892516385418585908368056386669445315625696706939840887495172401263957634672 -20207055997465600258590823136326869317265887543207831056737256668785160996102476475846643074001715697259463093105530757445235263029351945691975506286608384 -0 -35074662110434034853557842365135370752418744845644232722536724705537763948482350096800897815346751615649214933728622564185847196492435060706089044924648133026630781093917108095545664072447733261889308217617387286462129653489067160073127037621299577013488448084508231783465815261392679840044175715075506479855049458960417568692240224459402814116944692003822183536118940032489278563958573604110859792014017609258239480765944544557609136035015279952003072 -0 -0 -116 -805503763346205994314608148480 -340183520577929235344824363378070683647 -50883512962844550181662585462065381386388661208506498472907426814607210053632 -45369850654893983160918274674785697866505559719989912454419447783027986399232 -154302323811821750963079572939589834113268452193107213579317085474668962267832 -0 -4429069692833256013828033293417215715515853411462216173559663486555105143091358435961898114179716869586726330539677802025756405286834909255857771319348908 -3413964447864496376412885553514780382856915468175834848210969401153418011678718897157435214753452394983129758958670494207724335130333416367673732227946338 -16964157088706179080247287469821598064768337533310042213494946742295692399965660890951897881516503810954503600149541042117195593349934525801018052923359232 -6219124707082025502769567924616693739587312435415643801252808044792830067505770684521209436169838390705513344865384140441996828911506822132071414925409408028130637631320031048854647327652038853841213412686318991744226884295456904299892342457948750792019916788977941916623229379610287222408835093233664 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -455 -12835319081690733822122917887 -127603331931914074262646774143133941767 -57894298449951398757252466847559442906950400042978305056517893187912714944511 -82474589191995733226059000413673369947276401483368126437014470136553367666687 -1725367873614246661397681099761093360336884063153660970896243052915304594387 -762145642166990121634319750876330910924084988636669303246807590962648009181396757661856053558136824594202566053704052044355748404436481867775 -0 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -0 -0 -475 -269541700715505410264581275627 -297693402123626315271960004983435296922 -57859375075817421675264509035607066940711169672592018945053659095526761562091 -110877123712432405983959456058661230794625399151749997930016980310850197782507 -36232725345899179889351303094982960567074565326226880388821104111221396482127 -16005058485506792554320714768402949129405784761370055368182959410215608192809331910898977124720873316478253887127785092931470716493166119223275 -0 -11731831938715777520612778668353660668457099020791414454225295929553974535670831510938460179265466667423020702522654604586832988760873000236316670377852914 -25139639868658374620186803666221046139915857558720373440362266333582541989451932860803919374764761513214467321863672910499344396369729736178810105052004331 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -0 -5 -0 -245614345371770711601028529268 -167820641058901852935904395164427229736 -56310523432975538089098580075519192838001302745960229901614877098209729051764 -85237339161927006470244447187006939882425784659579906112900841728046555724916 -177131608341019431639531082486186163608416895761801191709521384618872970096746 -692028243087627030443962333795708467119069169682095727348101292594084392336708255956965296630788236731535929976763279256275019551228325535939700 -0 -6703903965663326792874639644623508444541867482964682679434051891075530751941922751907842696366672689743371632139729220006073257144919838847329290738990536 -6703903965663326792874639644623508444541867482961636626584690077349172630440349667689597258763613066465466237204690258874126738857263918193693789796695156 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -0 -1 -160 -0 -255379205689110469036436140398487013344 -21712727978259726293356856503923709634858594384551721609867559528598392012800 -78804868437368052447826264773726337899091993565064061055519163000168722202624 -126692487956834088798699213663796104822239968371597515995972949581479497637654 -3272191856449843397936528109279065189423120989381483148252736570244975246791533222321103893532128937121700575680421527659060354684671413348913870012416 -0 -13407806431527680864118905249453282443943463537493942574619644941644874674867613388388672408903771364000715917576061318747309897108671036700271413040513020 -13407806431527680864114646428811044898323062754972248613389335474203674829637362806742733003747804478536315412888860690712090962708053524257498831612018688 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -0 -3 -373 -663267250817584834358806577153 -0 -56110155743260739980845965626188839982990513354097487512099126177554948423681 -66666258429912423795302025160998297406283412204636264833390604129473579188225 -177994438507773838104674369561918072421924948198026690191960823057415806012035 -3273390607133996772975242796981079624647701753259790782144449701319437329708326713265031806779102708651838281996474323697106010993858402738833536843777 -0 -8386426737429153324968205650664410607248107158089641410510843662012750503977158430826944378515156355394129659501044340493857762955532974869923505951997952 -21794234667370607206078979989803643224514176633634282995495125018424131531561390135283100088614877484687692308031922838398470089333320084110908709465489409 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -0 -13 -120 -564218626380911088370628689920 -233942201656969550061072941959067205379 -0 -25031101341889169304446127873801694869672305027436646693413954045557168144384 -165064675467119978329806304469495034196158211217327340109988910890598366587009 -3223038340265066352421850814896502694405790913890534440785162024059085821303576802379715024281065670685423211275932766116756343825199840534435352018944 -0 -13407757579211451638802076068759612924846096912682157704482048789996310624570651395256602779668977880231523760330028517458758478729840969627215870130389003 -26812343265274399502606909495718245988987084717537620990700517306408588619638088444509530024658803496505053000329815974901893831224981448235838474180100096 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -0 -1 -296 -937851138887707228635723202560 -71983759896126428559029594843447948425 -8631296002177605477020671384515986058586860067875426006234029207453268180992 -0 -126524937455879451586139547446289490007618298090847498853547995262066917814910 -461909328852694177172628852102963427766642499620477636423836056046062222974423229303007292519570049771036188762256954253356039472445656007369836986368 -0 -12981654982074150949064673360867568394481876121003306153656885506667214694877409921526304923306457555280612555616595184427145127254267331837419389850746631 -26382916729981577107547759771891963394403114106409408238867427511943648425024917427272522438481202960509647220636524403278529999467034622910615416018567168 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -0 -4 -317 -998761803804737242509890027521 -132039001590898075997904159680621457685 -2677345058813886421031534284112569580584095914017301741535579811855705571329 -60086096674039095602687907949217152706690659894880673128709412004817402855425 -0 -2714004844124354489262183477493191831493012005747958882863179807930524116686711719016251086947965968979909339785418727709907819948220584644496513302529 -0 -8405533274884546926710394653557214629954286686035689894640417053261039449975179806395580875447085555592435119970176210181270653403403863429651960501895178 -21800298814094737305815960841066827715322148914667052956623818275284251694892649734484894880644976907985921519360277878586310362568695504456731719639236609 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -0 -10 -348 -523874002024118652564917977088 -338901215317800664056147458209075441279 -20015404885278470017469976725210570559655906804843243074581372998796865175552 -43274941068732512819275538703072682628533785069604029470934065279649011728384 -151466924635469551055256360824023267312984281713620934309319977921329196239204 -0 -0 -13404087018778832492634401257245418742650562079557921670746800976248311228584588308262299650079813922944058763900421125187070437820594035236042565924945791 -26698406546920616841305199515713884093627804673942214629126931437741327626828248539905171617610372257154484914410867070819728868223045327378733339376615424 -2496627633295536710441065870382453446241561054214863710921665326529331643172374721617027428678254874230352879588117492841911257615156115956910959191976693536620732271898886706126399033775592559421285971973156294900372189579993713535075264782220371887837493529329161088095671325291250112790528 -16458372206383560850154727152772241309834362634645429482097685030171884111143709030259566480970840675586286057165552281949963074948236086284290260005656649082848245798566424861913543183906541851941660459520122404258933779435071795354694349641611033685309336221143617936715894403545863381859480370679906304 -0 -1 -1 -1241060280638922538398643126273 -223107749314796826962584252670127175984 -37426377577435691661688826510960246067884784188204265749528164919729924866049 -9094069612766502477615976059819999205719984916420114506692488266649035603969 -3380611435722827550596800218965089112417919202169043851920848313612545048694 -1664815596802569074693189778674696904124995874607176724972727327974860890963436416434523283587337601895433614890072030760067154716436803630431767363585 -0 -5026379062749122967384141319640742832082429726007228978085493989865528045961662210976446288998254740248728104766675876130693513073515674327757412823465985 -18631546706693366379985337373654708162934588015371887548718390108226073979961939305072220762985923906599359456912063191643353388948502368005581210117996545 -7666833439049186440719686713438138111752796508797184433364211618118049443528439839182907766570695724955741264553761512695357483049605308096343230024550531616749580571202626675398897041388144580063394461398121571858194591602410925543646216736039567657824018701113172610876571443952867447178002433 -25280059709008981479231968148711644861442111696433705443231567360117402528393306066518309787037990431336656280044426224316649696164180810324022564882675140987960579876942528723700768267175237032707899300231700312150169638715161500573218061631068318350665689512114702779435677385026007774824467356638267834369 -0 -20 -141 -486322689729062157054589796353 -5233832698719648034352645296049093138 -27877848636933271899195252835832934260926934447655424254310367380191336988673 -95046404405893116989598089438620745282296913815862276271716035282978715205633 -10841566652186130506277177706281856862358509613184422194022722527294938334508 -156434328106533251561339638441017247952143366970355853257183610685433013344346228490597502025103483349050029645755110902306747038242560335594425155585 -0 -0 -9662133425425935480096041513586603904206372855623860335152003830393861922520465132980695412533788401921809758413865008548781612626086595943461642943070209 -10226186785978516664210805392326593786462345638665902695628517775117882756746916394520605503809745503417957883518194096545736577938472828035527628923176465229393070640145365178809018733067849187814952270541749630550256615957994079654887183103510976798785535680840409779747058642514043499117019137 -67413492557347065242233762416053344604668789415978336070232719704519730695842285928289497175591340668357371303688672579355774019833840277487199698360245654983419254719431470785652320406511710713843764264258846732773932462012692405900266395961054012873582577495350462230973011642886763600990552681342023237633 -0 -3 -780 -740317960503384010933160378368 -316507402868367164525185572856543168929 -40944587918560512242849087391658885933299065666179689123736656251983650357248 -53237017879751543050172096634393803827800675748632110441734097441394601754624 -132470015928546996419857333379835247542469691948112962947775617810494227864559 -12381248203704069044821113079836417493113102715524505752538680624995272774705373241736873135446865373915002606486008774642271568665461550394720124928 -0 -7489725100210230830056017797355917611388687018068692527647069379107719450592925731141813143964045960148998275654062617196447019140695139546528776055701583 -0 -20452373571957033328421610784653187573014781479277475450110247980520397649467016471081241044022090207387138993173997495512406464502494422419429757329144085539684125779893891001040429996115472381316500865902248751448570530371710095556107777628203078382948159704011898603287476828183374436962926592 -134826985114694130484467524832106689209337578831956762230667385079098314604114856488714967534864721366751145206577896381937685648970101488283024945486839684466321300593943839555804243973666843820217508302523380061872683673515732869098988513858354359158586336115486301550288354364817320561524260650122775363584 -0 -11 -372 -144551572086744903187712442368 -253202426808241135906467029640487192367 -29680470818121163968957353650459808483467973521537669013305160691127391617024 -63848746629419255316830265708104138899454095951935912495751338301473488371712 -209179562285481528286524049750565802502654182439978651496342336317521325724342 -829324642730741957628232762455280255995410668556934354798600131375360370313925839584716822046061062812974125513273022383261764817641491685689738133504 -0 -10836405946081245422742336274313850195167725364810465218531215713913733992452962410563926301612533486724610590860754949426288686136633692982939049606026404 -15887885497553844866770022409478037710567337044667622956829665017046764846823120909657790652833375698308583354385844777461147565338510949008545557338128384 -0 -8160474863985113685413960163730584223719357599028623408776979120151702813402137032877800155396033891808446883844651711571976485904937817619793465524297057901468757849014724833218607197896257643791218077720889294851053074003636817287341102356113265918596060624586091608267779758392456426930790953644841698561311367516692847963980681340876468289861231297504400770481454056706129830004757201411809017347700323276069106007745871452840572171060736 -0 -0 -757 -684792255765091403806850154497 -187838498585952187882913144128949314842 -17880804947835988471289056186186828981571763627604504410978695443865690701825 -73844297609558392021902368753050990125982306435322133697955932525026812624897 -94126279577000071487888075124472770168590198066457285629539552899453239919702 -397557122057289471646406389446737856415741637101452044100444345939109374880449993641098520778717953204989669069789407806036305996388182260507211726849 -0 -2707983572801101648847089953010363517264777469603935668937876318086482354961489015923724138040887781936287836173917247739006658581124406484336614660459035 -7693080372738418372562538145926343985101781270464320587350012492765027537754950396945540360958485453885260344064936191173890891093385965677925002802888705 -778559862303730584584607440255252490793391466369614533225223492586012662394527783524384957495858401288387266400498759442518012862732395452853782993747936084285564333054556373460085077607372150007252189634712684068883939412024299532578569222291919966589108225297068060127971290200979371023459277602817 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -20 -595 -182776107722364003135434784767 -281466386776064 -57894277771803943810359491595023458960167522338927730828258057680034478424063 -89555305827206481420887328961785203324625996138257453019240897699860153630719 -31072229483910585358509303280765563029411398133731077240605874038996845471709 -3273390607896141870013189358366943131609359382662427080975450085374873026606564249289725539410814263596351691407518053929870103702401568479817676881919 -3351951982484124983609172268969732236347481510662869536744042230485685559782341996467619926017805728755414692283401971053259449792684445385138897718083585 -0 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -0 -0 -691 -35346468568505532881936842731 -5910794122297344 -57858940834720867790512028732351404058270737887529960151597113430083794632683 -27987994577646976394885848050655511990655596618857190835730974489325485424619 -189349345528550113558840916863781287758481416281111632097967585676192554370339 -3273390607896141870013182589153822719463706115049672910839483679347322768167747021268146513478152778945973706436024623603772905158629315134471253524459 -5027927973686555902021074910529630935073811046690329450391073974713817483441477451426676218078007733400564805010857217050528619214139991213665936757227554 -0 -13407807929942597099574024997867385471458758537928833560431408446992776237013389516985884383362698273068238750137810819277666550973482668621461384635351019 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -0 -16 -0 -1166128089190511751946393615476 -255571479192666112 -56291747675086446310277050772845769159148347468038830641684235944770968484980 -30171046655374400321248558358633494123141603303165813785349498983526118194292 -191952216559694677850253030981251311226493861529738242207409594273293055910997 -3273390607896141870012882374551932440803983696424025465309373572025468806406202958511116713364615934701710072950290988641362149742329879268357377621108 -1675975989397670037760469732976766982351910933877220375401361925712068316784901484293350661165516699901005329555649261275619258565077645558288068779181545 -0 -26815615859885194199148049995734770942917517075849908645075912980200181493966889942992355387418779976258047876705787108142786970073112315813368816870620276 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -0 -20 -768 -0 -339617773210407217629729401159633862623 -7010406519791895781934865720833530372990739797465297891865306283193669779456 -12209728235921518710323559633526468837955820087843857124387593282079453872128 -169951871966800283810649419399139038928050251176624833301343281456431633787453 -799167622926631891612934308861836795138629576941338685100903095647779127140015260950429312778514706312501025604542433181196096945393694423654596608 -3723667608053767842867675023364612649103089456109822250875653249116275769787555601877076944892223098697379930243317445603459059303034099481845912857059652 -0 -26815615859885194199136693140688805876096363840529017362328825455840484156274161514345456537746861803711227865957219195669584452460326767114515755500044288 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -0 -18 -837 -1055049663028717702603910152193 -0 -56145099392931667494602725117889809978473099780543110806328723994744381767681 -94020940359982983746102974658635634485631399798433967904585529336909218709505 -122813165867929679368114907950462646511665206961139246511547700775532269629820 -1453677448929674437119254898402693728557812421065819075020451306758949997680185293878407872152173525219742439043086557977078210366537729 -1212366891818018736612622703605109128833655236089151046090687816762735515567219375580031566019793843481784966322613342296478137028286856502402053516577749 -0 -13407807929939548517005357036703926880414038648958573042868430206064246508364035546678488116044098549923745343892353753273525546559935574983116802679111681 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -0 -8 -576 -465758099028613903974183993344 -340199292706503106677777063217100488703 -0 -63322527565292422495135184712771504401404079317527680938906069764207697461248 -33634219328043204849947836966835886827823990925961402040502023813129318293738 -3273384364399040874614920955958929289600820575248783786959254747067900097257458270481819637251484144665389340236271253096323308912816041648439255105536 -1330293784850007390428494387229269593003967094877098901329361383107711576399124678199758357643942725207512746641251003704936924381303175630906145151786085 -0 -26802522297453609678208395193923639182831298274557295628307616531706906562408033942581487447261461746690211924285806443346587426535386688183750358252650496 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -0 -20 -876 -178816467053815670676679294976 -260528687490744071814225196668552224255 -1692970775754977972396027533715957606705876841991018145249546038233761054720 -0 -181828467860701888405812140333208298279745113568339986475984061945003903302670 -1636682816191726232111652774583460805243935699557095064129709690597186770076614594127682973063911518410637341738923286963278231026430050646302719475712 -3045552175859305524398839349214018181219011839758787491368043134180477082575118427510368420050628050508519285324861985507713808320174592548749510991674891 -0 -26789430333357330553113882063869622691267025750963372291024681554553700868085895351207370848391420617790680534043234808601716445354096156366798348609388544 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -0 -17 -205 -314141171791122356976004104193 -340199292706503125566666675774975279115 -28964696041878609460721583118083346598086669607678005131861204748455792082945 -99957388910786315412288528953468780503360427998738617462677827481821872390145 -0 -3273365633955366159313212088482024572601269794428675433404928819417293489209545301349145972651249428525225361135543177673426309845521311956629494693889 -2190371668160050365756300375697808168184891704732002727464497057741609895764023359151357865247649911895528229907788684972491076160970752924555757567389999 -0 -13355433780100006387064008929644857372863390914834597336542712946266197713041680465273314799460842675250982197484946421191657518371278691462674567665811457 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -0 -11 -36 -59097216112512413701662310400 -340282365019520591560372007455816679423 -29895497284693382961837918038726436373289999003535570507137440248052571963392 -539397372001143345279173783832913095317038465503343488764756916815116894208 -43994058047115475121282498486196678252570359157229388277889413858394010898203 -0 -3274891444621295245553525375878222425626418688341503128076239321506909158791609365713742593237016516146106629422990668626267243448311183620042075794130081 -0 -608850787303864097127237682881155896480025728423916605455510274914880477289308210564971661455576995032557297687074419380448068658475227845925355707695104 -4872657005698891626244130971294875853353482202322888855972256338282628395923949326743092700819820592141121207789967023511190748555596067213433893308801964320495837037320565222597343423626118652097789780615868838966704829900803241659660193801529518775840320495759898504684544115156495368192 -43888992550349509466047490008389760228034918444740354476264789433451422494435484145605446873947256471512988292597450618642207676514741311261882165968599848430310026415377131508214755446432997138431116604680521746555008944242629428478468183278110882114932777127534190355484669739785885245968619478007676928 -0 -20 -141 -486322689729062157054589796353 -5233832698719648034352645296049093138 -27877848636933271899195252835832934260926934447655424254310367380191336988673 -95046404405893116989598089438620745282296913815862276271716035282978715205633 -10841566652186130506277177706281856862358509613184422194022722527294938334508 -156434328106533251561339638441017247952143366970355853257183610685433013344346228490597502025103483349050029645755110902306747038242560335594425155585 -0 -0 -9662133425425935480096041513586603904206372855623860335152003830393861922520465132980695412533788401921809758413865008548781612626086595943461642943070209 -10226186785978516664210805392326593786462345638665902695628517775117882756746916394520605503809745503417957883518194096545736577938472828035527628923176465229393070640145365178809018733067849187814952270541749630550256615957994079654887183103510976798785535680840409779747058642514043499117019137 -67413492557347065242233762416053344604668789415978336070232719704519730695842285928289497175591340668357371303688672579355774019833840277487199698360245654983419254719431470785652320406511710713843764264258846732773932462012692405900266395961054012873582577495350462230973011642886763600990552681342023237633 -0 -1 -813 -45071397778386282833495719937 -79223326884772855371683332096 -30064000136008847467610977278449545131856789547954749300842716042281091073 -47481800796242493701011224877673450824539887746013036166395443280909340508161 -54157550229285718465004690895357292888716023226777486213199274714961136165506 -818341408476952279648221140163715789549897935288622536894945939243943778448242598308927351092346187094151421640908013117684957061336895645328259153921 -2482049033302423024636121117027511919147101564783075942011293794333730722477081753627970658004775337881648218209979066720442151086313712570440590284651662 -0 -51146728248376854009350675334529289286007270165566930302861632246924015978522782117829380865101254993439196691732003758250424073760232713310443667457 -19958403095338122085628193786479237329997442708307584904483704091804803849695408757200216428509421792133342328792453788297992349608708721951932439381822315292254947801152823242445226929207389231565784328163754082126708473023000571426917955750604503480854336945972830926696168848653425651482625 -179769313486231590772930519069826442426264354005082326596360185112449176958549873259557850386600277367825925997915804457268344327753240407083712876794615624877762257166854166161646934596377646965522119647578649647126703396773127858133295428017635762092566594653974618708449402594449637475763618354340068065281 -0 -18 -112 -514367322948399470595574595584 -316356952598867061911922717515644469373 -7293516171214693095994040481591203674140336388851356962934636744469142044672 -32168719419553521126381348517134497442891825101604991549835256987305375694848 -17971033741324285370848719732885538533017442019292722212631856707833117719013 -843908553492555440461456712712792961310926726449234669737815334741161663581434956136120425892313305072448311249427561305380916961526632833575236403200 -1987159726477906926480106742200993388672969577973820652993709385070847538717450919815518154185973289833754703858163650459518129052975801260401038083579075 -0 -0 -39916806190676244171256387572958474900235423938365577168044454433841303463317458684539623736033909906352541410387606389752863508317732324267453159028957650595214293380229123627941993873878917982253053083577235260430657717424267222556465147677697709664344844735910306680957072990830612317208576 -359538626972463181545861038139652884852528708010164893433258891975305712994145996750811464699841724874842731010897931000393441458205293971046234853904111613343804779646728343027691647116232436982584254759296818415737834043273351893507362234301351226814369365796651940053069649153544102516262530232441150373888 -0 -10 -696 -63686986775670542563280945152 -340282366287113163957929847168792788479 -20821682061037147931036251650445879572012829156925512449007428546569783214080 -25601876446080871661169789388095460981655785026634060440120544542909439934464 -46283051732920571920660553002376350134344620740408867024246119461520606057344 -866122526761241437267861069508391876570186472078175495527865085403608259792507543050734579496604017172579797485513755153244749481961395145194250174464 -751887818320028821005993828455893384336758898230218749149055282685783130973133321426891964359199525788718707578879513764058378266511916633326134896279766 -0 -3604618761161597206342350970913574830294233311288308625456005962609440682103556011303416761181780744251205047679242944742588136174508865995314329397755904 -0 -35074662110418088637949626617730057226421936600416036348376934414485375457507498005829987619930255909681071425174751723949400452853556137093441769597080466171349184338243707744904582316746564809238360330702316984594788071319980528356364662139411447217586668974101390062027509809569485429840519136697767119848670374358890937563379457324918923934982963321305895999319544761333153673280222818219235010124705215046496087159771444404027458888031487607705599 -0 -0 -501 -566596565891779443770567688193 -297747721340733346357341714763372042239 -51359594683295705627160736875920391224771401002889258517037659597857844363265 -102373118558924839957330237483174298027903126318578007347934548864993903771649 -36691334835498136477362251073079490846730566134092461259856543443113747864664 -3255972356450126473367269507262012034771303233154406275900353518426269324152998622154947912079887019154967753855758652434103601805140721100074954784769 -1871705776278248879396484688509131256000830153477129182065775246470064636917070070521484000777436210886966790871882875760627093515239202937263621093957970 -0 -14537918423071330571316124318525036093070741242523364509933761893317940689672532870214776228434171755763504198262702582936752520446579624047220655990505473 -2231145649723506021153228184702309227849416613571515961923155876896496547807580652010734720048413104124530251069253785706943018637185107546734040714409749571116423316614742963894337402654119736587425733433625423343298072444077468715756898217058657745332650905736779877862268124403573045045696512655361 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -3 -780 -899245128272588436190215012352 -79382824738944801807794176002 -57892510978869681736730162220291002611042557329170362915611609916721313349632 -63318549916452990938470040291368379201286383559458191806845672058759835287552 -174649700752953765434988902296252491183916410461948522823307150782111144039507 -3273390607896141870013189019906287111002076719299707330406074198757955051929170942113619750485120149822099821188584330248544763114365430245700361256960 -1675975991239013909235918173490637855139324507692685389812651662983087942796182042459207730713681429512584287523480954202975430917613169254383951955361797 -17917957937422433684459538244547554224973162370938960458267029029640947834979519339152474433150005542914 -0 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -0 -0 -36 -1137039323585710672837611618304 -1667039319517840837963677696042 -57821838183101364244296111862969820726646472682625233986021710400507328069632 -55976566637351530465294054767787754721301958177994368678033697653636844224512 -193887663560237731860858557979085795918560035746611554628781897853775020813022 -3273390607896141870013175481480046286710770184432558148882590060392045299942487570569924946038576389686684431838416426295940752809870412218007625400320 -5027927973643875746059723456610435252591983579747963576265463600113040980879343903461819778081504218012049853558984265154213360147307310400101030144508015 -376277116685871107373650303135498638724436409789718169623607609622459904534569906122201963096150116401194 -0 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -0 -15 -0 -147591445132561651497663725568 -72079604862961880041477111809816 -54687499690776483454845978515775404153680118608348765958709666856417080901632 -60367062534952635374606720010641822146836281167009160997706035914060475662336 -178652301000333807279700689952188731164083671132840159036419863339428204714256 -3273390607896141870012575052276265729391325363074491948316068523863947799333080042607060368834360627681011914158469885997952895805516362689834790158336 -3351951978795340075520939465953533964703821867770710256609903421209625894295852147822976953763845975898117121470212503185399920690105737901876342591329002 -16269505807179569785489260726049179236275631432812576096106462358913980634161403559950446785300205032965912 -0 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -0 -3 -404 -0 -338702491610940821868309420475587008199 -14328023572559317685526112591674235629243509140269133282451074114125069025280 -86578464609153953273064023043464932375781820914327168919289930680154219085824 -45279483869291221953284779682805788559295893933395790607583424358602969434844 -1598335245853286496935960547857315896747917205953978724820311075231669760316560595231635274478219159108858101014665432801177715434970395175083311104 -2419407242378299627775637392372328814292846869244624971419290896315467839379870742253970126076794151625802980270332067936106109204494869156371160001888905 -22713710091930133642306470658052071301354618504883936111506036530073330776648921189746483856049805580566438785521544183006327774117888 -0 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -0 -9 -380 -521589998193582783072001064960 -0 -54281131142068578829492120775689398115089911863432918901729887943966322589696 -87063228931437267679054882381527280567353791974986020151597093229234539724800 -26411440510656131983229190397498319604014001314162142030505989681964889965410 -6097168044690820186266056327949574746284769358267195096071257642746052771416966407359752215426164404883642352301456819389505193941734761955328 -2424733783642134638362581329537135439756750250119199404424017986489904138736282190413580103086576851422180352745763276505772852789790102244589421062035368 -6097165137335922326917182089439777940897312242642352964433107601843439253516971046989164458610420100536591912816578733216389239785314028879870 -0 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -0 -18 -76 -684365645831470144534108700672 -340282230054365050593775836264159842304 -0 -15963897412190306650237099369975576841343765721507556435820338296445483352064 -132759447225495983324090510251744519265863982169783448911677742254978994593249 -3273378120901846843848851698962475229131019779252045777777029295839115754571447880452966684129870618993409677065064471065432673898932044455452686155776 -2509556964148715486845629632058434150976495148022092181360043667351572633930311684969654834869500136098220017267480404777629280818717809976634762654456925 -26187124863169041879310296789687050376151294751239194912085646467180246245385039287091804854399398837876868573183873563217432331431922121057537489895424 -0 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -0 -3 -248 -212502350661610110150999277568 -131552641820690640981991613732830315020 -46852064353518235919274685523532198947963594743216103431489805826506451058688 -0 -7645097472420881927533172642225448567010395265033633905391291186112176472435 -3270168961867742817620220960232150749713460118789595424509833186804266725716461911398439168187233942133765790497556243236366961654908249264223007277056 -1319225067735212639136382308035057590059309856444213959056608122715470491794696873946870339629603616630101618120768022446070919013133368825305188791863556 -52371053055727292068337250065143561186684042431774613097615715910894227837903200691375492620062343989956747038583170958041377999304092089877801155428864 -0 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -0 -12 -284 -469455506976059516207830663168 -7983013255172082095516649800004406650 -3278348336177517342236710812605326558782498594141487069958521276783524839424 -49934227818811015946301187530148750948812049358364108430512532097940534067200 -0 -3273340660038035347812612675467311420171630095312401315302288177974137674243108506349429817414905569256600781798349855567184974503808521851120146972672 -982327148533126242072022252525591418809619347163444615270839351763368878975643860701971724697659683917532263718739457150238974028247429927345334167713733 -104748299685181425020034909109462515817741155084762579290698462017633201711683603583490202643438294700869447910072736485865669638419855454497265191747582 -0 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -0 -9 -856 -104786732573771294575085748224 -202208808855666172013483050871179179009 -18208247384094454656528310293180405432371044123060662434648593997496387108864 -105494033770685441277383922803075785930338167649609913078213878653210490568704 -70973503770577650219006804111060955590039707453059535237966473815460451376894 -0 -844360569033703606248598340834091269063135277987118651898622507491431258386488521835163613652641657086294536373967783421434176997645745172742040950152481 -11325931297285258457955557624614589968683841815173942490148987555849011164310979692749741711850895657264350824049096301947145237348941454577709986315503615 -0 -9745314011397783252488261942589751765359439629682498258594182172731326020846015217955153176479771037023708782206427041980054199788372006158023795063990197799705875528943477976666060127441354860040345913426334531070250301774693553668261371384134782207849454728022523237602477129993155510272 -87777985100699018932094980016779520456069836889480767605004803903623391638540464457280122746011077411993751425324753978750781979522477580196467009117071428016628499217023421730630965195213525748235513398478599337876370083082111993797578339643292113170849335330813036879783075982297998725326138636180127744 -0 -3 -780 -740317960503384010933160378368 -316507402868367164525185572856543168929 -40944587918560512242849087391658885933299065666179689123736656251983650357248 -53237017879751543050172096634393803827800675748632110441734097441394601754624 -132470015928546996419857333379835247542469691948112962947775617810494227864559 -12381248203704069044821113079836417493113102715524505752538680624995272774705373241736873135446865373915002606486008774642271568665461550394720124928 -0 -7489725100210230830056017797355917611388687018068692527647069379107719450592925731141813143964045960148998275654062617196447019140695139546528776055701583 -0 -20452373571957033328421610784653187573014781479277475450110247980520397649467016471081241044022090207387138993173997495512406464502494422419429757329144085539684125779893891001040429996115472381316500865902248751448570530371710095556107777628203078382948159704011898603287476828183374436962926592 -134826985114694130484467524832106689209337578831956762230667385079098314604114856488714967534864721366751145206577896381937685648970101488283024945486839684466321300593943839555804243973666843820217508302523380061872683673515732869098988513858354359158586336115486301550288354364817320561524260650122775363584 -0 -18 -112 -514367322948399470595574595584 -316356952598867061911922717515644469373 -7293516171214693095994040481591203674140336388851356962934636744469142044672 -32168719419553521126381348517134497442891825101604991549835256987305375694848 -17971033741324285370848719732885538533017442019292722212631856707833117719013 -843908553492555440461456712712792961310926726449234669737815334741161663581434956136120425892313305072448311249427561305380916961526632833575236403200 -1987159726477906926480106742200993388672969577973820652993709385070847538717450919815518154185973289833754703858163650459518129052975801260401038083579075 -0 -0 -39916806190676244171256387572958474900235423938365577168044454433841303463317458684539623736033909906352541410387606389752863508317732324267453159028957650595214293380229123627941993873878917982253053083577235260430657717424267222556465147677697709664344844735910306680957072990830612317208576 -359538626972463181545861038139652884852528708010164893433258891975305712994145996750811464699841724874842731010897931000393441458205293971046234853904111613343804779646728343027691647116232436982584254759296818415737834043273351893507362234301351226814369365796651940053069649153544102516262530232441150373888 -0 -9 -40 -888846870654399541026800795648 -143482146854237819476935345763042525404 -57081193366483196042719382409159372256144104676078398008505091648468224573440 -114395766977069725950433431832947109769631375895653248371844490916724740194304 -76683728808117611343679768926517760481973038840157260737743824104456148997924 -204562036559167079530065527319219679632890724123975675460457785758037047119878176949411406189670571930446333377724771404241790991901801347760290529280 -802137782523454342979007031051565319673311845595383846648820509821467014315763391764416287066988487192983357684422811427862593841641642759639983545182786 -204586912993507416037413532079109816519356519304585293253957198114703293337099790607326333051861102317300881004677310304033452329931030113527401545220 -0 -79833612381352488342512775145916950280951924920231969054243001368145998454488199709357950283314599696470744140704516663672606603760614212434767697879481924503085776089335544299108415792005484658584236033962830081560638467608223040601306884583644159167581710543549233353963456867137670558515200 -719077253944926363091722076279305769705057416020330267347594827451426144142384493965014457252965790028388273268575745766448202845714472108972056832957787126548989380860079998712572623109761917189596553766242330909605534894906264486337757228291297942005327959842043718998160370035708197081835945941328224845824 -0 -12 -508 -682119343286724555858841174016 -180785746917974735173281714194813183472 -5580224303111442529144190824250128399011214808680123220308726439587289759744 -17515932956706260988612620612193590854953640142730013998213746538449020649472 -9907659893672398453699179009332720595687058981623531639980926043720794275887 -3184888035370039058932907606064617684917571849344562184675716803601698363355196380950800709154896679240574015331206027838214015803667188966671205269504 -1939277412313910453923860576024263353915990343780420167100598244605807128360981219313745050560191239583084621187461028223430162008675508756516321033825953 -7544913367357988624324327418946969851430820484554794210102692898204924870041052270614682468020351324034119781305690108157765024705315031670841334136761032 -0 -0 -35074662110402138528271665275179256459319857320693236334209567133960895549163137483507987803016808302684056470332523040304974402229876505917218722980389688882912590974423582656638710685279885141901775655476694920569196059922042520886245076198361066013800570532010333824987228784598797064323901995346025826169147094870809135589836258762705579827219567252284956034887883311295478937617923515174929544803288635466681019026754787765599275517274090540900351 -0 -0 -260 -1150353400223286785695195070464 -232614738005118298378871055549980366307 -45914987571294562618885391000426099066127147173061261158053815643543682678784 -9569712348825214221479561079764339151873872589857025114776692020553231368192 -211318984418980660719515851833230980412973699079543743262439772344023095592422 -234544158960971249369204990932617363388429151281731167641912793995080736229449905117680762908260619808068871667768771987742765263413118331700270596096 -3261048926869246197614171688872921244377276606862369969053710013018569059589860033341776493183825115231316522373635165226613047961168692039060337222141976 -6177808343254419424305538794190808827588752291948002891198342429196741356936483631524785345634116296590873366080588084683919843713525117596040803781187142 -0 -1244991193137117952846157937025143570362432662590599290360149373303239443080491255301509550176958928885332124568019988033187164346281378990556622407311466824177344146655391099723319760271743904382662218341941576791410880342566702310623956919116180818445686624258025155723557685769130435612651017994240 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -11 -372 -21559839277448701880456708096 -5316932265549267143283400319839567616 -16282337714987120045892153486177165723161016453744476162789802658371874062336 -88537159989150876890408569427587371627685568624275408353614000822051296772096 -117153649598454680903779921606971322936867216714956963589387307178373077018966 -2451846285358576582757929577719947826359368522202829009982370031469538161149259226929244677345525601309596834316982200231651181236759995889790497062912 -3757219424385931343692363754866841943466352992489416825487678472853020775555494604110835156534238535884545156565469882173219151445924025758587329876446051 -1637494313049913248505508525988843145443777840250318553676797222770022673451141317105487157651955800518089884835975451904017985829916792949984404185120 -10058766516776922387083424546997939506014390161211981301999104219673036875504700741486695698194962976600388399803269774949866748354955056527082238634885120 -0 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -0 -0 -548 -452756624826422739489590870016 -111655577576534610008951406716630919936 -52448868921439032906975899518935171660069537721145902608130331363149292240896 -6606931978479281254831897832501046354906618825234252857566140055339491393536 -144389273399687404130106093584920102378421828377891285603354605032128614720536 -2387912874087980187718675679704917070297108275613437367396044575716543291206867105152070863644327560068474660815235323172050351528107196479325467574272 -3482688306166017347803243385471281549458835137781679310740694623583921742064189215873681869954494749194897826789578092866925457557981431075429025110142766 -34387380574048178218615679045765706054319334645256689627212741678170476142473967659215230310691071810879887581555484489984377702428252651949672487887520 -23524785833119010734715565516813333025879573854445303556124669257483291515289313842947157240147918658465448640671956584728128012482443412626594575622864896 -0 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753536 -0 -13 -0 -561575095917728642643722764288 -63821360225596077614082986369572434958 -20871267450490110775349857218815741788300741818091418479909066205998432124928 -32031339597730053368408931380419192214478581749743918680291072536952960974848 -78578636492035187050572077718135746998684508179115744547225899703648410312403 -370813736211065535231062726945202826990026850884684216063072714490283444143902782007113348673056265481923913676784506940297294856753087911972659265536 -2620071154003612760581214322788037964725604937167931635387043904448806788414974868124012072732534985166121320391570652048401642048690823355593944068875722 -1486844836249321229643001741597869576062950278947289246738531878275180587493636315931782339147975866870425615431065710328848331133564447998585839000088960 -16050604872479499761412490124306950869110460588744266902155691331522600686097332182352004895063052889078290340385962178123999076249427996544240088860590080 -0 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248128 -0 -11 -500 -0 -303146897509458239699003654989925023487 -32736756639827299933225685485072629321729229567663511068893875767449657278464 -108940097746668219126218354657538950829059055375326097462906774176671100567552 -105141957406996918481926167015587865122928475911973508855892157417175502573997 -2455149808749324871737694767528984736302289775261104798859058407114388639278815192018708121934602436285743845105491713074908633063448335548472405000192 -4562499427405661443491300822283693488291153790729079292153765540644759467686485522209256976888988737125973966009916644323573026335699548740771640065918781 -11974395290736899579322825765453901754951747606481982721750002227664784996135932884515664533409327198618661731940390586307041084433465121132950282273424559 -10582878391618641992149234773030997029303083829182787525505648743266561935310197607787383974646115897854100240644934138582092413692188764048451304742191104 -0 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139072 -0 -12 -740 -1105766944361955339797897674752 -0 -40666663073457098908560285583894926858884570977736712496628899075489882177536 -6937574406821791937994980945260712046755937992433195708087316793731338731520 -80322259934434364526645140508220590735304755095944269027477694810351667453580 -823941825448211091053403141520018121212799809786471885049437482634825107662178709702891406359238800860434148555858011304517982766915436234049188790272 -1429667404699267204948871302671076616887599344321904849215552824418556681505872488697278049467681761406353434390315540634737585531078670948788251814191550 -11625439539104520658533890711797855194868386980241970994181859680105840264577003778987586821796875152410687590090669235843895015251319782115307313685324767 -4012611011364738290027830363884880786616340613311963081947244935301921679145302776974417725708110952074146887700764495196805422393345600132552727271047168 -0 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569280 -0 -17 -148 -450240345242768217289160392704 -3323100413057427899902215874225930112 -0 -32128284792822784994903339199193643879812144651157443822976047334286323351552 -21399706425311438129470274645609834512526586881469866045657503407040080328762 -159433945212719143010790645441202787170674053048920862244647201067119362630573280745854349563469472787838188810917861838889021573237764523468984942592 -2613373875808771403998672140838135919995120994584909205876832476647953072519054679140019492621878245598201737849936937907935929346666864089763133225143876 -12559952028652149332649304876493520745078520101115730204663594383913123396138444109637941720895076538473855709838631312376520291329776343398099754614544377 -8017856871157406767405434525273062325252187193195991171406074702436922609351041617384360871147372313079815808387436833569595297983737028164810164383252480 -0 -620361101309323186180458062143306311203234091808422651940994483172973212453462540277947848500764537416293185308049243121091077089522237714087216426627812178598932718744797175293795424927554129248204812001597295596246882084347634304130710520242400817201806901439842621734294451519310289297352148130231477204202016235965703658712648032709044435339313088432114418262153009154752512 -0 -11 -244 -487467278610825971461915672576 -37219007724616326815534795495032483070 -54261526529889546426837093235106983177444977555403308095742429498729088155648 -0 -107016117482346548945132799722790195082654004276904907765013628055123622641280 -532756921246156839774884522958957381993726902395061115447006962643864665611333830690037070998783636394390002400377153551361959604547283639352680251392 -648914867617031739979557694643818049979203668057216504375276139438794885369435464053038064827480502814067006804498359154626423603962100263088399856229752 -5364093535730936147463541649596432444678219926077667883734454151057107749354453510341798218396870848298991695001682180874640712029651920116811669171520874 -1991156344814561149591525479707443429666289210206171383484367556587262040991283414287056032203739853332216309866181308381599630640701720000238883574906880 -0 -1240722202616389513973922768685786106091078788555724681396110088894901580356174059693107801547999717844793849118253907517164294570375212313898259715085141105940658891307423763778538377665051892100124228941562467259128201610076266056373405707086736336167605239314782881108716366473706926696989024480092947548903006603393526774842227227605392632574744471346260936099413707173396480 -0 -2 -792 -908463780656071624777999056896 -261515365625536058211954562321762025570 -34363418467612574510754913120306634286074855670046492773113408281091304849408 -10298831750975224705374170392070256950455108341101642844973509149630901780480 -0 -2574028491173218120625899532721726274186853463121682997388228482942119986058323148061481093086189118448821055880517089549298918525641370835815728414720 -4120102032912485584810225493556057268029400391693494480932846592203494031076348985539608962819052881655035473708663582142166452080933667740632631826057007 -856003575715523076496369484540354031854736721527329146406504552401922587486406713440294096206322908227412081709722944295562761974115865043527354259339729 -21764966850770680808129698311404458926940461873933266305652877546331848854154464975008335516009248494361468975669230614809453688953293987972822568271872000 -0 -2481439672835455316435450222961490952467054688477443973252759241836914049418059193066289201770080478782010589768308256548437208615056252516149995383823877308087652590010038324508787501850632011084265376115191445269164718609708653219057772287156297315205415965661016801043979428655461218374679406517792258741473669803979329510535342890018189384338466515427440201885258439091814400 -0 -5 -520 -604099872677461461719763124224 -319016032279817633173454602139076657167 -25937207995432447928130425230903848811380238160577523896265360534027632640000 -28845060685139153243157308053688441659186794603444860805606738986483991969792 -153301215783503611051396286471481032939260048039658940814506725148996787175097 -0 -3149305835778308075879605336418583050818943987437860348514400116385895791740711322789057830278628831079477475379487412794197710343939864774337162678064305 -3659421921346067805224249323884195392692805978261828687014108315869866733026982892516385418585908368056386669445315625696706939840887495172401263957634672 -20207055997465600258590823136326869317265887543207831056737256668785160996102476475846643074001715697259463093105530757445235263029351945691975506286608384 -0 -35074662110434034853557842365135370752418744845644232722536724705537763948482350096800897815346751615649214933728622564185847196492435060706089044924648133026630781093917108095545664072447733261889308217617387286462129653489067160073127037621299577013488448084508231783465815261392679840044175715075506479855049458960417568692240224459402814116944692003822183536118940032489278563958573604110859792014017609258239480765944544557609136035015279952003072 -0 -11 -372 -144551572086744903187712442368 -253202426808241135906467029640487192367 -29680470818121163968957353650459808483467973521537669013305160691127391617024 -63848746629419255316830265708104138899454095951935912495751338301473488371712 -209179562285481528286524049750565802502654182439978651496342336317521325724342 -829324642730741957628232762455280255995410668556934354798600131375360370313925839584716822046061062812974125513273022383261764817641491685689738133504 -0 -10836405946081245422742336274313850195167725364810465218531215713913733992452962410563926301612533486724610590860754949426288686136633692982939049606026404 -15887885497553844866770022409478037710567337044667622956829665017046764846823120909657790652833375698308583354385844777461147565338510949008545557338128384 -0 -8160474863985113685413960163730584223719357599028623408776979120151702813402137032877800155396033891808446883844651711571976485904937817619793465524297057901468757849014724833218607197896257643791218077720889294851053074003636817287341102356113265918596060624586091608267779758392456426930790953644841698561311367516692847963980681340876468289861231297504400770481454056706129830004757201411809017347700323276069106007745871452840572171060736 -0 -10 -696 -63686986775670542563280945152 -340282366287113163957929847168792788479 -20821682061037147931036251650445879572012829156925512449007428546569783214080 -25601876446080871661169789388095460981655785026634060440120544542909439934464 -46283051732920571920660553002376350134344620740408867024246119461520606057344 -866122526761241437267861069508391876570186472078175495527865085403608259792507543050734579496604017172579797485513755153244749481961395145194250174464 -751887818320028821005993828455893384336758898230218749149055282685783130973133321426891964359199525788718707578879513764058378266511916633326134896279766 -0 -3604618761161597206342350970913574830294233311288308625456005962609440682103556011303416761181780744251205047679242944742588136174508865995314329397755904 -0 -35074662110418088637949626617730057226421936600416036348376934414485375457507498005829987619930255909681071425174751723949400452853556137093441769597080466171349184338243707744904582316746564809238360330702316984594788071319980528356364662139411447217586668974101390062027509809569485429840519136697767119848670374358890937563379457324918923934982963321305895999319544761333153673280222818219235010124705215046496087159771444404027458888031487607705599 -0 -12 -508 -682119343286724555858841174016 -180785746917974735173281714194813183472 -5580224303111442529144190824250128399011214808680123220308726439587289759744 -17515932956706260988612620612193590854953640142730013998213746538449020649472 -9907659893672398453699179009332720595687058981623531639980926043720794275887 -3184888035370039058932907606064617684917571849344562184675716803601698363355196380950800709154896679240574015331206027838214015803667188966671205269504 -1939277412313910453923860576024263353915990343780420167100598244605807128360981219313745050560191239583084621187461028223430162008675508756516321033825953 -7544913367357988624324327418946969851430820484554794210102692898204924870041052270614682468020351324034119781305690108157765024705315031670841334136761032 -0 -0 -35074662110402138528271665275179256459319857320693236334209567133960895549163137483507987803016808302684056470332523040304974402229876505917218722980389688882912590974423582656638710685279885141901775655476694920569196059922042520886245076198361066013800570532010333824987228784598797064323901995346025826169147094870809135589836258762705579827219567252284956034887883311295478937617923515174929544803288635466681019026754787765599275517274090540900351 -0 -16 -368 -834152975168131183109883297792 -2731153852327718723642764315351640056 -16739345702318154238468390120664842988923487568550402381597989018513369464832 -26192847372642984316493130988730774078076196015632139922330970610388567064576 -104723168010174221329114934225821991910793576870554554426099931685475302321381 -653922625519279168710728508282959717574466941571571942643038627955030290090421817577902989875684705528942667503338750102009083326094076109973771780096 -186190712750209308613922069017064352951759692473158908687555524327905273430597973826862816449453775210658767117383914522170327568071347324740763469658896 -8514554361885317577690191823659571610507469059802094070771568440358941323502767221956336550299182555302795202787986875783648471851574051008502057414069492 -11833661586052332638708107440826226981195435085476685424422655780400433674433173003695842384585033186629182554430905981693347634778306555757794509457457152 -0 -35074662110434038747397340153515397959083397646635472623346269044001699659166492053837664748628512113832781775758233367100322901717652040469021952229078800851746673632207665259952100994382714318608499234127623361556318825050908217829532854192232468821284169836658542344679746921635369271191749836604148403822116788109294291805412513613991164094354391878717457753446781292365337428058331236571214709561469663140595145329285885214502847991934657427931135 -0 -0 -124 -645776086430913674123709251584 -159512843152379287437155296564366721151 -37666767247444293394418691343023257558894789271851834370123820338381311705088 -80875413624778233697934918483499353310630131199204877857291824167164015804416 -8629254921654409097470166752178700453582150003113870357684330220966921426411 -2904466608754129848706649256486733305790060880925161433940664496153952298197563316148752865089704089457726747125749981121845234247076084305222904578048 -1519636082375093377009006113600844217813181186233326309253633670185518917147244780749625773892695777404234520414310928477021665313548887544553248312197793 -8236195114918708792553106022372502777707407609963091055128846764757013023089399340055634197487570935890166639564669252984893194439683590472008916671435989 -14509393776357103763423267386297242187260543661154981367335031195064380877173387136138957262037470360482300982321646561928409250601513458954265358443216896 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -303 -1157847834746177602004396802047 -339617671798359351079692875821060456446 -57867775067306645753843239588701162944294645608219998114893217643713234206719 -26369455828243150957066923597651481411694301395220676885210503666274163228671 -11817720390503827648781894443752171196964219125141796458180108788425643579396 -7804371375807015860240250407429415282553584425143433436919818048882206801446222314107580658597196283229772192970700820967091727381696957189718015 -941081632068259047813718693871170463934954812652267338340003979702879896717377687462914527089361639645292415086148993612559161291073345891219533759743345 -13407795142668967188561258511782701242040321017549139392778624385515507058390936234830335193232662892196414947249258255251754028773259240421675666352640010 -18030219496951436955703469321804144806589781292361309272091751131804808417784938468039344100394760314581363716192040882359255669821551713959394630176866303 -20764409093138106714694669222174000783080657450980639056024872831998438690744740872244449057340558976478438798965361453061982147397763104606367603575189606869434278459569124428885756214636676791959099839519692515219337740131125546138808227180751694999895383762073154332479036368513723304712662417407 -0 -0 -0 -7 -229443170195852266665106472939 -326323769346777103406058243606905356266 -57302384040277608603670736599583187724940326542667573170935472667337666068459 -90590215444683886737468380513972670188957748728496883947338607689822992596971 -16588391383794291139492557302647827206669629364357230644404399985567914899541 -163891798891947333065045258556017720933625272928012102175316179026526342830370668596259193830541121947825216052384717240308926275015636100984078315 -4678930352245731830140813477948099889967135524798399300239972912494574922144691942630433785423691531675030624592070979309607042551255641744629228495565384 -13407539397196368968305928790089016653671570609951672448135458669233231578292961080874356758157767482668285483672278785166507680900960181539331299589837030 -3215987397587457281700155817600247737540168077574887142213735056600011032864904372279321264397358905923222529639441151106221660309360444263022362294747115 -436052590955900241008588053665654016444693806470593420176522329471967212505639558317133430204151738506047214778272590514301625095353025196733719675078981744258119847650951613006600880507370212631141096629913542819606092542753636468914972770795785594997803059003536240982059763738788189398965910765547 -0 -0 -0 -0 -443488317747981344772787797108 -77021562540042882543736912340894874738 -32227291991539810993520229032200986746576275985417526906409482965081219136628 -90295509201033475928510352277060489802362767446802083703915909927316798569588 -77638221005315388787307758184174122082878241502208415068156089607436757773918 -7086369209232770401098147369945909076558654658030237560723194788385043775713169861209683238006254225172633151217396345438119288462580837128263957620 -4782294357738321615826518443171082886036061084269642536684046318902415683538782049212941629293505381904644369995691924320760708675564364862074754654096757 -13396197085486637899982055632974105159517490033004013453211059152114314035947763002926713162586156070095743774033239286885832152764496920107358633659524867 -13913628701936294298439646799953209203835997190662346291813340972081107507476264087323589245978744413156578492374221121684868367585557359458783374461631604 -8138997384706728877072035945360424843873187368355532293441241975164893238867331679044578164475889238039813183177135485141754426910479982949590179951415562696524274642701652917859487803566915383278625110689674346601085345682179558937583211472781420296541000741603192074524711609552018240800068099636340 -0 -0 -0 -356 -0 -159497014274388579608780698593857114352 -107785315756833360443652745599246659761450128680158928423717212164001366016 -8707697307478053793157872089996555339211642752348089356505163635680694239232 -117438470015262408821519431736839939113907975050128689419137692043750973261407 -2221209391926212534974417566134914959972451519200164319238141998788375520151939771003428560858041773117069334356919987508503730468005507757378633728 -2622195879240643840430956614410100176111727871855021220330072279789821003236833457966150025063799142237083045026927065925049487875866674616289389230367401 -10838798868213081376093889742061103552907660140058537670650158514072587789313088268351190762885376907745601175335770832945166703110256088823029782185916220 -18496502392531382272454270596836064792028917016576313981708381711553340991353189878658648246899858104559267375534283098234252896441286568201405948139405312 -8023149053251434330424390239980489075400259799640003038245956978737225036782755130046389361466959635606250835455657093887634358047146742300058148458943937296682605801163384099346471767498054974948940568984229726746253562492021189947972342847243200153751700399237590637919220716300341227271998607982592 -0 -0 -0 -881 -82111759186571175044015194113 -0 -28976293586122847380028336844772629234948658991570145015189074743489934131201 -72183022301677682291580123397013999783183881280283071356191234547195098169345 -2406637046295717734201709310168745488608959709590841627534368777523827570155 -3270192376506343102611379566519773667881704351629044304231346291458365652232113923427745246534084152592966234784884272774908997999646616577428097597441 -2215358573235998131874848884702814898312700633459869320330807378394889665966339455958575987405219519810470844163424203838448880979342258184452542694566999 -11572673791131936765291373228428070441513344356977752630227033953477527214083770832877643183241668103444587247814952464182468268813420651873183304946511860 -24753412486464952127871838303672650263739621747289578351238067167728759524343749186993890880512639565343382059853348185532476853433055754161685198366834689 -10715075847328263987810529469149461320386431997019495731790863105054566969900033555506363209309697962458956036917241364630652892035661693225326421884469385462600854566136062395614311464777955889879559949117718229449226545818148296831892519665180916040043014060359216829885348018201091171951617402470401 -0 -0 -0 -40 -43745959819122117852054683648 -168479607903394759962667067146355343357 -0 -38715528233353617672333399869957801244330607838176871550029916258713271795712 -176969206917672212723902333860838760994531076051623743859174420245776832201772 -817958506506309444344089149754156868721831281527900674165814694260381207165130129275912203117045009595571041339148072007269936892193254382249592225792 -2495012392157735608513384785636121563536148297029125165554961898046274459648386947163697616808537824603964419169205882992327425601924558527830506385578472 -12578080335425898937631034914453481580403735226890616361473931872760592191355069944068852702365841074365524729550127783372599268817147362827445181721803028 -4414537338963156055930766392922676968349794825277298431003769994668621271197649418638348577968549041479152488344038511751351114279431399223340286968070144 -3810222062618068365811717150182972227866160775529219066268690269078204923607636500488737226639216135781691347449728957827779459090610135784916509209491391739525780725489429856708513412863019815772987039494965127359929026869978738901475381609951397576604571746823630328801160385531009949957361678417920 -0 -0 -0 -704 -909145740350063529575317504000 -339617676839812128055403992765682035710 -14413099430244372787741726071480578266606634696124905686464507820541353132032 -0 -146524398017507879126999928537531295106484509790504566263346294215577739873005 -3254975840579470187367063835491635394057759998624067184757863431671954891319873665317138530989747197877058152019092707613957271443147158112429244678144 -3843711574626452538685381156156840092205919446460373936017331874304198217565605768350118518588418750150969106731407527286192635659176975773727687264432087 -4205957252106146285324136572167758103773911995097166007755650365293107620480255479665969264400061595898062331701334890614260926224653154131473051276770435 -8408484607981740863725931685258026020089701582650612125407183571331861026600781248142155638797478057210678107527105854861551567600339635902678924701728768 -7247432637346354371163106580695035004917324395155467725275783809664161137077212420767747102195383275127295776660124857944728848308128852665484048296413410766845286744067292360940573825374902097450095259142985756793464941933627257907444867237270935916576555993785848630242076423028942395099630612250624 -0 -0 -0 -257 -298786413494195337926333693953 -337290227426691062405101930191574138860 -1721557327184276218905921841580400523329018723867073430309901742933509406721 -43140760179084762019079083403357933038043634800552651991719281264271550316545 -0 -1722565222021586222240835395091318715753470054907533129614806861693763466404200515482815025582326808788762316564603538107574831856655144128570072236033 -2234952031666178846254667297858171395150952361837877098087851428105640476175757319358218484222203579051989090125885331901125929168491732957000222166220648 -1518988687987889548258752669937919719163485722572881415548098968240657129317428928409749883901857358027538087530548460431243184110285208539870701198734266 -7561054567803749177499530087746897215634212883033990021094138935566693847154583699959527092384199840443587272455015656435728392875020886765971239530921985 -2731552852909560361740271235103848633585430399772966278368610671996079656149041832681481179874533365237667879503570748309035396591549050138743255246745020083145958259577107779208560547563441300194243546659169188197945241464932070395298656044644609029877051203642896482946894197008303189122778822344705 -0 -0 -0 -116 -805503763346205994314608148480 -340183520577929235344824363378070683647 -50883512962844550181662585462065381386388661208506498472907426814607210053632 -45369850654893983160918274674785697866505559719989912454419447783027986399232 -154302323811821750963079572939589834113268452193107213579317085474668962267832 -0 -4429069692833256013828033293417215715515853411462216173559663486555105143091358435961898114179716869586726330539677802025756405286834909255857771319348908 -3413964447864496376412885553514780382856915468175834848210969401153418011678718897157435214753452394983129758958670494207724335130333416367673732227946338 -16964157088706179080247287469821598064768337533310042213494946742295692399965660890951897881516503810954503600149541042117195593349934525801018052923359232 -6219124707082025502769567924616693739587312435415643801252808044792830067505770684521209436169838390705513344865384140441996828911506822132071414925409408028130637631320031048854647327652038853841213412686318991744226884295456904299892342457948750792019916788977941916623229379610287222408835093233664 -0 -0 -0 -757 -684792255765091403806850154497 -187838498585952187882913144128949314842 -17880804947835988471289056186186828981571763627604504410978695443865690701825 -73844297609558392021902368753050990125982306435322133697955932525026812624897 -94126279577000071487888075124472770168590198066457285629539552899453239919702 -397557122057289471646406389446737856415741637101452044100444345939109374880449993641098520778717953204989669069789407806036305996388182260507211726849 -0 -2707983572801101648847089953010363517264777469603935668937876318086482354961489015923724138040887781936287836173917247739006658581124406484336614660459035 -7693080372738418372562538145926343985101781270464320587350012492765027537754950396945540360958485453885260344064936191173890891093385965677925002802888705 -778559862303730584584607440255252490793391466369614533225223492586012662394527783524384957495858401288387266400498759442518012862732395452853782993747936084285564333054556373460085077607372150007252189634712684068883939412024299532578569222291919966589108225297068060127971290200979371023459277602817 -0 -0 -0 -501 -566596565891779443770567688193 -297747721340733346357341714763372042239 -51359594683295705627160736875920391224771401002889258517037659597857844363265 -102373118558924839957330237483174298027903126318578007347934548864993903771649 -36691334835498136477362251073079490846730566134092461259856543443113747864664 -3255972356450126473367269507262012034771303233154406275900353518426269324152998622154947912079887019154967753855758652434103601805140721100074954784769 -1871705776278248879396484688509131256000830153477129182065775246470064636917070070521484000777436210886966790871882875760627093515239202937263621093957970 -0 -14537918423071330571316124318525036093070741242523364509933761893317940689672532870214776228434171755763504198262702582936752520446579624047220655990505473 -2231145649723506021153228184702309227849416613571515961923155876896496547807580652010734720048413104124530251069253785706943018637185107546734040714409749571116423316614742963894337402654119736587425733433625423343298072444077468715756898217058657745332650905736779877862268124403573045045696512655361 -0 -0 -0 -260 -1150353400223286785695195070464 -232614738005118298378871055549980366307 -45914987571294562618885391000426099066127147173061261158053815643543682678784 -9569712348825214221479561079764339151873872589857025114776692020553231368192 -211318984418980660719515851833230980412973699079543743262439772344023095592422 -234544158960971249369204990932617363388429151281731167641912793995080736229449905117680762908260619808068871667768771987742765263413118331700270596096 -3261048926869246197614171688872921244377276606862369969053710013018569059589860033341776493183825115231316522373635165226613047961168692039060337222141976 -6177808343254419424305538794190808827588752291948002891198342429196741356936483631524785345634116296590873366080588084683919843713525117596040803781187142 -0 -1244991193137117952846157937025143570362432662590599290360149373303239443080491255301509550176958928885332124568019988033187164346281378990556622407311466824177344146655391099723319760271743904382662218341941576791410880342566702310623956919116180818445686624258025155723557685769130435612651017994240 -0 -0 -0 -124 -645776086430913674123709251584 -159512843152379287437155296564366721151 -37666767247444293394418691343023257558894789271851834370123820338381311705088 -80875413624778233697934918483499353310630131199204877857291824167164015804416 -8629254921654409097470166752178700453582150003113870357684330220966921426411 -2904466608754129848706649256486733305790060880925161433940664496153952298197563316148752865089704089457726747125749981121845234247076084305222904578048 -1519636082375093377009006113600844217813181186233326309253633670185518917147244780749625773892695777404234520414310928477021665313548887544553248312197793 -8236195114918708792553106022372502777707407609963091055128846764757013023089399340055634197487570935890166639564669252984893194439683590472008916671435989 -14509393776357103763423267386297242187260543661154981367335031195064380877173387136138957262037470360482300982321646561928409250601513458954265358443216896 -0 -0 -0 -0 -101 -207168185181127192104916746241 -6668390089268912624007510659545825281 -7392501221610052037831838960183063187938375774631636401628501937882774110209 -38026175163071874838805499329493802282417735205548075568597458066193409638401 -27115525541627581254244701722304401192786928921452957799270526453608608017866 -78987678596506322875930617159547621292696460537660610821634608467472622976648818522866145789310763062187654478521495420694971228490978312743324483585 -4053875392434894222602240814071875993431182939882351675772072986442918242597468387233771257670847063960183350051500946180997166333232802487389361981648970 -850525959840592625281286805594153401834851520276436404150377105009802771490702947106459729853684800259058796419640436047592545322828029446280706426269812 -24822032730919654179630912014483181694785612618577272870052909691681757080115622405795650637501816464411130630991049613540142767661070926162190722618359809 -5980147341549944839171394575473711669305217586234872794681557732495920877555939181217702261799375726098506547210273244879426757432133511899699597944613836454358060166562881498108768497035056190526685259548739450204860349461542132307353618525041387175591587130200688094247732159967740384726635113349121 -0 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/mul_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/mul_outputs deleted file mode 100644 index b53c5f7e15..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/mul_outputs +++ /dev/null @@ -1,225 +0,0 @@ -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -1 -21 -908 -1267650597867046177654064545792 -340282366920938463463374607431768211455 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -0 -21 -441 -19068 -26620662555207969730735355461632 -7145929705339707732730866756067132440555 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753536 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097971691 -0 -908 -19068 -824464 -1151026742863277929309890607579136 -308976389164212124824744143548045536001140 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248128 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014068 -0 -1267650597867046177654064545792 -26620662555207969730735355461632 -1151026742863277929309890607579136 -1606938038272679619211255036084048932956190504430095264907264 -431359145870941220571487096504865044588697904564591140391738786447360 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139072 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542341632 -0 -340282366920938463463374607431768211455 -7145929705339707732730866756067132440555 -308976389164212124824744143548045536001140 -431359145870941220571487096504865044588697904564591140391738786447360 -115792089237316195423570985008687907852589419931798687112530834793049593217025 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569280 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347234305 -0 -57896044618658097611351864738157061705262361561497619362091104892532012613632 -1215816936991820049838389159501298295810509592791450006603913202743172264886272 -52569608513741552631107493182246612028378224297839838380778723242419067453177856 -73391955574979118963811141843059488536193514605218060347731553038824318137413435829926783487818828867436544 -19701003098197239571963727475337245584161626291901231402719522479678259504890677218990119895590117915143388591554560 -3351951982485649263264086660821751293167574115217012473116062855570117177114956810549394311722122195190355926629690380263290286253266199596551551712231424 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -620361101309323186180458062143306311203234091808422651940994483172973212453462540277947848500764537416293185308049243121091077089522237714087216426627812178598932718744797175293795424927554129248204812001597295596246882084347634304130710520242400817201806901439842621734294451519310289297352148130231477204202016235965703658712648032709044435339313088432114418262153009154752512 -2030684202530045702032438070068470600111523314891992114474287665026563901579442604747297208342924347358657516628421307413109601517971206668455722861607048951866317121347560401223028395731439655580245638497036905507303055096291514983167804903577061365248747133480848909746535730849828042674125732472216127172563383280944806187156793401092909358110607810689164992655422507992440651826516323860482006072417239646401793958680956430020069720523666773505207631846930990177373853120582291091033196123813538806140647566683428129469366272 -0 -115792089237105570840234253759177109864155645142784332660520492325483608801280 -2431633873979216987644919328942719307147268547998470985870930338835155784826880 -105139217027291858322932702413332815756653325789648174055752607031539116791562240 -146783911149691239803259475393272332038744581223672095908357420057841712239442615794649035123910216788213760 -39402006196322807380529480735708200350692396090822410401547663254352517428719749608020233606624972587007562114662400 -6703903964959104207882943247098074879292236184530784614687051259089361504778664925296585557644723899819989557097938820112754998714546796218504411307048960 -13407807929893819778475470707735784992488440665279129110225796518690280399801487040957178859490788616767266574533518552379422042573571257824675478529638400 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -1240722202616389513973922768685786106091078788555724681396110088894901580356174059693107801547999717844793849118253907517164294570375212313898259715085141105940658891307423763778538377665051892100124228941562467259128201610076266056373405707086736336167605239314782881108716366473706926696989024480092947548903006603393526774842227227605392632574744471346260936099413707173396480 -4061368405052703825017540452826323628177065892919822203995680056457293914967717151139514347845670761968317643193023213196105279034485542899856942497055973489887306964055240783810529148217259878326321073593635104947630604439996612121186052320432514468047976270758130684476727539892394554268861392044474193564110067997362607705131765085400073399409683739317269432145800594368855385753859106179981112207797478255195155380669937444019554506527587107321605367039934467094788088851405871320736566808054693946822238648612224007804026880 -0 -231583736816786089484927226016147767929578972263620494977377884571370600267775 -4863258473152507879183471746339103126521158417536030394524935575998782605623275 -210278033029641769252313921222662173280057706815367409439459119190804505043139700 -293567262432083559770702660509494961636846893913475830448684457955877331972488384611363806317219648949452800 -78803862104411649792976274791681038936454943300723566886694079153850261012036590002656572187311458765011955822362625 -13407782359700221432208153137018396716476230393601324554188676058418403973105159781474496977845448805812756231084668278028521746573850893544243696815308800 -26815564719351665682859297115793503881598601899157213443769731667066042177950468012051000327834243644827516674656869253925854969676662574929007714762752000 -53631027158026444898639041353951338860236953565976155186370673275827169990229796710610136585486710294744741538759339503142188427219888626557524901703450625 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -2481439672835455316435450222961490952467054688477443973252759241836914049418059193066289201770080478782010589768308256548437208615056252516149995383823877308087652590010038324508787501850632011084265376115191445269164718609708653219057772287156297315205415965661016801043979428655461218374679406517792258741473669803979329510535342890018189384338466515427440201885258439091814400 -8122721319120455379930921158326117871838290539711891253924140849349950965031209192469013362813226005733220497444267273168824363782607514900842646678617218420603025285381689301022402706647780653422402593097268270904777394649862358017106423565800216445602438232961812733726274533361295534877361561034733716956723820342508274535124544488827440330912196294237990424996646092080259959888790753448676537815274083485701600749922454909872662388097681488248458568576219237116237159724865297204930388201921385039717285631411970780665217025 -0 -3273390607896141870013189696827599152216642046043064789482248405676250539528505110690804490707447337828870590656092725446174963629590181147084998049792 -68741202765818979270276983633379582196549482966904360579127216519201261330098607324506894304856394094406282403777947234369674236221393804088784959045632 -2972238671969696817971976244719460030212710977807102828849881552354035489891882640507250477562362182748614496315732194705126866975667884481553178229211136 -4149515561151917970063980879655710938513649460124350952720371065270297197065154634382680097543307069927884450339928718724097725668943949230978441358030208342019760039438410280075264 -1113877103911668754551067286547922686738237475419584309931192581897577662915063323503306740447008536028968181678028580322422866333819711284625174374608617279642589745120484326564854874767360 -189516368689051383356419666365934487249694726206307462692478442160356412055175068191548410359233923189538270288357620597887942150501998384340803566981539803652861191767034299241276777771401726930027167539955532412991464793964544 -379032737377413310837469826127201516165849126556927185280268831441004642386080191344591587626743143491163530774059885949190517071683841202707214877193839283509836239507327810571115256102701286181423314856476535119531046873333760 -758064029037559548223859302619650075474792064511873124215265408149659395559453789942397036648834306197292055977388138161024258210175532633810972040915105374809744387652086706188503922572983286780452241329985943497170267983052800 -10715086071862673209484250490600018105614048117055336074430675836924241540472703456698285220172692381666915465456597657220856438022326048260043738079097569861723477222864084024723456654120868104119493560585512807944190828392178187984719137393049169103254142087127248661337626132381236011316443311243264 -16458372206383560850154727152772241309834362634645429482097685030171884111143709030259566480970840675586286057165552281949963074948236086284290260005656649082848245798566424861913543183906541851941660459520122404258933779435071795354694349641611033685309336221143617936715894403545863381859480370679906304 -43888992550349509466047490008389760228034918444740354476264789433451422494435484145605446873947256471512988292597450618642207676514741311261882165968599848430310026415377131508214755446432997138431116604680521746555008944242629428478468183278110882114932777127534190355484669739785885245968619478007676928 -87777985100699018932094980016779520456069836889480767605004803903623391638540464457280122746011077411993751425324753978750781979522477580196467009117071428016628499217023421730630965195213525748235513398478599337876370083082111993797578339643292113170849335330813036879783075982297998725326138636180127744 -35074662110434034853557842365135370752418744845644232722536724705537763948482350096800897815346751615649214933728622564185847196492435060706089044924648133026630781093917108095545664072447733261889308217617387286462129653489067160073127037621299577013488448084508231783465815261392679840044175715075506479855049458960417568692240224459402814116944692003822183536118940032489278563958573604110859792014017609258239480765944544557609136035015279952003072 -114813069527425452423283320117768198402231770208869520047727692128105340272556505252150160006414760200146251310083289009338166699942758143608103582817139261613860686884404865091240091700335916571366968398775231636972558602982410372368561085607640039695044427708003371630792169233277670446637805866344012196830083200549854376617192853167566486801110672010487276196765581360935421367998631422802129683085092788505721142860577202194007940588172852101767238437699363756153062785321947495141655278338111834268668260296874876439322420209270108736741859953710415066991479177424624334867465316525824363705925632 -0 -5027927973729236057982426364448826617555638513633071601633370220421967636306746498030257094484300966958703364072352628851378448187094873990326993486348287 -105586487448313957217630953653425358968668408786294503634300774628861320362441676458635398984170320306132770645519405205878947411928992353796866863213314027 -4565358600146146340648043138919534568740519770378829014283100160143146613766525820211473441791745277998502654577696186997051630953882145583216910085604244596 -6373655901930312136397229380018411440828683949391920950832693445711366448193584697763678864931677968612065262908033170282412527802037703004824976134384286640214222530054133889372258304 -1710915231608582551713494414139930175751260546116444122900349352360496966554854730076753484481830650675467827629295222707109502155201699203484369755223655341193278275893163301956243837193027585 -291097142306427050053565423426590890568146840930776532405423684287884740444915857761347334012000265903675449121187612221625638517177392299309155307502337486020927127535947499456450345719682189031665783117734919052512795878316048384 -582194284611795095882563124181957331911668652816390808061798178917116866814434754607160150716463467811845369643570012125338680346889780167007790616300675328647243232411818768880659637664081627824716845155217383295556538120651407360 -1164386348601867966607659549490709240641434805493466720013626865708760416816760684474263347086593403580537140168446677038569831210140531685534851163995750025031331763664446019825967919583282914146489408839507982335431232969112551425 -16458372206383560850154727152772241309834362634645429482097685030171884111143709030259566480970840675586286057165552281949963074948236086284290260005656649082848245798566424861913543183906541851941660459520122404258933779435071795354694349641611033685309336221143617936715894403545863381859480370679906304 -25280059709008981479231968148711644861442111696433705443231567360117402528393306066518309787037990431336656280044426224316649696164180810324022564882675140987960579876942528723700768267175237032707899300231700312150169638715161500573218061631068318350665689512114702779435677385026007774824467356638267834369 -67413492557347065242233762416053344604668789415978336070232719704519730695842285928289497175591340668357371303688672579355774019833840277487199698360245654983419254719431470785652320406511710713843764264258846732773932462012692405900266395961054012873582577495350462230973011642886763600990552681342023237633 -134826985114694130484467524832106689209337578831956762230667385079098314604114856488714967534864721366751145206577896381937685648970101488283024945486839684466321300593943839555804243973666843820217508302523380061872683673515732869098988513858354359158586336115486301550288354364817320561524260650122775363584 -53874681001634843991219960220676811838216618976411872431379454389487697453585414607630678378827390586054634875212617995067952662788488677051131226188233783645486119633174249397007355413950452332486425618874867205381482252662669001815967811090854730467120253594575199233845159152018106715728564966104973555959854476034899985603379172050429045260764066870120309159237337727562784303384359771520036909014223788365313020881488428759631128761329980986194132992 -176352874794152226923041126648344020296255845787663270025002097998433518218912651792864315498223942025499431461820100039971032945845086214853910514425858431681855101933052310870599356102672426974402827054711569107570218750844042593899023102723914290002809698086928282996956938154665332804357164535720651529889760012561396833002255365927318549914998445177047355818945707770980966071916771923738923854573521041830764111506524804411686150461839884079980325493144566147614078318969489021758698999020279168507320287970878237811504779638217033120905407240942949803405092731964364881039731836747753076040480587777 -0 -13407807929942597099574024997867385471458758537929012740010782671329620832395834992528134114986407662672821420428107228756016346166874193365792884690780159 -281563966528794539091054524955215094900633929296509267540226436097922037480312534843090816414714560916129249828990251803876343269504358060681650578506383339 -12174289600387878166413214698063586008084552752439543567929790665567295715815418173215545776407658157706921849748721363710462842319521767576139939299228384372 -16996415738478256005382065682640742151192912704409604594012987116129634408058546371285779101609491343355313613863898166638327059654603396304113335697353121717251389843622698066460540928 -4562440617622195218641171605585119131739514871918667547682857357314343535508689562160604455918497575074715174882382908134196095677901105600185199282794955018371204524886410100925712980030521345 -776259046150354466227894953415272126532120228827236797123305565388337686400499060284004070246335310139188795312112703394374362640691719752700909206017051198060493495608220461125520225112836119459965070690227765389768580522718527488 -1552518092297884921190276407777826343730130571016241164825192478707830380921635634356568453745503455149401263113730019700298099019902002012202155472213879518073654497303294521076488615535022163842239123103052036543841131161997803520 -3105030262937843909524927703451704725658035213057063685463909781308268889930827638236818973565112070382840731126979240983820442568989111483626724942061970361307566813811606244576718762256490550467423465073089242630619613999057076225 -43888992550349509466047490008389760228034918444740354476264789433451422494435484145605446873947256471512988292597450618642207676514741311261882165968599848430310026415377131508214755446432997138431116604680521746555008944242629428478468183278110882114932777127534190355484669739785885245968619478007676928 -67413492557347065242233762416053344604668789415978336070232719704519730695842285928289497175591340668357371303688672579355774019833840277487199698360245654983419254719431470785652320406511710713843764264258846732773932462012692405900266395961054012873582577495350462230973011642886763600990552681342023237633 -179769313486231590772930519069826442426264354005082326596360185112449176958549873259557850386600277367825925997915804457268344327753240407083712876794615624877762257166854166161646934596377646965522119647578649647126703396773127858133295428017635762092566594653974618708449402594449637475763618354340068065281 -359538626972463181545861038139652884852528708010164893433258891975305712994145996750811464699841724874842731010897931000393441458205293971046234853904111613343804779646728343027691647116232436982584254759296818415737834043273351893507362234301351226814369365796651940053069649153544102516262530232441150373888 -143665816004337806760172922323967843540707266966555160070938769845933482343514311921981576218185379382379331497993348644831971734671586556667433058226409769129546130073493215528938527708576559133205415270644020134698724204081225254029881946465693095984137609224865240709292656860316268992459694963883823498044606777540574301742157684362334319609311262061951308672635789253885343679712825576151996358495453517343899352653487644349083554108658142161712185344 -470274332784334653125768479190507147507942688099845821153656843754997301836979240962837606053917822376117946484914782536168160296609184534239096896145262203039351588043847354547077881392556187451052716096931348618124350989790368724551688012179157579351487604719105146798178247445433672967393357192101321816498019101823012168469706240238546733430563115610529101897377344839733586268940478469960836347004487048570855680122137578043206023975369775048480356517721340390187833972378623137126307547351741242129046523455257536374422351385724837117101473491922399552018441281779378146120832426011456202350251737089 -0 -26815615859885194199148049995734770942917517075858043397979502765092926124329914532610493203135186264306101107885244098459867671853087539206018919387103232 -563127933057589078182109049910430189801267858593018911357569558066951448610928205184820357265838911550428123265590126067657221108914838323326397307129167872 -24348579200775756332826429396127172016169105504879103405365388510704376920891562395610327828446749127989939805959801641401559846042603485599065178803489734656 -33992831476956512010764131365281484302385825408819231901736066162392911122587750794642859557837487570646738733764326406607430768230396539092082721200286809873288301231428402460695199744 -9124881235244390437282343211170238263479029743837341192530852050551013988199468564099149809149237792502394782872367659707645708326849200364829008985690446628655225628506036591091211274089922560 -1552518092300708932455789906830544253064240457654474631625503351024913201337446226079742769388732571127058525201398326436194714101150988019032351961389195980792206263906993685249030935631117974530339035198582125552984050009714458624 -3105036184595769842380552815555652687460261142032484404408169394138177593232526559709091771925126394796328107261127062087481484183515508524562872429687439110406240134689348936276584863546005281248676694097265868804492346319133736960 -6210060525875687819049855406903409451316070426114131520435474836896735901928651243521579858300210079569809751772692509393442960425269123499217289079803270033146012536744468900746753485654275750950451702497211174173610068660767948800 -87777985100699018932094980016779520456069836889480767605004803903623391638540464457280122746011077411993751425324753978750781979522477580196467009117071428016628499217023421730630965195213525748235513398478599337876370083082111993797578339643292113170849335330813036879783075982297998725326138636180127744 -134826985114694130484467524832106689209337578831956762230667385079098314604114856488714967534864721366751145206577896381937685648970101488283024945486839684466321300593943839555804243973666843820217508302523380061872683673515732869098988513858354359158586336115486301550288354364817320561524260650122775363584 -359538626972463181545861038139652884852528708010164893433258891975305712994145996750811464699841724874842731010897931000393441458205293971046234853904111613343804779646728343027691647116232436982584254759296818415737834043273351893507362234301351226814369365796651940053069649153544102516262530232441150373888 -719077253944926363091722076279305769705057416020330267347594827451426144142384493965014457252965790028388273268575745766448202845714472108972056832957787126548989380860079998712572623109761917189596553766242330909605534894906264486337757228291297942005327959842043718998160370035708197081835945941328224845824 -287331632008675613520345844647935687081414533933110512134339071188196745795513335307638657529820500030503638059893998544639282004826074078543526937501430385498822553680856810441605837007924382387640823649251663812111395581050675179334316291609265083908971530669507424781643811407050868800014638372550585796767486279157989351235817419037012278476998673070763482207177579797214780269850906321355704457864297379025560392407254253435440813631626425868706906112 -940548665568669306251536958381014295015885376199692270773634051571468443177252576492255360423853770864325258737964248491672738005473375204458692172487804734089712687353813343670064744128701098573532877590228667667008286737596697852387981631862326481177071413971930746756905828624662822957794948866823607625220615269013165228239618537011536940291208905424160713389821033135818514340985521905976691408942139437741483410228329038000371498590009406379772049151623867582176028814687068466685902099735918960489164147117893952824903803155286426295341357243145288763878804327513759861599032840758403923281677647872 -0 -10715086071862672019870723708373567867164049597134887969429342556289689092328893032953381579589338312602609246283412714238525362926688916032991604094856600340922050198587112063568778839323187143820237543594206457218073322356882436956454658807341118763364007714359232059366253413058442519879029375369216 -225016807509116112417285197875844925210445041539832647358016193682083470938906753692021013171376104564654794171951666999009032621460467236692823685991988607159363054170329353334944355625786930020224988415478335601579539769494531176085547834954163494030644162001543873246691321674227292917459616882753536 -9729298153251306194042617127203199623384957034198478276241843041111037695834634873921670474267119187843169195625338744528581029537433535757956376518129793109557221580317097753720451186105453926588775689583539463154010576700049252756460830197065735837134519004638182709904558099057065808050158672835248128 -13582985265193575509847153720290630277475301176695966793104558898916525902171099726111653252346386417687634059875171486934990855171785554727726817192028731914702681172768151028934363876033198780003232825417916826555570603003610443324900765322801514628836373961563717819815659178099324514051673737887522648522145975473923735339139072 -3646154850295010964902624396817474390399390616036693719739415376725561063085178919082942505698777176089060633115597677023110409583667009527018497076154392047301826729011247840192495744913649306569979494349478121435366144715292958165239232610420731738086890347478323006048396719686780691510330825043607290550999833104265740262684222585569280 -620361101309323186180458062143306311203234091808422651940994483172973212453462540277947848500764537416293185308049243121091077089522237714087216426627812178598932718744797175293795424927554129248204812001597295596246882084347634304130710520242400817201806901439842621734294451519310289297352148130231477204202016235965703658712648032709044435339313088432114418262153009154752512 -1240722202616389513973922768685786106091078788555724681396110088894901580356174059693107801547999717844793849118253907517164294570375212313898259715085141105940658891307423763778538377665051892100124228941562467259128201610076266056373405707086736336167605239314782881108716366473706926696989024480092947548903006603393526774842227227605392632574744471346260936099413707173396480 -2481439672835455316435450222961490952467054688477443973252759241836914049418059193066289201770080478782010589768308256548437208615056252516149995383823877308087652590010038324508787501850632011084265376115191445269164718609708653219057772287156297315205415965661016801043979428655461218374679406517792258741473669803979329510535342890018189384338466515427440201885258439091814400 -35074662110434034853557842365135370752418744845644232722536724705537763948482350096800897815346751615649214933728622564185847196492435060706089044924648133026630781093917108095545664072447733261889308217617387286462129653489067160073127037621299577013488448084508231783465815261392679840044175715075506479855049458960417568692240224459402814116944692003822183536118940032489278563958573604110859792014017609258239480765944544557609136035015279952003072 -53874681001634843991219960220676811838216618976411872431379454389487697453585414607630678378827390586054634875212617995067952662788488677051131226188233783645486119633174249397007355413950452332486425618874867205381482252662669001815967811090854730467120253594575199233845159152018106715728564966104973555959854476034899985603379172050429045260764066870120309159237337727562784303384359771520036909014223788365313020881488428759631128761329980986194132992 -143665816004337806760172922323967843540707266966555160070938769845933482343514311921981576218185379382379331497993348644831971734671586556667433058226409769129546130073493215528938527708576559133205415270644020134698724204081225254029881946465693095984137609224865240709292656860316268992459694963883823498044606777540574301742157684362334319609311262061951308672635789253885343679712825576151996358495453517343899352653487644349083554108658142161712185344 -287331632008675613520345844647935687081414533933110512134339071188196745795513335307638657529820500030503638059893998544639282004826074078543526937501430385498822553680856810441605837007924382387640823649251663812111395581050675179334316291609265083908971530669507424781643811407050868800014638372550585796767486279157989351235817419037012278476998673070763482207177579797214780269850906321355704457864297379025560392407254253435440813631626425868706906112 -114813069527425426929660656670434000556791117993150086180221643697431093173028910532415727113435976215204539871783667883838967071195340228232917109351973450013767101034272479645738741907650504496198421145870980109769343781666731468018474148841379223523433938268079692841589810552977465445261846437407748635458762124821556936873529967035483590621122322119705006554547460062408802518269990075352861435909928146945580063563725505925881145977291236879775785378910221502159993605883245075109657690762294065063726665915384443183063224455834919138638489409150864878627937633765733984255718952202044576320454656 -375828023454801161958069925084019843923978286415518836749560060086790811063826786575473978319828101315166509670501153778395493519601305062611233710167108096659872687219140685926605165995932330503576533938874126310080807833945025154398047371417183906188778761688181994841114264769649793185124682348180893134333861320517448227288705530984286877458322203246948912271425933606164725949285658013879720265044644617847420446340162585011712000860105735035610334678196167563897824599954042423669530327316471628496333180947012248953569457951187522058076800530227428892238847959130402967459985851634689104636645635632818315245484817355205865998912643567216769076790812429039605288671959330499956935814269560500882854907846825228822521982049802574527070231420993536 -0 -35074662110434038747627587960280857993524015880330828824075798024790963850563322203657080886584969261653150287318235382932361986378200976930485864824618483190079311572443216561952044719477365706568052969551481762567552970942589810378882925959353769117684987193135809357565477252870988890605581060988309091800917730687720491038972968230771526418895306251191697869752495655463738834197691172975281348836466132387998255571752838316161056568930429957046271 -736567904319114813700179347165898017864004333486947405305591758520610240861829766276798698618284354494716156033682943041579601713942220515540203161316988146991665543021307547800992939109024679837929112360581117013918612389794386017956541445146429151471384731055851996508875022310290766702717202280754490927819272344442130311818432332846202054796801431275025655264802408764738515518151514632480908325565788780147963367006809604639382187947539029097971691 -31847793196274107182845849867935019058119806419340392572260824606510195176311496560920629445019152089581060460884957727702584683631406487052881165260753582736592014907778440638252456605285448061563792096352745440411338097615871547824025696771093222358857968371367314896669453345606857912669867603377384655355233299464450205863387455153540545988356938076082061665735266055161074861451503585061555464743511248208302416059151577191074239364588830400998014068 -44462416394276340862910922260389321581298853470913371563358965890746390942505208917810214508921403677924050484037361854923909199530783769557857398908416797467991642660044081596444758567725905941578862500220928052627591160031402858547990440510112144073496748573294083122100295594485512238065152913601534980743295758670057701269862152855527112109603873427804287879276629501582896265202691196110634325565999430887153483257613158570830305101308846059434951620873330091758706311542341632 -11935289041890653422458560211344367565999989567726224393815132782921219808519438180124970259072038417888105302535754774032670805811336900569676094726678661851388189281957447472598923409448779291182901295823514011338832830575108282476521982723289897702706625317681157972521881629518687524749107700488703863037130875637878556278141428758998783243913325146240393321801800775493463196094645471464263521615496507631433158100292031999009575957202146963989209941350094442798986370318517904347234305 -2030684202530045702032438070068470600111523314891992114474287665026563901579442604747297208342924347358657516628421307413109601517971206668455722861607048951866317121347560401223028395731439655580245638497036905507303055096291514983167804903577061365248747133480848909746535730849828042674125732472216127172563383280944806187156793401092909358110607810689164992655422507992440651826516323860482006072417239646401793958680956430020069720523666773505207631846930990177373853120582291091033196123813538806140647566683428129469366272 -4061368405052703825017540452826323628177065892919822203995680056457293914967717151139514347845670761968317643193023213196105279034485542899856942497055973489887306964055240783810529148217259878326321073593635104947630604439996612121186052320432514468047976270758130684476727539892394554268861392044474193564110067997362607705131765085400073399409683739317269432145800594368855385753859106179981112207797478255195155380669937444019554506527587107321605367039934467094788088851405871320736566808054693946822238648612224007804026880 -8122721319120455379930921158326117871838290539711891253924140849349950965031209192469013362813226005733220497444267273168824363782607514900842646678617218420603025285381689301022402706647780653422402593097268270904777394649862358017106423565800216445602438232961812733726274533361295534877361561034733716956723820342508274535124544488827440330912196294237990424996646092080259959888790753448676537815274083485701600749922454909872662388097681488248458568576219237116237159724865297204930388201921385039717285631411970780665217025 -114813069527425452423283320117768198402231770208869520047727692128105340272556505252150160006414760200146251310083289009338166699942758143608103582817139261613860686884404865091240091700335916571366968398775231636972558602982410372368561085607640039695044427708003371630792169233277670446637805866344012196830083200549854376617192853167566486801110672010487276196765581360935421367998631422802129683085092788505721142860577202194007940588172852101767238437699363756153062785321947495141655278338111834268668260296874876439322420209270108736741859953710415066991479177424624334867465316525824363705925632 -176352874794152226923041126648344020296255845787663270025002097998433518218912651792864315498223942025499431461820100039971032945845086214853910514425858431681855101933052310870599356102672426974402827054711569107570218750844042593899023102723914290002809698086928282996956938154665332804357164535720651529889760012561396833002255365927318549914998445177047355818945707770980966071916771923738923854573521041830764111506524804411686150461839884079980325493144566147614078318969489021758698999020279168507320287970878237811504779638217033120905407240942949803405092731964364881039731836747753076040480587777 -470274332784334653125768479190507147507942688099845821153656843754997301836979240962837606053917822376117946484914782536168160296609184534239096896145262203039351588043847354547077881392556187451052716096931348618124350989790368724551688012179157579351487604719105146798178247445433672967393357192101321816498019101823012168469706240238546733430563115610529101897377344839733586268940478469960836347004487048570855680122137578043206023975369775048480356517721340390187833972378623137126307547351741242129046523455257536374422351385724837117101473491922399552018441281779378146120832426011456202350251737089 -940548665568669306251536958381014295015885376199692270773634051571468443177252576492255360423853770864325258737964248491672738005473375204458692172487804734089712687353813343670064744128701098573532877590228667667008286737596697852387981631862326481177071413971930746756905828624662822957794948866823607625220615269013165228239618537011536940291208905424160713389821033135818514340985521905976691408942139437741483410228329038000371498590009406379772049151623867582176028814687068466685902099735918960489164147117893952824903803155286426295341357243145288763878804327513759861599032840758403923281677647872 -375828023454801161958069925084019843923978286415518836749560060086790811063826786575473978319828101315166509670501153778395493519601305062611233710167108096659872687219140685926605165995932330503576533938874126310080807833945025154398047371417183906188778761688181994841114264769649793185124682348180893134333861320517448227288705530984286877458322203246948912271425933606164725949285658013879720265044644617847420446340162585011712000860105735035610334678196167563897824599954042423669530327316471628496333180947012248953569457951187522058076800530227428892238847959130402967459985851634689104636645635632818315245484817355205865998912643567216769076790812429039605288671959330499956935814269560500882854907846825228822521982049802574527070231420993536 -1230231922161117176931558813276752514640713895736833715766118029160058800614672948775360067838593459582429640872806815375600524611787633664372109038831131496892713847574204213375769343685580784773753635629758429922695406379579200240048301333922009856375733053746023334939149959900017297080529761791881121040475796858581600002403295404329868081465157652227751491087455277833629681152969524186808613504119583496193428420962898057357571055319374451607243028930569896539578660288198289657864101794216603970753824498208473733314597712660353208543367490975176753797670373382265859509054611517991409426349053031299407068631581080918639237312826090371923206219034977458927000306459751555164292071452400788382130601457910261333218639691866553206384714007138038691594287732934081410852761487999922797074320518605005374647219205312884015226747835223983423030251792941745800299484458323597491553884981698082835005441 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/shr_outputs b/evm/src/cpu/kernel/tests/bignum/test_data/shr_outputs deleted file mode 100644 index e332af0713..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/shr_outputs +++ /dev/null @@ -1,15 +0,0 @@ -0 -0 -10 -454 -633825298933523088827032272896 -170141183460469231731687303715884105727 -28948022309329048805675932369078530852631180780748809681045552446266006306816 -57896044618552785420117126879588554932077822571392166330260246162741804400640 -115791868408393044742463613008073883964789486131810247488688942285685300133887 -1636695303948070935006594848413799576108321023021532394741124202838125269764252555345402245353723668914435295328046362723087481814795090573542499024896 -2513963986864618028991213182224413308777819256816535800816685110210983818153373249015128547242150483479351682036176314425689224093547436995163496743174143 -6703903964971298549787012498933692735729379268964506370005391335664810416197917496264067057493203831336410710214053614378008173083437096682896442345390079 -13407807929942597099574024997867385471458758537929021698989751382546463062164957266305246601567593132153050553942622049229933835926543769603009459693551616 -5357543035931336009935361854186783933582024798567443984714671278144844546164446516476690789794669156301304623141706357119262681463344458016495802047428300170461025099293556031784389419661593571910118771797103228609036661178441218478227329403670559381682003857179616029683126706529221259939514687684608 -17537331055217019373813793980140428996762007940165414412037899012395481925281661101828540443292484630826575143659117691466180993189100488465242932412309241595039655786221608280976022359738682853284026484775740881283776485471294905189441462979676884558842493596567904678782738626435494445302790530494154545900458865343860245519486484115385763209447653125595848934876247827731869417098845586487640674418233066193999127785876419158080528284465214978523135 diff --git a/evm/src/cpu/kernel/tests/bignum/test_data/u128_inputs b/evm/src/cpu/kernel/tests/bignum/test_data/u128_inputs deleted file mode 100644 index ca67d6e7ae..0000000000 --- a/evm/src/cpu/kernel/tests/bignum/test_data/u128_inputs +++ /dev/null @@ -1,6 +0,0 @@ -0 -1 -21 -908 -1267650597867046177654064545792 -340282366920938463463374607431768211455 diff --git a/evm/src/cpu/kernel/tests/blake2_f.rs b/evm/src/cpu/kernel/tests/blake2_f.rs deleted file mode 100644 index 7d9349c7fd..0000000000 --- a/evm/src/cpu/kernel/tests/blake2_f.rs +++ /dev/null @@ -1,135 +0,0 @@ -use anyhow::Result; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, InterpreterMemoryInitialization, -}; -use crate::memory::segments::Segment::KernelGeneral; - -type ConvertedBlakeInputs = (u32, [u64; 8], [u64; 16], u64, u64, bool); - -fn reverse_bytes_u64(input: u64) -> u64 { - let mut result = 0; - for i in 0..8 { - result |= ((input >> (i * 8)) & 0xff) << ((7 - i) * 8); - } - result -} - -fn convert_input(input: &str) -> Result { - let rounds = u32::from_str_radix(&input[..8], 16).unwrap(); - - let mut h = [0u64; 8]; - for i in 0..8 { - h[i] = reverse_bytes_u64( - u64::from_str_radix(&input[8 + i * 16..8 + (i + 1) * 16], 16).unwrap(), - ); - } - - let mut m = [0u64; 16]; - for i in 0..16 { - m[i] = reverse_bytes_u64( - u64::from_str_radix(&input[136 + i * 16..136 + (i + 1) * 16], 16).unwrap(), - ); - } - - let t_0 = reverse_bytes_u64(u64::from_str_radix(&input[392..408], 16).unwrap()); - let t_1 = reverse_bytes_u64(u64::from_str_radix(&input[408..424], 16).unwrap()); - let flag = u8::from_str_radix(&input[424..426], 16).unwrap() != 0; - - Ok((rounds, h, m, t_0, t_1, flag)) -} - -fn convert_output(output: [u64; 8]) -> String { - output - .iter() - .map(|&x| format!("{:016x}", reverse_bytes_u64(x))) - .collect::>() - .join("") -} - -fn run_blake2_f( - rounds: u32, - h: [u64; 8], - m: [u64; 16], - t_0: u64, - t_1: u64, - flag: bool, -) -> Result<[u64; 8]> { - let mut stack = vec![]; - stack.push(rounds.into()); - stack.append(&mut h.iter().map(|&x| x.into()).collect()); - stack.append(&mut m.iter().map(|&x| x.into()).collect()); - stack.push(t_0.into()); - stack.push(t_1.into()); - stack.push(u8::from(flag).into()); - stack.push(0xDEADBEEFu32.into()); - - let interpreter_setup = InterpreterMemoryInitialization { - label: "blake2_f".to_string(), - stack, - segment: KernelGeneral, - memory: vec![], - }; - - let result = run_interpreter_with_memory::(interpreter_setup).unwrap(); - let mut hash = result.stack().to_vec(); - hash.reverse(); - - Ok(hash - .iter() - .map(|&x| x.low_u64()) - .collect::>() - .try_into() - .unwrap()) -} - -// Test data from EIP-152. - -fn test_blake2_f_eip(input: &str, output: &str) -> Result<()> { - let (rounds, h, m, t_0, t_1, flag) = convert_input(input).unwrap(); - let result = run_blake2_f(rounds, h, m, t_0, t_1, flag).unwrap(); - assert_eq!(convert_output(result), output); - Ok(()) -} - -#[test] -fn test_blake2_f_4() -> Result<()> { - test_blake2_f_eip( - "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", - ) -} - -#[test] -fn test_blake2_f_5() -> Result<()> { - test_blake2_f_eip( - "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", - ) -} - -#[test] -fn test_blake2_f_6() -> Result<()> { - test_blake2_f_eip( - "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", - "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", - ) -} - -#[test] -fn test_blake2_f_7() -> Result<()> { - test_blake2_f_eip( - "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", - ) -} - -#[ignore] -#[test] -fn test_blake2_f_8() -> Result<()> { - test_blake2_f_eip( - "ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615", - ) -} diff --git a/evm/src/cpu/kernel/tests/block_hash.rs b/evm/src/cpu/kernel/tests/block_hash.rs deleted file mode 100644 index 9c77951d63..0000000000 --- a/evm/src/cpu/kernel/tests/block_hash.rs +++ /dev/null @@ -1,130 +0,0 @@ -use anyhow::Result; -use ethereum_types::{H256, U256}; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn test_correct_block_hash() -> Result<()> { - let mut rng = rand::thread_rng(); - - let blockhash_label = KERNEL.global_labels["blockhash"]; - let retdest = 0xDEADBEEFu32.into(); - - let block_number: u8 = rng.gen(); - let initial_stack = vec![retdest, block_number.into()]; - - let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(blockhash_label, initial_stack); - interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); - interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); - interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, 256.into()); - interpreter.run()?; - - let result = interpreter.stack(); - assert_eq!( - result[0], hashes[block_number as usize], - "Resulting block hash {:?} different from expected hash {:?}", - result[0], hashes[block_number as usize] - ); - - Ok(()) -} - -#[test] -fn test_big_index_block_hash() -> Result<()> { - let mut rng = rand::thread_rng(); - - let blockhash_label = KERNEL.global_labels["blockhash"]; - let retdest = 0xDEADBEEFu32.into(); - let cur_block_number = 3; - let block_number: usize = rng.gen::() as usize; - let actual_block_number = block_number + cur_block_number; - let initial_stack = vec![retdest, actual_block_number.into()]; - - let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(blockhash_label, initial_stack); - interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); - interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); - interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); - interpreter.run()?; - - let result = interpreter.stack(); - assert_eq!( - result[0], - 0.into(), - "Resulting block hash {:?} different from expected hash {:?}", - result[0], - 0 - ); - - Ok(()) -} - -#[test] -fn test_small_index_block_hash() -> Result<()> { - let mut rng = rand::thread_rng(); - - let blockhash_label = KERNEL.global_labels["blockhash"]; - let retdest = 0xDEADBEEFu32.into(); - let cur_block_number = 512; - let block_number = rng.gen::() as usize; - let initial_stack = vec![retdest, block_number.into()]; - - let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(blockhash_label, initial_stack); - interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); - interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); - interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); - interpreter.run()?; - - let result = interpreter.stack(); - assert_eq!( - result[0], - 0.into(), - "Resulting block hash {:?} different from expected hash {:?}", - result[0], - 0 - ); - - Ok(()) -} - -#[test] -fn test_block_hash_with_overflow() -> Result<()> { - let blockhash_label = KERNEL.global_labels["blockhash"]; - let retdest = 0xDEADBEEFu32.into(); - let cur_block_number = 1; - let block_number = U256::MAX; - let initial_stack = vec![retdest, block_number]; - - let hashes: Vec = vec![U256::from_big_endian(&thread_rng().gen::().0); 257]; - - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(blockhash_label, initial_stack); - interpreter.set_memory_segment(Segment::BlockHashes, hashes[0..256].to_vec()); - interpreter.set_global_metadata_field(GlobalMetadata::BlockCurrentHash, hashes[256]); - interpreter.set_global_metadata_field(GlobalMetadata::BlockNumber, cur_block_number.into()); - interpreter.run()?; - - let result = interpreter.stack(); - assert_eq!( - result[0], - 0.into(), - "Resulting block hash {:?} different from expected hash {:?}", - result[0], - 0 - ); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/bls381.rs b/evm/src/cpu/kernel/tests/bls381.rs deleted file mode 100644 index 1ffa711505..0000000000 --- a/evm/src/cpu/kernel/tests/bls381.rs +++ /dev/null @@ -1,33 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::Rng; - -use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, InterpreterMemoryInitialization, -}; -use crate::extension_tower::{Fp2, Stack, BLS381}; -use crate::memory::segments::Segment::KernelGeneral; - -#[test] -fn test_bls_fp2_mul() -> Result<()> { - let mut rng = rand::thread_rng(); - let x: Fp2 = rng.gen::>(); - let y: Fp2 = rng.gen::>(); - - let mut stack = x.to_stack().to_vec(); - stack.extend(y.to_stack().to_vec()); - stack.push(U256::from(0xdeadbeefu32)); - let setup = InterpreterMemoryInitialization { - label: "mul_fp381_2".to_string(), - stack, - segment: KernelGeneral, - memory: vec![], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - let stack: Vec = interpreter.stack().iter().rev().cloned().collect(); - let output = Fp2::::from_stack(&stack); - - assert_eq!(output, x * y); - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/bn254.rs b/evm/src/cpu/kernel/tests/bn254.rs deleted file mode 100644 index efe2ed9f17..0000000000 --- a/evm/src/cpu/kernel/tests/bn254.rs +++ /dev/null @@ -1,253 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::Rng; - -use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, -}; -use crate::curve_pairings::{ - bn_final_exponent, bn_miller_loop, gen_bn_fp12_sparse, Curve, CyclicGroup, -}; -use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254}; -use crate::memory::segments::Segment::BnPairing; - -fn run_bn_mul_fp6(f: Fp6, g: Fp6, label: &str) -> Fp6 { - let mut stack = f.to_stack(); - if label == "mul_fp254_6" { - stack.extend(g.to_stack().to_vec()); - } - stack.push(U256::from(0xdeadbeefu32)); - let setup = InterpreterMemoryInitialization { - label: label.to_string(), - stack, - segment: BnPairing, - memory: vec![], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - let output: Vec = interpreter.stack().iter().rev().cloned().collect(); - Fp6::::from_stack(&output) -} - -#[test] -fn test_bn_mul_fp6() -> Result<()> { - let mut rng = rand::thread_rng(); - let f: Fp6 = rng.gen::>(); - let g: Fp6 = rng.gen::>(); - - let output_normal: Fp6 = run_bn_mul_fp6(f, g, "mul_fp254_6"); - let output_square: Fp6 = run_bn_mul_fp6(f, f, "square_fp254_6"); - - assert_eq!(output_normal, f * g); - assert_eq!(output_square, f * f); - - Ok(()) -} - -fn run_bn_mul_fp12(f: Fp12, g: Fp12, label: &str) -> Fp12 { - let in0: usize = 100; - let in1: usize = 112; - let out: usize = 124; - - let mut stack = vec![ - U256::from(in0), - U256::from(in1), - U256::from(out), - U256::from(0xdeadbeefu32), - ]; - if label == "square_fp254_12" { - stack.remove(0); - } - let setup = InterpreterMemoryInitialization { - label: label.to_string(), - stack, - segment: BnPairing, - memory: vec![(in0, f.to_stack().to_vec()), (in1, g.to_stack().to_vec())], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - let output = interpreter.extract_kernel_memory(BnPairing, out..out + 12); - Fp12::::from_stack(&output) -} - -#[test] -fn test_bn_mul_fp12() -> Result<()> { - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::>(); - let g: Fp12 = rng.gen::>(); - let h: Fp12 = gen_bn_fp12_sparse(&mut rng); - - let output_normal = run_bn_mul_fp12(f, g, "mul_fp254_12"); - let output_sparse = run_bn_mul_fp12(f, h, "mul_fp254_12_sparse"); - let output_square = run_bn_mul_fp12(f, f, "square_fp254_12"); - - assert_eq!(output_normal, f * g); - assert_eq!(output_sparse, f * h); - assert_eq!(output_square, f * f); - - Ok(()) -} - -fn run_bn_frob_fp6(n: usize, f: Fp6) -> Fp6 { - let setup = InterpreterMemoryInitialization { - label: format!("test_frob_fp254_6_{}", n), - stack: f.to_stack().to_vec(), - segment: BnPairing, - memory: vec![], - }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.stack().iter().rev().cloned().collect(); - Fp6::::from_stack(&output) -} - -#[test] -fn test_bn_frob_fp6() -> Result<()> { - let mut rng = rand::thread_rng(); - let f: Fp6 = rng.gen::>(); - for n in 1..4 { - let output = run_bn_frob_fp6(n, f); - assert_eq!(output, f.frob(n)); - } - Ok(()) -} - -fn run_bn_frob_fp12(f: Fp12, n: usize) -> Fp12 { - let ptr: usize = 100; - let setup = InterpreterMemoryInitialization { - label: format!("test_frob_fp254_12_{}", n), - stack: vec![U256::from(ptr)], - segment: BnPairing, - memory: vec![(ptr, f.to_stack().to_vec())], - }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); - Fp12::::from_stack(&output) -} - -#[test] -fn test_frob_fp12() -> Result<()> { - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::>(); - - for n in [1, 2, 3, 6] { - let output = run_bn_frob_fp12(f, n); - assert_eq!(output, f.frob(n)); - } - Ok(()) -} - -#[test] -fn test_bn_inv_fp12() -> Result<()> { - let ptr: usize = 100; - let inv: usize = 112; - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::>(); - - let setup = InterpreterMemoryInitialization { - label: "inv_fp254_12".to_string(), - stack: vec![U256::from(ptr), U256::from(inv), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, f.to_stack().to_vec())], - }; - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, inv..inv + 12); - let output = Fp12::::from_stack(&output); - - assert_eq!(output, f.inv()); - - Ok(()) -} - -#[test] -fn test_bn_final_exponent() -> Result<()> { - let ptr: usize = 100; - - let mut rng = rand::thread_rng(); - let f: Fp12 = rng.gen::>(); - - let setup = InterpreterMemoryInitialization { - label: "bn254_final_exponent".to_string(), - stack: vec![ - U256::zero(), - U256::zero(), - U256::from(ptr), - U256::from(0xdeadbeefu32), - ], - segment: BnPairing, - memory: vec![(ptr, f.to_stack().to_vec())], - }; - - let interpreter: Interpreter = run_interpreter_with_memory(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, ptr..ptr + 12); - let expected: Vec = bn_final_exponent(f).to_stack(); - - assert_eq!(output, expected); - - Ok(()) -} - -#[test] -fn test_bn_miller() -> Result<()> { - let ptr: usize = 100; - let out: usize = 106; - - let mut rng = rand::thread_rng(); - let p: Curve = rng.gen::>(); - let q: Curve> = rng.gen::>>(); - - let mut input = p.to_stack(); - input.extend(q.to_stack()); - - let setup = InterpreterMemoryInitialization { - label: "bn254_miller".to_string(), - stack: vec![U256::from(ptr), U256::from(out), U256::from(0xdeadbeefu32)], - segment: BnPairing, - memory: vec![(ptr, input)], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - let output: Vec = interpreter.extract_kernel_memory(BnPairing, out..out + 12); - let expected = bn_miller_loop(p, q).to_stack(); - - assert_eq!(output, expected); - - Ok(()) -} - -#[test] -fn test_bn_pairing() -> Result<()> { - let out: usize = 100; - let ptr: usize = 112; - - let mut rng = rand::thread_rng(); - let k: usize = rng.gen_range(1..10); - let mut acc: i32 = 0; - let mut input: Vec = vec![]; - for _ in 1..k { - let m: i32 = rng.gen_range(-8..8); - let n: i32 = rng.gen_range(-8..8); - acc -= m * n; - - let p: Curve = Curve::::int(m); - let q: Curve> = Curve::>::int(n); - input.extend(p.to_stack()); - input.extend(q.to_stack()); - } - let p: Curve = Curve::::int(acc); - let q: Curve> = Curve::>::GENERATOR; - input.extend(p.to_stack()); - input.extend(q.to_stack()); - - let setup = InterpreterMemoryInitialization { - label: "bn254_pairing".to_string(), - stack: vec![ - U256::from(k), - U256::from(ptr), - U256::from(out), - U256::from(0xdeadbeefu32), - ], - segment: BnPairing, - memory: vec![(ptr, input)], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - assert_eq!(interpreter.stack()[0], U256::one()); - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs deleted file mode 100644 index 4ee38e92c6..0000000000 --- a/evm/src/cpu/kernel/tests/core/access_lists.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::collections::HashSet; - -use anyhow::Result; -use ethereum_types::{Address, U256}; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata::{ - AccessedAddressesLen, AccessedStorageKeysLen, -}; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys}; -use crate::witness::memory::MemoryAddress; - -#[test] -fn test_insert_accessed_addresses() -> Result<()> { - let insert_accessed_addresses = KERNEL.global_labels["insert_accessed_addresses"]; - - let retaddr = 0xdeadbeefu32.into(); - let mut rng = thread_rng(); - let n = rng.gen_range(1..10); - let addresses = (0..n) - .map(|_| rng.gen::

()) - .collect::>() - .into_iter() - .collect::>(); - let addr_in_list = addresses[rng.gen_range(0..n)]; - let addr_not_in_list = rng.gen::
(); - assert!( - !addresses.contains(&addr_not_in_list), - "Cosmic luck or bad RNG?" - ); - - // Test for address already in list. - let initial_stack = vec![retaddr, U256::from(addr_in_list.0.as_slice())]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); - for i in 0..n { - let addr = U256::from(addresses[i].0.as_slice()); - interpreter - .generation_state - .memory - .set(MemoryAddress::new(0, AccessedAddresses, i), addr); - } - interpreter.generation_state.memory.set( - MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), - U256::from(n), - ); - interpreter.run()?; - assert_eq!(interpreter.stack(), &[U256::zero()]); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), - U256::from(n) - ); - - // Test for address not in list. - let initial_stack = vec![retaddr, U256::from(addr_not_in_list.0.as_slice())]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(insert_accessed_addresses, initial_stack); - for i in 0..n { - let addr = U256::from(addresses[i].0.as_slice()); - interpreter - .generation_state - .memory - .set(MemoryAddress::new(0, AccessedAddresses, i), addr); - } - interpreter.generation_state.memory.set( - MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), - U256::from(n), - ); - interpreter.run()?; - assert_eq!(interpreter.stack(), &[U256::one()]); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), - U256::from(n + 1) - ); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new(0, AccessedAddresses, n)), - U256::from(addr_not_in_list.0.as_slice()) - ); - - Ok(()) -} - -#[test] -fn test_insert_accessed_storage_keys() -> Result<()> { - let insert_accessed_storage_keys = KERNEL.global_labels["insert_accessed_storage_keys"]; - - let retaddr = 0xdeadbeefu32.into(); - let mut rng = thread_rng(); - let n = rng.gen_range(1..10); - let storage_keys = (0..n) - .map(|_| (rng.gen::
(), U256(rng.gen()), U256(rng.gen()))) - .collect::>() - .into_iter() - .collect::>(); - let storage_key_in_list = storage_keys[rng.gen_range(0..n)]; - let storage_key_not_in_list = (rng.gen::
(), U256(rng.gen()), U256(rng.gen())); - assert!( - !storage_keys.contains(&storage_key_not_in_list), - "Cosmic luck or bad RNG?" - ); - - // Test for storage key already in list. - let initial_stack = vec![ - retaddr, - storage_key_in_list.2, - storage_key_in_list.1, - U256::from(storage_key_in_list.0 .0.as_slice()), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); - for i in 0..n { - let addr = U256::from(storage_keys[i].0 .0.as_slice()); - interpreter - .generation_state - .memory - .set(MemoryAddress::new(0, AccessedStorageKeys, 3 * i), addr); - interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 1), - storage_keys[i].1, - ); - interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 2), - storage_keys[i].2, - ); - } - interpreter.generation_state.memory.set( - MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), - U256::from(3 * n), - ); - interpreter.run()?; - assert_eq!(interpreter.stack(), &[storage_key_in_list.2, U256::zero()]); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), - U256::from(3 * n) - ); - - // Test for storage key not in list. - let initial_stack = vec![ - retaddr, - storage_key_not_in_list.2, - storage_key_not_in_list.1, - U256::from(storage_key_not_in_list.0 .0.as_slice()), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(insert_accessed_storage_keys, initial_stack); - for i in 0..n { - let addr = U256::from(storage_keys[i].0 .0.as_slice()); - interpreter - .generation_state - .memory - .set(MemoryAddress::new(0, AccessedStorageKeys, 3 * i), addr); - interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 1), - storage_keys[i].1, - ); - interpreter.generation_state.memory.set( - MemoryAddress::new(0, AccessedStorageKeys, 3 * i + 2), - storage_keys[i].2, - ); - } - interpreter.generation_state.memory.set( - MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), - U256::from(3 * n), - ); - interpreter.run()?; - assert_eq!( - interpreter.stack(), - &[storage_key_not_in_list.2, U256::one()] - ); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), - U256::from(3 * (n + 1)) - ); - assert_eq!( - interpreter - .generation_state - .memory - .get(MemoryAddress::new(0, AccessedStorageKeys, 3 * n,)), - U256::from(storage_key_not_in_list.0 .0.as_slice()) - ); - assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - AccessedStorageKeys, - 3 * n + 1, - )), - storage_key_not_in_list.1 - ); - assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - AccessedStorageKeys, - 3 * n + 2, - )), - storage_key_not_in_list.2 - ); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs deleted file mode 100644 index 339e2182ef..0000000000 --- a/evm/src/cpu/kernel/tests/core/create_addresses.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use ethereum_types::{H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; - -#[test] -fn test_get_create_address() -> Result<()> { - let get_create_address = KERNEL.global_labels["get_create_address"]; - - // This is copied from OpenEthereum's `test_contract_address`. - let retaddr = 0xdeadbeefu32.into(); - let nonce = 88.into(); - let sender = U256::from_big_endian(&hex!("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")); - let expected_addr = U256::from_big_endian(&hex!("3f09c73a5ed19289fb9bdc72f1742566df146f56")); - - let initial_stack = vec![retaddr, nonce, sender]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(get_create_address, initial_stack); - interpreter.run()?; - - assert_eq!(interpreter.stack(), &[expected_addr]); - - Ok(()) -} - -struct Create2TestCase { - code_hash: H256, - salt: U256, - sender: U256, - expected_addr: U256, -} - -/// Taken from https://eips.ethereum.org/EIPS/eip-1014 -fn create2_test_cases() -> Vec { - vec![ - Create2TestCase { - code_hash: keccak(hex!("00")), - salt: U256::zero(), - sender: U256::zero(), - expected_addr: U256::from_str("0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("00")), - salt: U256::zero(), - sender: U256::from_str("0xdeadbeef00000000000000000000000000000000").unwrap(), - expected_addr: U256::from_str("0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("00")), - salt: U256::from_str( - "0x000000000000000000000000feed000000000000000000000000000000000000", - ) - .unwrap(), - sender: U256::from_str("0xdeadbeef00000000000000000000000000000000").unwrap(), - expected_addr: U256::from_str("0xD04116cDd17beBE565EB2422F2497E06cC1C9833").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("deadbeef")), - salt: U256::zero(), - sender: U256::zero(), - expected_addr: U256::from_str("0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("deadbeef")), - salt: U256::from_str( - "0x00000000000000000000000000000000000000000000000000000000cafebabe", - ) - .unwrap(), - sender: U256::from_str("0x00000000000000000000000000000000deadbeef").unwrap(), - expected_addr: U256::from_str("0x60f3f640a8508fC6a86d45DF051962668E1e8AC7").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")), - salt: U256::from_str( - "0x00000000000000000000000000000000000000000000000000000000cafebabe", - ) - .unwrap(), - sender: U256::from_str("0x00000000000000000000000000000000deadbeef").unwrap(), - expected_addr: U256::from_str("0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C").unwrap(), - }, - Create2TestCase { - code_hash: keccak(hex!("")), - salt: U256::zero(), - sender: U256::zero(), - expected_addr: U256::from_str("0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0").unwrap(), - }, - ] -} - -#[test] -fn test_get_create2_address() -> Result<()> { - let get_create2_address = KERNEL.global_labels["get_create2_address"]; - - let retaddr = 0xdeadbeefu32.into(); - - for Create2TestCase { - code_hash, - salt, - sender, - expected_addr, - } in create2_test_cases() - { - let initial_stack = vec![retaddr, salt, U256::from(code_hash.0), sender]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(get_create2_address, initial_stack); - interpreter.run()?; - - assert_eq!(interpreter.stack(), &[expected_addr]); - } - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs b/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs deleted file mode 100644 index ee9db0dfe2..0000000000 --- a/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs +++ /dev/null @@ -1,33 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; -use crate::cpu::kernel::interpreter::Interpreter; - -const GAS_TX: u32 = 21_000; -const GAS_TXCREATE: u32 = 32_000; - -#[test] -fn test_intrinsic_gas() -> Result<()> { - let intrinsic_gas = KERNEL.global_labels["intrinsic_gas"]; - - // Contract creation transaction. - let initial_stack = vec![0xdeadbeefu32.into()]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(intrinsic_gas, initial_stack.clone()); - interpreter.set_global_metadata_field(GlobalMetadata::ContractCreation, U256::one()); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![(GAS_TX + GAS_TXCREATE).into()]); - - // Message transaction. - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(intrinsic_gas, initial_stack); - interpreter.set_txn_field(NormalizedTxnField::To, 123.into()); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![GAS_TX.into()]); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs deleted file mode 100644 index 7923997d7a..0000000000 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::collections::{BTreeSet, HashMap}; - -use anyhow::Result; -use ethereum_types::U256; -use itertools::Itertools; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -use crate::witness::operation::CONTEXT_SCALING_FACTOR; - -#[test] -fn test_jumpdest_analysis() -> Result<()> { - // By default the interpreter will skip jumpdest analysis asm and compute - // the jumpdest table bits natively. We avoid that starting 1 line after - // performing the missing first PROVER_INPUT "by hand" - let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"] + 1; - const CONTEXT: usize = 3; // arbitrary - - let add = get_opcode("ADD"); - let push2 = get_push_opcode(2); - let jumpdest = get_opcode("JUMPDEST"); - - #[rustfmt::skip] - let mut code: Vec = vec![ - add, - jumpdest, - push2, - jumpdest, // part of PUSH2 - jumpdest, // part of PUSH2 - jumpdest, - add, - jumpdest, - ]; - code.extend( - (0..32) - .rev() - .map(get_push_opcode) - .chain(std::iter::once(jumpdest)), - ); - - let mut jumpdest_bits = vec![false, true, false, false, false, true, false, true]; - // Add 32 falses and 1 true - jumpdest_bits.extend( - std::iter::repeat(false) - .take(32) - .chain(std::iter::once(true)), - ); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(jumpdest_analysis, vec![]); - let code_len = code.len(); - - interpreter.set_code(CONTEXT, code); - interpreter.set_jumpdest_analysis_inputs(HashMap::from([( - 3, - BTreeSet::from_iter( - jumpdest_bits - .iter() - .enumerate() - .filter(|&(_, &x)| x) - .map(|(i, _)| i), - ), - )])); - - // The `set_jumpdest_analysis_inputs` method is never used. - assert_eq!( - interpreter.generation_state.jumpdest_table, - // Context 3 has jumpdest 1, 5, 7. All have proof 0 and hence - // the list [proof_0, jumpdest_0, ... ] is [0, 1, 0, 5, 0, 7, 8, 40] - Some(HashMap::from([(3, vec![0, 1, 0, 5, 0, 7, 8, 40])])) - ); - - // Run jumpdest analysis with context = 3 - interpreter.generation_state.registers.context = CONTEXT; - interpreter.push(0xDEADBEEFu32.into()); - interpreter.push(code_len.into()); - interpreter.push(U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR); - - // We need to manually pop the jumpdest_table and push its value on the top of the stack - interpreter - .generation_state - .jumpdest_table - .as_mut() - .unwrap() - .get_mut(&CONTEXT) - .unwrap() - .pop(); - interpreter.push(U256::one()); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![]); - - assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(CONTEXT)); - - Ok(()) -} - -#[test] -fn test_packed_verification() -> Result<()> { - let write_table_if_jumpdest = KERNEL.global_labels["write_table_if_jumpdest"]; - const CONTEXT: usize = 3; // arbitrary - - let add = get_opcode("ADD"); - let jumpdest = get_opcode("JUMPDEST"); - - let mut code: Vec = std::iter::once(add) - .chain( - (0..=31) - .rev() - .map(get_push_opcode) - .chain(std::iter::once(jumpdest)), - ) - .collect(); - - let jumpdest_bits: Vec = std::iter::repeat(false) - .take(33) - .chain(std::iter::once(true)) - .collect(); - - // Contract creation transaction. - let initial_stack = vec![ - 0xDEADBEEFu32.into(), - U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, - 33.into(), - U256::one(), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(write_table_if_jumpdest, initial_stack.clone()); - interpreter.set_code(CONTEXT, code.clone()); - interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); - - interpreter.run()?; - - assert_eq!(jumpdest_bits, interpreter.get_jumpdest_bits(CONTEXT)); - - // If we add 1 to each opcode the jumpdest at position 32 is never a valid jumpdest - for i in 1..=32 { - code[i] += 1; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(write_table_if_jumpdest, initial_stack.clone()); - interpreter.set_code(CONTEXT, code.clone()); - interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])])); - - interpreter.run()?; - - assert!(interpreter.get_jumpdest_bits(CONTEXT).is_empty()); - - code[i] -= 1; - } - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/core/mod.rs b/evm/src/cpu/kernel/tests/core/mod.rs deleted file mode 100644 index 8d71051c41..0000000000 --- a/evm/src/cpu/kernel/tests/core/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod access_lists; -mod create_addresses; -mod intrinsic_gas; -mod jumpdest_analysis; diff --git a/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data b/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data deleted file mode 100644 index db38ac8c80..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/bn_glv_test_data +++ /dev/null @@ -1,1049 +0,0 @@ -// Sage code to reproduce this: -// ```sage -// p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 -// F = GF(p) -// E = EllipticCurve(F, [0, 3]) -// q = E.order() -// SF = GF(q) -// -// P = E.random_point() -// s = 0xb3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd -// beta = 0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe -// -// # a1 = 64502973549206556628585045361533709077 -// # a2 = 367917413016453100223835821029139468248 -// b2 = 0x89d3256894d213e3 -// b1 = 0x30644e72e131a029b85045b68181585cb8e665ff8b011694c1d039a872b0eed9 -// b1 = -0x6f4d8248eeb859fc8211bbeb7d4f1128 -// -// g1 = -0x24ccef014a773d2cf7a7bd9d4391eb18d -// g2 = 0x2d91d232ec7e0b3d7 -// -// def decomp(k): -// c1 = (g2 * k) >> 256 -// c2 = -(-(g1 * k) >> 256) -// -// q1 = c1 * b1 -// q2 = c2 * b2 -// -// k2 = q2 - q1 -// k2L = (s*k2)%q -// k1 = k - k2L -// return k1, -k2 -// -// f = open('bnout', 'w') -// for i in range(1000): -// k = randint(0, 1<<256) % q -// k1, k2 = decomp(k) -// if k2 < 0: -// f.write(f"{k} 1 {k1} {-k2}\n") -// else: -// f.write(f"{k} 0 {k1} {k2}\n") -// assert k1 > 0 -// assert k1 < 1<<127 -// assert abs(k2) < 1<<127 -// assert (k1 - s*k2)%q == k -// -// f.close() -// ```sage -// -2013903480656938991561360820573915418551322753594945075887137499154548905374 0 1882462671847353709795309622886516129 42967685287677743822535440511395041270 -1268423676977918116861975711668021910978037329160197471654664118597257530440 0 34025934268629390987380972573718725934 9100169853444546116471837216426965693 -6668508633972199535163357076801579139221271059568834560363130537981332354695 0 79749746257933558980012311973715305501 116307227499605143480534356082216636552 -17768755383659786872958372620209327055213100458468309610137832646098313388946 0 90492014861931952583240494324496011409 17098585553523121578298898637157199310 -139915108452039320442032675295009888921418533694625463999489248643447547676 0 130863349746563396365758476405085169156 41092388482931584351521374815106346033 -13128053102434855341416433272324981024795488352499750050774381107936031805518 0 111993216900539341442706137325818807666 137565870375219228049024477065823682246 -1702057401067803897344460009125950084354506608147461765081028590098722533147 0 89320927675710536555702924851905599673 57402933744947499056148386684720925644 -17005695837813689466883871391635184470112639313859656064329991355435674991930 0 116398947678020711160080424096010423750 108747437388035888582963622260003883653 -7086100296664440681555493186326798897740340267847656106744873676028945469395 0 96972415532060635551093959933438242603 23257729070477712317114923418552439671 -12338378408039930925830626640419058948417759069530323857857041896165107321772 0 108739618626932594868239374750975886648 93989709738727518510089540785140463212 -16074868467455275153656399682320783657344238462740245950989214767804285156465 0 124945325095228305422526147528118621991 131043774062590891741685091793265303217 -19195889723945246569833633488811643062395336305972323797307038899519949045674 0 91406940890735908818407540652368068077 22946665784711399034024095613215284843 -16878059357100929177394086610398884126944484626015801518271668980848753530118 0 158685935518915245640238947169032886610 134793215726165740486246702952287496441 -8325603801922742344078036768480191507441897100827441324946084397116617032 0 41490138803893230607633049416882226619 124673980134955039118584382178102696778 -9325883179127897386123774699052925144305338736647350470583133656939432200831 0 139931126646594491806914429391920585426 78225769979522689473266770343704109358 -12501089776369792137119969478904813552547470850596243618311747107167923871567 0 13738337333063011247408533974722991257 89607995746647237436128016511595360803 -13327709821112824364418761072674816333107719040597511409847659950586323405211 0 41359556861476606716468299725809967559 24779620677574801712087901097075555658 -10072034493273912357208008082234894141200648132501530946575291007116504474912 0 76781485796521395175099070381733460818 54003671595202200644291460122856371574 -12415707277543767503114740191518652765197283788755102445275521613377410029393 0 135753421486585699744195544219845252608 150841019132250760761613924944579333486 -4104241302194832202060868109825893266856089460759866147130306967207840490353 0 72632096389954696026051518267652621825 58311233169600549592501119543441840024 -15641193331141795500404920739318637702175291243746414267137813367649672781597 0 106221019150617401856867137784525615810 129892506056817991810573577291747352408 -816554620499999456760417653930347998485540936848720176009878112766956695282 0 42578171369173589216953618502787776664 69985919394579153590243135093861734968 -3618999731369743698037020169549086621735062715757041260202007626249856285140 0 70479413959604976938400136995735249077 92018284790473177743612029079885903592 -8282392752418738514738037266717884679552851530383021352949808777489106022741 0 59083605669051103901981945167708559129 129434131571940229941736941249681818611 -21096192386638039916785050771127398604333119795524445061361888861891075829734 0 79534567798130056474033051034989262504 142946613134803093195740442669810857018 -10101498357252415148502309917342751131398582557621135785780805133283261024792 0 44906858186234193843102296541527217304 59552430467714119585567120582577203775 -20112769534491828475443523184517183213350315644637000504826261991625160232783 0 30234675555615032630119660875540291761 92045956112742459930398599086356266320 -8639311409233893057930501182535689195828478336034331066587279382603140380739 0 14245908125083619550479629450177117702 86437021859600078711727011527677011621 -20737815980509591915939868090463649964873906948333953535316209463938421468705 0 24312039971287035889348447665361865041 158277629640412196848585265468369025037 -6197227419391822835076932637236297541290374789786376326423788662479152361519 0 76496967785165538463245128754723851558 95440788607075906560233133330659767073 -3507837857872618399440486221614100493560628544763221937027684571500846183502 0 41672597601356600184765766690700743898 117834431255450129291931344965405662590 -6720268116387614937034135177878569264237612293675442003174314662967937918785 0 94615070668075626786314506076954480195 33298452450813520294111985372067233343 -14381517248617759701593246607434622490677928737243562728457392454760929038372 0 85057647397170897262749171627080226797 11104452434052993064625158649147121451 -18954942053065263623163292291400812710941942380493557036867148924847683221037 0 116098565261915536859058214311956521496 107095647256320549952063177261127403384 -21245964711735305595085252100217757249100856147266220418669463943934469595130 0 89425554484564370645878058721897145904 133621090946116450164920821147036035676 -13855299627765189646426405419566467616787864634288548853387691880571357087369 0 129244261991663500678156375006569763204 23752809976752496066838187903227429081 -1822456584591336401017566069777014434419918310655791129780834422964576129637 0 32456389978783012215128545102555515113 49705974297727806054407959930778302884 -4242438794619008401291747736749432956107493618360325518596771015869397733374 0 23029258932599814890835391781825046572 89928059378813183298112746592689937319 -10710422245046232407168773592562830140888090033262640488839808950006259544048 0 20650005017952450165831427782919607521 17165572506712865129404111243666588898 -17788715985715636563000364448397908306199762866088859004364143176771982087214 0 66622507473979419553078178615485935316 148529310638469314645168859680663064154 -3377122939761792234122582696557032775007463517455574762776743916285544123113 0 111119740153320066061492788749667346493 22352679708662289197049477394263721555 -12770430942224004163887551725370542426656410602013908480168182825982574689534 0 93167006826122426252651851223571175062 149404785261504123950278206323102677923 -7914653125787114558556628418337662338669149259374219600811623019425744653392 0 41614548541415479621814517623833673714 74102977816367027728407106648858462248 -4855096168810305874154876410714123250949331990652068276527783418282158099378 0 61268271483078069249586798875958432219 6132364793062250794328123733082453691 -20862295479185535454366605523434105664116787544991207265552468533663403802641 0 59324675913770502986160223082594712677 132262198727986221695414173190980825522 -2432825820738285014056108364985748494713416241803005743649341312088498782766 0 30926822802637295777997499494191035930 92930397505363243590184937249135421332 -3086882891195079898104693558067544064689205710993459505482475246268331375135 0 19415751936365368220330060983112386764 21301207598482446250120145862542662972 -21020982200787130600925362703307382385477354307977468699485849855902476380854 0 99915419306844447307629095766691709425 67677588612254889343094551392956214295 -4001162074869575919138545291349837940721221168567182801370488685679145732119 0 112159988509949591181337894096592297549 96781312402450425354695542721635138394 -19424443275625802349749801394308225887795876978437482453151058760268602915125 0 81992891475808402974753876117857285441 92807876163894776274283067777560148243 -12356743738820508242869888992307044238455072028894484377315646327878524263805 0 77340846141437284070413915040903809532 55374150923287050299252352919895826513 -2816385703326540570060286639731723051637350659698794116445514687609233841562 0 98631533884964190926678125293065045565 59060370337134406756988948331354493466 -13679157396045672380204462197779227102178042225281356906927108324872331939937 0 153190639553726436515397063911822794726 134275143240074016056088210745284305065 -3534196524445723531846721744271446821648809697911317317272675137581691519546 0 56135160407783528244729065000605092715 17135730127830633383884883479931316765 -12857120393899251392664989154218479040197569694511349074262609952837607018852 0 36182320363689350492888780094353655621 85360177004210031599090920438480444333 -5069930643992342169771113434177678776838563613391697656781752379441172420408 0 103514174353199457699741123891662506342 67007682662069148782539844186855043961 -14236578055609805328341699966689023934313002726810699771608026708054661884878 0 29282622597492291931007555885098587384 39540551682545274426631996207899396257 -19559647535094692172647026727995170440442113211517315088606828511749336396891 0 36902252623096012269615736083636098314 129736364315867474600842169108607990931 -7492011590682813368947695242427005238460366670527007463701988535794831394720 0 108846454110272925206443119671691144702 145005988668085801504645320000090688291 -21694378248444338899242669118017467976309325472573244213105009910765238984288 0 141446042240176980349849770617330746172 89687245217393378864582176933416791087 -2022596022619437019490512858644223367393425728900892125939613273692738941049 0 33244535586513888755662388243785383077 25201212084910399522503678041217895565 -7443033656318178261624920923436283069218216637101619105369519917441800822169 0 117599034828781259783879514980170263285 4682244129366517672418147440411932120 -7674116104317128246243320006794844428457818538130243350023487951014925229451 0 99218841949086303169787314623069596589 147497643195573215285131901454286457034 -9454801294389857964873083905510975414996396481509063825764904652566507872940 0 19613186410261907049224966165884894925 76162987653417149048693633230383125776 -14427220640368555004153228601879953450107119825054957434025085192822814682429 0 27301384580836782816166396172425589741 30579952858711227751335056852869278393 -3626548879026433462459900323997219792597298762481157476567669947854725865034 0 69675686238789743868824091216979465171 75410807058380994737491068757588482255 -11484484326389654617630911458134798137517430692393929013668086709387963575405 0 84608001613363167506601048264537178457 140728442440839098169237413212370763898 -15327616190270804643968916167286892383621114779418019823126786886534724918552 0 18853044112394421051519948350411128282 16630645622232611349445200516451385272 -4066560358596882333579077316185067203375715547202265984757269762002429479915 0 132169671792307462476951171913567615566 124695614572693011495261695679187261793 -21622253506052368019563094489171363215029688777987462164016204506457047215410 0 81621331257848532444093970395282872350 47735454593038525228842454508518064756 -14673804697197050495686980927552760145316485913113403342712155660850922867161 0 97246858732616386439371494674637975736 97022608481548679851725319147656928135 -21648477644381130139022330392374747838821377951749456929882678573452872385459 0 37947203105261458064597004197150944466 80244718662459233139129624852285665635 -2447162147146976942610673124277772567734721323746017065456655370861430468054 0 144081914084468380293152626129836250004 129896001297238730075743827259748585451 -11243079166886829275820162795129236214428078266373885635009680831722843532930 0 103046553265162575613009303411421489078 23870983078488047184971744985117562736 -4445823807351183366945810664078605505745130495731399069776677343628516782959 0 20441637978714792313465835468524049456 144209327558546751806619925462393613402 -11567009681447050078017230575723103947493147810213653158268424203516034460512 0 48814298387192682745019884465601961687 104802001882621035189083610946773312461 -1425301607538293493802098468662327057988543750237085016588949378148626915323 0 86363832869193776028503242537913987204 95217995470718347044258619588025881789 -11070730452754177189791028544498934892810140199367008239627643228442543391269 0 155433320629549897332039105088934604083 58246184262613812388239965768891128519 -1423800753123844555623243115266520025893183388993975198691940843592474213925 0 136211659881183678135360841644795687471 147664878955414491561374341712050409794 -19978948423543790393608023292769760846180525023567354283212408907371396253245 0 159820297882067231703309873192443912164 105543377670290750689670247500926843994 -16153906840466643595056891629991018985299298466802827953938625367030205206698 0 154345389632998521513275970774947808926 28231654744301426899418123204257367229 -6351060107210141996129655144251706738309013727154833137060657876282153665656 0 8481046837054026056102147849708977620 103579595522851589326703388203161688479 -17468993932379690821016230740682655515164049469071024953348819549348625460362 0 109606600218454276515315057401174169430 44572594394908953847168098729945926357 -13397118011889355828031153891922369549839729713728181794537464183077040733792 0 86066135209603853473278981105646245940 143651354857918178303858551292988460215 -10956750739069221792200690046716838678898580501288832531130436262834486735572 0 62373052908445012073819011909555689487 35765782914477382989685090244901120782 -1244372474953766360583406906056258609906572494371767680831922303833601534135 0 67877803276831362783594984553572015648 72806657139353526301469142035519317405 -9422168168141646719197670065216458629473162716609125349048345887058150845335 0 126800617368060694681187251760162221598 32357210355825736284356087866665091242 -19747162394569586270540438228898464487134416049591358133130768840309999729495 0 22543721318635409339925968656779506907 148252336790362709183708090087313006176 -5774930904406054791732748257640549439037002427300521812075937968536777761611 0 129381728211979557062678642540996754136 111249011397445827206839947853986433282 -7869916380173247517156869041218342357997711325349874926110057244674574131909 0 131992721832302066607856323412916443818 16557417837910070673237009316717923331 -4474419926085223123810227499188667218966777367246218027620622550266381529509 0 101471270887218562681633364079092583940 10991361027276362368935256945740457778 -8542092826488922515152759017773231352750685105366129500128361269416272895348 0 137131837098860123484221471405180191649 53516084093595345655540752022100903969 -9199160124401302999190901020445526779293315344151384707849997595172739510363 0 102114291447143807615543445924175069494 83207061658716581862448524803652899992 -715246366928472824274187536457666677306633451880904218285233056803317616452 0 146216816488882317280346530046335798266 17962724980664094210671326130289900146 -15568824431016318695805352863265380489535684296488072833284765474077864623800 0 49645051037231964605284493743277689787 149604627341800180528969216929243839441 -1338867713698862065830761550370451274345785283413167833665762376498251436330 0 99212656197142454418481439965551861040 30884101454839991087998997618331596299 -17004820518282227105473119377761200367961298370037693850930064323936272579567 0 82588829328088937942436878278665990845 86156285119703754608436632557963004850 -16179760030090990028423956433770305788678458424105383838296980315187679563593 0 59636846357127821751591712202707705584 120829523793409329365364894220330768305 -6924708397966391644249530186799483394531630412723339847178972624473811796449 0 72278963410177471723404715683554764530 61322485086527826835900701155231171449 -16141849849016152886520880342219593082003007394148777508347929520080975302345 0 63210380762203176431008311576053660106 149810354430835714723024054326218926894 -9771827212340220336727480154734885585015291830607762588007070095666537103962 0 127315397384500630876510844690798453878 92552057065340187822463548165472431488 -9597646247006951895316265355240166469160406794212290526173803074233451924365 0 55146595698821253681311846837758645989 148310238810928113863301066648837453457 -8465848373442502380818243577280492179253105498594334720569681107869854730077 0 133451101048113592055683350499161563860 113814297572446544749603286922880855342 -16673653132857000928380275544635001111469936779348716678450636691093203696516 0 69687063336547937332693262189647635722 47028826852068083201823677597655630363 -6439181605556652858268027192998705119180395119385479569188815638889386510761 0 126138670378803625345448506998329160855 107944646789713346674164346055547107902 -20056058673653006507705533571250129762879103494772935697655099346528884534802 0 92413287522364558449265158799874786435 45090575593929323976899713538215218639 -9314553185445691038796152671593920186670629617007841115423653025024535280928 0 111563319160310361226696462105863433466 34369942960198135839478071371124608615 -4927675434407010434192908778773106727870672086930323927970327117311926375034 0 137349766657253194323844974907461417306 37228664802908822292018829815462857557 -9264891767971107361061286327073412219579237133400928397329691112742202125433 0 133504748080119742812332049899559380041 106876024359098608208959709090695091078 -21239035922607569282561439017160081658703303219193009803256765527243104507605 0 127941568848838500748177750257074188988 132638228075569925032547445468233451964 -12348842325917196668770715357329540713732012334626978860888026446015650716154 0 59921368473223641826414036215286175621 103676925947245928210808006140329563943 -2219753737768880444965101007895419042638758013618081774117535009674221177484 0 131105321366147001564210323283626409396 33844118733696235679416574262791596992 -7194878590710675804853640127699788077347439261881193512253497408962266483500 0 16895890533395261054793594838090039342 72879958967130103621501661973489085960 -3552107302046736728860832826845367692507127957155289383325418083014523965586 0 81762797647997437598834018510547229721 66390684988940248209820729176774785551 -8097339585552837075097398405090059521094315234009487252835576343329440746629 0 23438106175967728802564576995787850318 53665161244209246105725166303407116873 -11054655230957830463788298366001221770970021210219187490726792616837987212525 0 52969092383179458327199369831696866675 67805529082176385107890148118165476234 -9064866667960745373257820646534360448388547323092276681589928695163353091040 0 99172726408657783804074683913325358988 68803942570330168616946756775615332981 -4085335107483456228711824671996457880786468675151301012841509325129606692118 0 5076874654040630438377355836550074866 53291134791444064409764799175732009788 -21545893280878247711733355769933082259098699652736605062388906457727183460662 0 89925294926152795057540681496719378171 55015642404984501503284615922403722966 -10994109279014726151842434916489679456280661131923745661485392437996493913246 0 40547872343875397689098153888030255971 128789524655963197301799356621798437472 -325328659367328808788528141420775240975342055043215457323093425616834994203 0 136676199889783899158325735369127301515 55474496592456017618488940429395093152 -1135557847988974470521207430537173999026125032166403865318345875605053812381 0 86785193037007706895442557835049074939 92220067422604656439096665435019721725 -15977032490864705745048699247093657931474080983191721393678655457949976448786 0 39375567166348018555873146433030444598 74750171233387550707521181505447136074 -361326910890375200661671650877500479008126344742210328486200419794238345671 0 123426060461948520366049381891582168107 83879919682805792996172598821245711828 -19559677070376545258905222496646144115978499302755825275723760316912331777874 0 36614334297150401183780374760562601762 133009903579894460388360807537806661430 -17488700737364198621697992155588754947512702189285299503356183327277722525843 0 65125450092976248924289549654823049956 25011646935153146668371829156662937936 -871566286944860963216464569539630013233131214898555558844869278576972512997 0 120056929924226259891435240777202595511 139235896101896705020091270111475935449 -19105499698699079805644179149269446118098430827893722479344973101197228438363 0 93449295397218795445004481890546637061 136990983915308940190324823208183792349 -12639536535918800122289087078776776207605580060505736102546974173097053447364 0 142368542641361777919497490495935835174 28930341887514949962677588934626556449 -12941689993532443291244234609289112374938850820091441614232177967544669454044 0 137787613283925954815035625600536083953 27591979204107365233094501816618965243 -13672488782862781255107443104968919282980607748905063766797107010370672505331 0 154503881568772565690679238142114314013 63508563917623905754560191181191738514 -17852038973435952038895908568340877955977640810087574953315125738034752334458 0 99700659722539029108779794443086474996 94431444547030296024548665008418709106 -16196707856201816307274184588330147042888967150448811965184354608079542574020 0 20624707822164422750822321035728161150 108253928629329297740560957460014155975 -10836657133379777240757002076404099521651607039779879821958228141700213040553 0 64955586626317456220191651896243075218 9614008823427608906681785649859750566 -9357160088593123992492745000226668912811073852317238647741225976146353332509 0 48914721174081175180330017564915057789 53505141990047464806651804181921773215 -10360859978912045230352351273197155421194605867225882119905445052651267237551 0 14679723065688440073942069799467063945 136903159801119617680346897228747122098 -17184195440054036227577403120957521856113627638309781147051386338709387931169 0 143084428086244789626501508212920637909 142766228648017924039211953735545318321 -5231671032826216013588041224816763328582354679502539534279382806972232153167 0 76549485174311203286822433333421195070 35008100149831898286258728448541216257 -18013923370691199448498119777195948527748550463339082266178599105959358158376 0 48937553643084584236495740868570428331 108570180185106335740010338445455244126 -2594023029665829426587507339466964716711832469219245654266497378245336724346 0 87176374581060978456038611063509252295 145075871079989610818627169418081490383 -15813880012217868472290885007755667283331172093500805962233835930047767010022 0 76341518754716952090643131237821699243 152841098823094172639843790719987012581 -11030007264506472133522047405214043268610700398183287187077054115104162031768 0 98539670161180354555385477603628449848 138866820075145778256030927513738716292 -5014344856429909238620696167627272156826126385672949387292660503471628455219 0 129205907403882026619518219249529467483 111842048237692303178353120976220464395 -21818397386381224383948944981064366738399732753159855194595213680679460637867 0 112898324837235896605205694473102896483 137844457667770529012912860678760032064 -4663573950204524768697374423110746710987068952403394719791539296704222233806 0 14693157305907829718141729111704389050 22736661864953488365655403288488629519 -10584176581322903196813350291876257301583814727864548713487127908733928647464 0 67107044846095716384663788649683494183 57111129590606090187151414525406121749 -18255192976839660952468262300841295115759668865507919120755139987281153550778 0 111072422607371374756296101045564022052 71843789446340993341908873402752004813 -2710869776800316374088237234669294401279430007950731893989140872786742497414 0 87283101401926982181563218843134645912 96965739153919797829544047162883029501 -16785558509260211396947085843619419681952604668717979195480607704849905770154 0 86574180818726941322334354625353221467 87183348994808222308006352488206993599 -18839498282802519756597954051334217801364762082759004229253520771452128229415 0 143245983752233963700458040686136053363 68458052618644111102134214205446966928 -15321017192736180615652525436821239856783979404748589944860809432708136971981 0 67125350214635324755034124561299868967 61279512469407048522813428237701085623 -13256401208762650391365575054332774615021824535162043517287474411704977777645 0 50307356023239464609626553529823816528 84489883308087183462144020245452058306 -21173414008041660741030363315170044730457160654891953005433375589718429239293 0 33863353291716309359512256524750995706 56644407163298346364898119347030825830 -10804639560060734993192884844123818828687595344163800515137302264634995209564 0 143328589441682921677398212504101530990 74444808071053277603300274430414203178 -5348205434817893470868030296569679878932077399532666795366269319646606651848 0 137851272484841815757512929697430218187 146523028008947142326880627325452380300 -19530603536932206465939490881275193096540470101958038201676457215044716225340 0 43694319418705769826227275662625147398 82855275197553782054071196799143405835 -5300535386661996315555957045335806017402338796526851211447184225712197735724 0 75027690019928158426796400753158303324 130317535920960243743711796423266337046 -10665932148295832385949641619158716103034021992020147985355072030986254326307 0 38263589710219153568382460239045332496 153460984846029144109557902578896169089 -10904459941646047087376769753596577310025828845632928273246834149347523729548 0 20939290740636140754111417361113398984 104738760930598289897223308451374976729 -16354713404570837199224819482664979719184288151272191810228986048981588059397 0 89994292772087346468465093391835886377 39734310302482244762473407624815510266 -8231085457425517431202832617060508575716190315059566300157109014010660452680 0 97488246584892977583873659379571620811 74114572896660693743309816516270465417 -1537687776979058006958206957941601847593037098103588991748885547964839919261 0 120059448064698830627561646950784031124 39036398033966117300204938295563013619 -17902796865730477230830386688771948667084601899486197156944880685165740067290 0 28652188414502771591426890410376220085 89893506380868841016392954870943668768 -5332223431391826475478951108136188762115993683370336975195041131627493058290 0 24073713274945449461204102910421598994 133977082569636963661536243748874674928 -380425728201550138215351789373127451647263068693480282132206203145759562497 0 27503981163725134547393549487842538617 106655950854004080229839562159043433130 -9094643860625296844594308664695409221741190461242442666085415089166714663931 0 101677022370027161416229269145250757180 10051393153790412798450973796217927284 -21203967679945449352592782828971643324871647645162206201344636516862710618591 0 154127897902987532067098930143895419396 51504178275968771159120675068776512573 -17133600252146012243030474668420979239399370607492821993763983531019237565445 0 70308694845002266677519949536281334329 11805503323436673874637124311480955943 -13305563257661636670996062269161632612639496744786522983956894862519677602127 0 94665335061753143079504184239675016281 20925409294225377486785868836834080936 -1613748901154869520717117650886617827728069142599559522430272581620768715661 0 14777480879203433327236849178133755765 57939539320460333626581422187493797300 -13928550854916627446713701982386387985292338783600390745764424028230307268909 0 104286017462547062920529917297042695304 85274371591593962333920500882334594751 -20509653999520528537247055603293771653278677091204334788709740880806577482501 0 132318474170916746781253310667652617459 86605881423325269640689758931479547037 -15162344391903873401033512050899866129866496358878497826781641970321833909141 0 119804145663446223032481525576291673217 144980559094180050667749710227688674635 -13101955570895084188986497275864908437114063735465368418257530098164376875185 0 51148814320155217668394792127965801935 119898888569725654988624099619729220792 -5355563390629056554212554571108823311415845224036293448279901436572842086772 0 53565469120923140993993624287110071680 94326573691304836739013984252141672703 -8460384886989908310931602038754624597279199232306451460203832935391963275730 0 72808036176991799723902565373830021551 91081963300692027433870871841576739412 -8717419877073691350459054010875134474393198145900956673514072015422183708508 0 68109057986592176082665503404977410511 82263634564452764257371433904170629774 -8296295216291850582107565138437939174146943807368701204871159359097780144934 0 151508408068363433091201627357084969196 113151731256104610672393163768897937963 -6564883377382889581663612447069319891313546754138399681719243846986722067401 0 82189868332278135896329410489570004338 45469948924932843584207098713031364017 -16605717347878739756034220921507751766741411260008120145117235175935231847576 0 148428706327036989473094290734077690836 44047402041058623985364476996563391556 -14106886857392864791875919224397360005665452938134693269011975507389125229857 0 29793790497044769286304631982748050341 82493501707645103318042954555585483059 -2246047253408912235085083655274395467668594419865628017936189788144934530368 0 77332580107145948902041101598307275151 119095449663896845795157205296684150434 -8541330885880823606562343433735574136206543561798846634106977139246130578077 0 18130450293687792937260412944503587441 74070823801559044374830465647592903332 -10830663321456604435834668466031401344725984350210729634896300566709255407762 0 82401487273040912042020418845704792908 111369567623619451567135876069522446884 -4020326609668276996073640956383330916750640208947814097288238928485167485291 0 11473224745137505769934061119683114996 109844765878587234632777043479132057621 -11382383428200735218663675979424684882818546916837101443032961519479025234523 0 16732684788627534315372869731216043733 11824419373994411050179083656205324695 -3778983568431381112344973983915404135338337441402483786364364089326080798704 0 22861276458330617253082557992769795799 61656847682365110522863082980725179982 -12200297907550944034544355238570538284497195001527189629607825667003184412943 0 63803649384759128179819013133065696862 137843720109479004011499732739302855738 -4136343231231079340230807312151713148386887206615730969370321889189733692156 0 77140227424098371841521188814143985754 90871902801807314531490706563386772310 -20872772636232776961382163381968800720842994407280498982095155046863342733835 0 78252760105754541837596972675848315231 59253312955959096895556934359100067130 -16491269645153102126531948069760689466982614884791048128605209593318091455454 0 85854783206247269874144071395629343863 38578300455303327556573962606837376732 -14523761610169717398707441410223306113512688556351225514714303647511662415422 0 126850193214858275902290980616071940475 96280198091281556167005260755354318855 -21607002836839609718999282157949841114290607236029856850198779337137481650937 0 115171130883025479177739711849271168098 41592008365038939737683102136099968353 -10754411905711122082940954016221786878560405082055143973509343221109954976153 0 131305990708764326502974077736425790493 25658895735782098975980691688324595560 -11821881265919155299977527935403241136710867632573523651653207667913737290887 0 15127548028744942410347864892534389506 123154175945893779441153542906002934256 -8734319675692523938942576987691654984832408911092402944485714302746850723304 0 101623394709175281894543999984941198753 94704306886792286824000694116598203340 -5908640987105460946764029564253033076203310671852281178184244879655993285939 0 116434816153489935612430370791447436211 10110361918276723095894555089413699697 -18993125208180033040763465715736119912087275890056334309534379345409559419844 0 115488526091524726694289095251193971762 95203514949554076918252847416779961142 -9815783918349002570137687485319776396025655170344951394813839512054601870777 0 51259709531084359930387591163750977499 24335427874409311600009171721011600353 -5384906812420282958644962103221151908825559220252950216980796805076415643500 0 25443935448456960581437203471057515308 102566220934195301190074970156000524791 -6400510987305340661393210078830682274119647213879800994514271436935849070847 0 132295663451502777701821306904544745764 125607215373062291889170887244116159387 -7201766139498405181712054740340929744287152935803604773699555861545298967521 0 37878214286495837770973378950688857247 135768101671052318899102135458283219791 -3118499952875249233968746651844418613724365615781254273838239692846519619922 0 100894620751020339709143349453848962708 63923403064982604386669927273959137317 -11724334102378957985385474716829876290732939953335422196983190343257158614113 0 148796111496142259056976858846424734970 99216556442865547987750029946291263911 -9366668579881767849470700769571915587384674210114828607038420215849456902071 0 95295789068935480764435549759044485090 13522842882746308887902962191641792451 -11015157408529988204387817789931368592500373516820422216151802400309036893657 0 109914143065292007160908538129169278911 153341395985691233344888198984708299604 -18850009560756628040530375187744184623271158962494419496350723494961023073824 0 94326412555743262142390939833492038296 104510032694164085867177465356143971004 -18734261801010584456084758852354367862624605169431252710078686623151248474348 0 24784066262621967912778170022827001566 113834753515129124682193554830504731195 -20578444892636482330285650175885356631569695608562894008483373704703117883302 0 44110215897176076448881924832881113865 66932386786310875895300069407760332496 -17849943885052706215105835056249993390508913024922065285518719937156340337836 0 48250257380451185596555805528576398059 75305574378810730315081861113634074306 -5938772252081441124519458943374979800176757759354636629654383471577695859592 0 35784620897063611743231235935191724549 36228033508010560931910032926049439148 -9640528084233565312433233074579691588315341558423727889705484598249929640423 0 106363058386671074327784502987176112381 19713267478647601940208757277518755594 -13884104469949986574274321871654594004123859602856221079989131517768816961177 0 64016602471378937507151580362462818197 37288862997087902893162006469100852286 -2522778370106054011854043999228290900045944867032472715990136341473380611561 0 50908847229933752249104919495059758795 84936741747454192290242903893716711816 -1110828752747549233028340338535861462073903731571707131090441750541184621249 0 140759983783838408130155051398705888422 21336757583287227684930097721457856504 -15091255345851317612948642627684216251885355867496351633021474460225736413242 0 99638951391073541268121057526190676883 127187941468942571943609917419990636632 -1246855297888052769303039794829448127706189374038477240487936700369157736056 0 115045128399105180102187390771289908699 140675248595828303004007152042964550717 -6406492415962141759533484242131626297706147383729197703926592288426734598158 0 101450218325085809780937426131010288995 68543920462313323549107992748571176119 -17779695552743569624203313258519478236593801014182834776205308352377699951109 0 131899722164379162584144002088457830015 112414520742048925207756188488535138920 -5173452134673506308552517409595395170672787497908310251377019455720001909834 0 92087783164269448074277748901774579400 70278723434447720772855536557813745581 -21081108158274307380985295085467074775702889189774315618700094812807792381814 0 92724740622131631442205325895594181297 43623343251768665640980478916867143526 -18992805362747922602236582631190834388142787327777776301295824237233276851710 0 52333368359164758450077280341613263853 131339881172850359269980501625133425416 -17823129244549712442496414494126939607021265109141004041891386632337810212889 0 21624764614233352698781165156463876568 50319575662427503509127569907934584683 -12618903785163753692248841153850193676921730006546433521223825766939288778869 0 42529353661788273581473844897089029123 43476122652753907482673680415619420944 -9869344531120176959592522039982882281154661705749415739592608475161322976750 0 11581110786762613964742771210487476318 153064977872011211130372800082957961094 -6908776241742698728893214677326857161157767891751243215554954811382078863637 0 37278809156219159807867817621930262866 144687147153940935882206247558895440284 -3503272465968792861254197611407897715691203652409008947273389810221049594028 0 14511680545222569325083491318164438379 72163436930150569125615475262175244881 -1013802213410278070068746930054401107132187255570158773246902221908961727239 0 131481001971245604974571548375922331771 31124290444818692958190048856098366712 -5097009584534971213418953408193447245784608598936825506465816909559651645052 0 32778996884868213656625749231189927405 141739453746600140096266601558448113181 -121458253919564856937697421656074629846168862544226918241706407944254853886 0 146167096983714083248076146951315423587 99041498269109410379112198242212832534 -20432567191717382698015014203903614754882188115096528496075487286048747802595 0 58940837099709062051804352356771120299 150678220028566789820995262688485728345 -3392525470963260420241015700896905984254835236304378296486652688755839143837 0 80771894660729634407611544310462400427 117484812622555529112963411351517186751 -20163524356848622168724016788459468635156319154467230293919637601767176312443 0 30352366167830386350424961984242886706 113026781420627564828644620473983855230 -7862456127596990264207554719598000682341005634647421774225692513462531596070 0 146366739962674338763668158610197696900 86243802892245068765134667644239931924 -14777071507559502859785941638657765412773670096472823827664266208606478798922 0 64788846616680769196759702812798123287 20302306474905235338115559704158306711 -11451848921864704641883561484594778534911737556388136444647135893167548111826 0 91582290106320362045711126657421217648 28749034394465977045357913095898104903 -9208352562862853407282920913301592727138189149899578101922667663564253749223 0 97760915266541304313425096525238357741 130590858468334384059509822356710337779 -9268091218761492177583715716696779433038308182339393086148231801030698319440 0 19721264591239667810669607617281105678 128783216928016905142034904302592215788 -14591640320612336332905789229547111442008947404045206036432821357720697571919 0 31384052017792767194524214753529115417 138332549600592183615151134278967176980 -7601853154634203564044653872626742639354221502074706097305424773011903584240 0 130552691075832300886583504906293696417 124573838046849311493192581088755503521 -21210871507194957606962596349883567045692684057801146962546923863250367772534 0 98234466374326077519370339666803212425 67086934521209181915854859880972503586 -14849314134314970701618299336175806518810762170054745948360809428182139207859 0 40845321987348053441298223118193602456 24027121251963240589631942947753607994 -4220449271316218479557489066589501128695802839233358766459583030825849516486 0 45251278511860382820295175867712561870 33392895693992196830583271822286978091 -21366915562467875953331307333840312488764731531267997046940326768883351514153 0 23887438716715283991464991075185400766 78331797643030519360891082920683592590 -1303542206078167613094286301940546868534058324916639606935352830019203458745 0 60984339456630510488434991993402344075 123828907542667975674519369475385615048 -7967005283628898540787633664844343937689975498900967380371637721269180978077 0 143977894858947170155320371336011507908 141926991478650240189703023154207934021 -9706555806895475098970457436877188601313046461187097073732489072760280000203 0 54411315451861724669027248261538299324 41384251829352211472370461755237455701 -9199841874580515711165292464262427096995262963325430577055528106888012615508 0 126741036173334961875331562175709820077 74410584569752940484536145504464627188 -3216395039162543114421294126831054616705388405218181439048930615169948970066 0 144329512034692042285881127977459573269 90034660253504351089760399840873919000 -4936411971571051316748314230772635948714942372040617488022157627958087673317 0 14085124693244694416029561222060033710 40886157509263684078478902765486033935 -15441742250045612486335798071682269445257432434954694800342816540391739975243 0 82305004844443175761133327799942131178 18049868482541908973250696716680374224 -1338193987983460549127486341860713370855483669297806733208634271042130564642 0 56771082835338903732504051886623639001 77829555658027726859132498269168235251 -7679688112476689978746704670328154926363416399345865143143695412717713752846 0 137455884292842510755005559056722808360 136416977478285300664563703298548712101 -8417007674912536575576604907425987836458326367786138955156729621834824875592 0 18128551763050584019203282584912109665 63826510614252262957716050968194104284 -10497046889461106457440470338067855917178574378336650967861617288954661335787 0 88986978808005639355689232180577995218 43439218118163422518779185734491534516 -12375479139424628215371355527957112462002152397796740599521524875416859116969 0 98804197067113322075804967665882902895 63529284485050370142311104147867322478 -1286951160715832954007057273466368425733092353504458846612410695656851163305 0 7460627326327327770496450070599887262 63533007830810200726329450957034402073 -15487107433304978959634828448368485037691636047994318071382324540944089853712 0 35229021757440413835725537675408404095 147610620197173421400054464871573073831 -14111477523623801296192279777389994298063873255928066401101315394384848579597 0 31727452672081377858571829300700754628 138781233827558132125594412287839375597 -9826149083739615308950715971443508759289775352938832482298447032174875268014 0 71342470718734851073310613246875759208 40377219356637658765695074385618576257 -5524004859203045098005551394312295088138045137117443853928007080848498959340 0 147003423468160783646374057774752591602 6847604983412973136986723518920928970 -15636110155577795975961694473480195469207132165726286137201714083155635760308 0 146321951065145302288111852619506869214 11152401948514590568913771305915790023 -11096235329597805574011290633711464507250406932173113076445296942934721915048 0 86920132714130895171813344631972475353 132514312287343125701546142232919090289 -10011724829740986540034149986813351586776475772386473535223480932266436279781 0 13024930814653439307157908948102913907 77582961967793203240144062965954716556 -12526590664749994148478277304590738285818396266637896550765597622406212382126 0 140665007310002546777979921956613380748 99276817275578236271191291577354821696 -3139316484052398122082789441352231733554825362678562077323404439982247293640 0 19892114204797782313058145438797802512 28417658371358338571765308273739862595 -396659578877452924577386552145848163798201157474613714233362341110725265067 0 63334103691444488916948487345080021225 141234478347949915148487757894298592683 -14564896656135537368347781215462593901495402829811925260011182148525280194512 0 93531911137594563007327505460841217439 57264679345838475599677268964321653724 -14998487026235249693446241096973162288803705129770472781787634876891515433648 0 20303634214026115627576871494279373215 10589464848034593094679617394225501694 -17895401077571767713931422569672082583499292858923175624120044696354356306246 0 96664602599121051018893873010428584571 56464119243283406010805453707076209438 -13287124534642201712458372167001528760604701150927447836541641349452652092341 0 134889914361890456981962931257972165317 66690357951437779397168678127907414570 -4062817382622123995807486340903475336600775478651177232119867722082806160650 0 121088041054853405759288115938510440296 62714826665365344224816053265476177787 -13404386938961453065167775420867372677777738194730734725936091577847819731191 0 149675825785433130352319436771955461167 39424845111180113934532016935945039875 -6785201547190064508421559377393546722889646844458092324002523551569064635354 0 12261711783995457755716820042186699051 136854226834269288241071113613549320045 -8157088011749965682267752845813854154489860688578029096700633084234454930895 0 47409441082549775454831544206470148446 102557200371911237387644512592794584201 -19878549954527748137587167266409766035432216985902847683422388102671320933132 0 101675417845949067167151793345157028904 112209950980096503991228468268195731576 -18114709950162905187923797336486044742359036674714599547531728627010596995040 0 97115920229731892059916134253930043341 39334331941114198693866680918491000404 -4614271683197046255526280879394984439339899681728826314109277656377501536478 0 92283020497994383906639093908709938926 55239208858067317956116871425838117129 -5798183017347162040153990035449931112038970750767844342836316646222408411943 0 45623979615402631782333555334726971759 26118107488418738964145660512631113924 -3218397319892384756554424866231448323303095268258698315166574417709058224260 0 117875858602113809161982797692562916461 90542593272861620404416127995948153907 -17258127988052713733293942901662240808776310080471939702374436974402127784799 0 91146750110754458469839193633807819630 19982666039769239359873544994800433024 -5899994018140299817171534289060052486459959335482835759652450905433909029830 0 100810785394235970958092132874045051370 24351554744031124233596165185375395638 -9707476248663330132021943951349320951260114074822426629328600785413725674550 0 13623542770865657071335648925125530642 13898568272337847123921414107835522726 -2914410430014428406646624783984999062604359129325072140855686487843861088428 0 16890592582866463102316212172850155035 136898598495407491102083338849187879052 -17852017682801501783939981399761840747564603114085758689832278383539087743016 0 36428010591102881017442843293047610222 86299372118949039279793906451153234854 -2667253114728230772161692663838384460915158621183705075930640651261984121373 0 127694610826840621265914685408328733735 83230429005550278553464996768614259330 -18961982642453660928922842638710964836503023042234978191851911695513945007575 0 50776378715572071442031117831537634598 16370696366790283109885814293839183422 -18915879796706137556707999569461839715852905176179213922663243603692140365073 0 23731390888189940468161992938193028084 90520110383834940215133956222498007854 -4276079371547669228696263800380148036412289247999454331970217380681659489794 0 4474176622394030703392679285977238540 140987639915648666007274416146855906505 -7143847956207292740530284226760702268955961807856421148718248273908159700901 0 87828173773502697202083051503575474208 100150984449397059311937399511602435522 -12210696627827556223780611230469126951533461742035575661358388335553101003929 0 134956342446799719399597222219614373522 7376442351042602532510252024873474763 -19246056557047236501580192098747613860094587617276365789922724583272449410844 0 47842311545201094186208209407734651549 114985567979655340038352958248819054223 -3720484268586184653093674810725513516927192631671324784292008987442718525071 0 130639892539424353885253434129533665332 54908279722412166104705686400367941492 -21601724890380573268255735824303182089873037741570268492956067919259223012010 0 136187138814193196968787907035473497996 73645469707345581942309624160205745965 -12730861766225566495730692636378278224770291128148600794332742983122474038934 0 19654189419830833799010210544763819364 26351293641790609304325101582775383246 -1978050544416254967348858996351251145949645678415513982064691415973835892509 0 136280356217930237558376560797527056022 9641565040113698813799936031369377893 -3742012010186901223652658243318341722558902861545522632733182269810335201136 0 36629124225265040527790697850937624645 10859664423703725385422507301747032021 -20686071428799159524221801975735949991879333669319119023239212017727958757423 0 163034285836366131810396335522201945904 38897417341132695826724759964325324000 -9535815918867897702501270850257536590738137815857421484516539354121689817788 0 145745210755731218279920983345457076992 92125808502270603286888249598929506414 -12848650368730872018992464563154827318916033153912443575833862264420054911228 0 128896321461466188149098966643151361047 152096178018751815803561958657018153528 -4442606521133839844614004181331841848737970458887937624199963729266390179619 0 149011476005130133894527371951116568281 102846498488372665414294524793482560347 -357619330669663985617087732810176682129604932261308600875176219354652922982 0 103847442623177463969641245036597634340 105604682621653949661104847979189557110 -8307003406953701384585437389199525841241833535296650733737377478634717655873 0 56418186492345603318070195502169818869 136237392937372159893030502396851142196 -20581896955339499863314267925144369416323103325529894959563996879534793926280 0 84388001629418508110640776812938940260 97916645576634224609170498437400337212 -12416763829908165045235956549654915849485020697700603783851244934493720203380 0 69908974801444099135586511860613634466 49062598561538267261866982930076943873 -4987416035083733525973957716595507767535834894741996778389788584154257034416 0 58161583950008444336434563594990547246 80772490866812495031810536695110762845 -20041384296047972875346831329539887308491528526446777378863098893248438949239 0 108036697505586643011940513740740196464 141999208084529154882246427086373279977 -19241954056925444996605700710684949914644064010022280637171980481344664443956 0 28679014702593097081643014815016859165 59842582308336098323879543770601350872 -3693449195901450859486790237472275265423975061288742411315057482709631177897 0 128879784200508226117283652205987854146 72141427796148942373009359320914449086 -5523647801399131796885074465819857389113605069630206898146344279605556693275 0 133430419652081349416700260249949601608 52688819177145150129884829814261782286 -11860048821021413630903334236472190470118559242578296350525585665737891698422 0 18372776443544755757839487353056065253 58983618763111795855275435611954136966 -4458215882598524622761830986582907904672949928186096928880304464442404241077 0 124486025957927352545944962771449806339 58720693470876098137925680877386373758 -15707426943105980097492210212777162841207802973201624325331462857297089394630 0 109273682785081206606002417848549783486 61238798097410210101993473148626112202 -13957851964124194755690938676037072049558452391974860206609905018241392643747 0 87753179948828174599719289772165344348 17069900571436952081297233514796123644 -5813486993747423614394153320955648383543901584412830993170206732193382799558 0 124543902665097614409816329816951750224 95266304223994206719193108126945072953 -14667839736266793286900177522585930798502389383978263094177378124157517561013 0 22645584717372056505011726085885935810 31977966039031977486813488589651199957 -7299694358589669371118349361230078440422261077495769750627103917723401063490 0 140405471997020111481626318562321667162 21444207978694923637491851964951348740 -13735435710689895797516246040294495817374259521680177034884075405426262052159 0 126104139559134898322731903029172789033 84702497182703826279021786510770821248 -20330500064371003684661323919129198962642767474306768149399583024058842061869 0 25473993563671473026937366159810836413 114330395435258944630427556016112115860 -17015908399265549404656365345607219190932834839376960619033619970215153846849 0 43767975269240953316527283919990013112 79292311474709140797914868403628366943 -4092536196216481095030298756368321930949021762573057050131107389390283289631 0 11967797252826989512111967226935107236 123037980880993989684063189712855105760 -16090894029286113453394458924793480116432566286566308319807385937569787078469 0 86445320173170360873372134640394885328 137223934835975353464410976859540901526 -14692588553870114413345899700111380807689095706182681253752784657567339032818 0 96219795066865121122580202373117882276 28480080047369264994201833470110732344 -3266654549083320904024774360189411725174597025122482064722467816957297966915 0 64307120794502627224589796579960455564 51653543672031254554470749839150054806 -13813539504722398169735805059347024588492281926429957493336154456241867102894 0 38523840865897820049341168222949782084 30154952157735089974384787806893593046 -17197158847256049049099138292940497856844845388464043169234610938249328503800 0 106213501787135136389225757679142728635 127029723847876910143301065654212539822 -20712037939497496192804010297717599799771924874999536271578735985180060397476 0 141431171123517904755722699291101099578 109834571731645062466220859241764672561 -6334819740892196091058468656624864135169279297771620863137144254522056077841 0 91459263913627969844256893607492804859 69831317422038024785968802896104215533 -18195767075198493095524567434474206758199983617898640333787691576855281974152 0 134668538207583972062619221046921045846 22213521196378834960541043380369589358 -14023866312938937287572015944477941125258551879702704304668041187121220018958 0 144180105578645203742312630980175479054 152289402171271546689320551384238700104 -5714887580760655414842433943854004219882719493024490159308549863774164501348 0 11419722054954809820828058874936469544 46031431774635803026053930421763510504 -16144457686869815017789991745223167988999122602630486881761027837602491628958 0 42430337983890955580948429745144836486 68115576137576981092415163560179076596 -16825309791670049082171545388426415737914399809545405740691458779807712510549 0 85944248541460814157803662886336237888 89362478863881229143763295790554057725 -4812454497092632573254351195545773789397545104158659975562480716476784627149 0 43357056741105004192188006408613887153 141765254344781250263841542674934796922 -4779205253461637837145123390355232515862862375382265003024122063588169806108 0 126679744385146266213285328195257815678 115213451143031782832844965835238625913 -17379467621772802951201838921302415097366066314792091014212153957380047896988 0 125993242563357622272510948689380402071 19879603543982832347038177585083576367 -2957260999853016148406814778642953798774122409700294742726883539396233889303 0 56944827010003676801928325144372747019 91896424910557216080107415147336597044 -121099358196517765613108240067037867549743530282548309558029574283286085243 0 144081437662222861328628111671162045431 39790939973329068297628605789469118479 -13196679823361890887496117623114618761559523832716513714828933126126741740433 0 34861522883074807869082187587196497425 55764040077561094764580188633426451852 -1448357833651497318747388460600907913420436444631503189366532147650489741417 0 47226107588379060474597120957754122900 18458999326820831909262328456929588360 -21488205615832860961753897114454582218353818723470382648742770982295072364877 0 60306727320578539750494076849052700207 16248562841347185321330756615248804026 -20764530780460141553865787564702490525931293454205906854616710159977934541675 0 29121633939851249475554049669551446899 129230213121506450996926051679526688697 -4127053254429472360280699532447227217295861182117124363860245755389297263257 0 129873743638747764402930582652665882643 140525708621500920221907725590190091688 -9510972476806707967889352377926344472315129901608320327517303878756644169756 0 84382763582881066249106357579692343940 20424337009649733011937863447292683745 -4100004877474630614093026835654356036234627660324480494140184222012025554119 0 111454821053240267712883864182243022273 14840966250130358674378361232580790245 -21564061500868455552374635271424443110873290171366432392055890015666597480157 0 33096872199416639199545131822177542902 57268355572649114741826224633134538541 -14232496969188933801087860681054441058404945739642889345734562492871863391698 0 145072166733266658485873988554955236877 85719183938448564572311910768712218478 -21673568707014063106380673176697966489910346752494838637381805803361047048740 0 89861915557762748112453261994519028015 25398881297641465424698399702405810854 -19168746023983977480112889499970136154265134164808978926192461712658317302901 0 105911967805835513394347132352858525308 49082603819998306002052187324385233769 -2572709504867550769898886304796216087110186436217112652266018491246056939176 0 50922002236889899277149851541414367532 140916327962257966472004689483262508935 -19073069870951676045247792642385742988181826336952054342010816735543313122400 0 125340093675700223006637016065303420913 96492582576135623371419973514691483139 -6104826072322709110364442955464355287444377961537639133748195283040247959698 0 23448011258442716611733045561022662897 100886572504952511303483198873015166356 -18879921967245395536634782041551751835939470730016103798143353048517844393779 0 143670902480939282208169605885383751293 137612836978759254145467221787573160061 -11001256171372713953092811055803191303479361221598041901910401752562652709913 0 81892232478475881861568841326876361221 74983396578841120249147009436371730763 -4763602570479238126579674408884414538899131317230326492899320004517075364013 0 56070147706916513860089238156423245190 15341008547809294500462937127145714123 -9044073409870579032593195601690236928359931230994805794767893890013916138056 0 19159221690594870674846151396445546860 19569956543829648513365540249257570214 -20159051941808550906233078607072941139301117858816537542850349914459095409157 0 21503967429628408317788639768305617126 95201071311973169246550313304907978386 -349347162387925312496280483889599498886725126692686809694478916814845460761 0 95733666682111105660417919217174039136 54590700735115571434759934564246582402 -8822727110522930989432650454117390686480461099018106129292611297714148971323 0 88160349584862270788229949644409466485 22010442686860639929775689468716815409 -445292251885435713943393277431127909477065515922726078647837086874201779685 0 99640690143503662786894353526946199340 37809949770249041185577281940624716858 -172982207375200048322335789841558446114946223763374896131173546046710520450 0 106481912390520025491389827640080821244 53942751716278175673727766827619153350 -20079648208857972763680274699461731151252562139792115786479922261831956565953 0 118877154857576896624765877286806423460 114599821541935330202364009542661355028 -13045805666429578353940257409565309408951129651719009124871619808398784502711 0 21549493067781799631137621798789760853 35950784074724483543563985560818407432 -2264612743459628441270670900116988437870044162226513642319964395885562947233 0 62309792815164251392025032598725111040 116958972470712457941482058922332131881 -2510428856188763713282909552872091509355599318044152166361638072610727584546 0 4404597221460081863383183442546679423 18219966988269011455155136802527608448 -3275400947584317652564517605744696612537685105676157045728823888077189581426 0 58827481906218390244768644942405052157 93911170606739652412757446518027159834 -2627894197330919171401315575543425955801426277016485171470254503338604572813 0 134008473745007824088221853738022392588 37219172987065227175955434190711152777 -16159945131104667375628977340925839724894894509232782011741375136732552257379 0 35960809624192126674187750826252049144 77068101371836662058860113060337571900 -9263229172891852689400612936947309355854215175764386372534637695162437187667 0 98725066922440112206854187222271434643 91045081652234005837391854463343110403 -3095207311267567420035976701985542344184458193027353264187885369192036217621 0 14724148513950637236678875892363993476 125069146490157419260177720474326390303 -1081799652148060013572156813107628557102694222787357640885856169108893906121 0 111066009518002536795600480172363826265 56650189812766186599064637533523888448 -19259739007177671727215696798641406905312168308616023272582714736839528376698 0 29087382211900932754359529827194112477 94031063110146648810532870074311199586 -8533516955016732040060213410625639091039592322748366054295623461647822233911 0 128965159945643323854498991985642676355 15192075467396817028733531337024243949 -13615037218973649702520892117818190320363774956298859523588370308369469751061 0 145794439649810691119426123382912522827 99497055150512912195955341020134338518 -6225122470092248426328254886772058831835496134345818003064226006995234768219 0 98166826115440895135943089025296791396 132656264405547232165970673948564185478 -16895878356118560023810139573042805701723380777481597995556179138738619188061 0 151967061506231599640182318184496548946 87254450603971583985055596693284930073 -21321791861226926998943789267386742957808121775188011361103113472550144410334 0 98871635074236707105865241410559484730 80212968518983989459473923320885678320 -13564336017723340724993404425788184058216255780902817953280054189252140872223 0 31543044381097243036574924004843293119 144906489816999860493983460467842075562 -5918417570340862134608896228662352551431838722672955883089256685694518275179 0 11556084129406557970228339623242470582 10106220126315131058489799027549660718 -14168991345074243347965522052935186827090806731158576943755562280664846496382 0 123906971478251910746827613203654564532 75181217032045468229758958879279562370 -10536964977822463281828810951980450446367329931516724601953097763117877308528 0 36482795809958517908452393560184287831 29819394541213580731679044648011557268 -4349752837260934538721628686930665164337058343891367361653601221484722271005 0 94513159595441204676779906629308103212 106263430132263711127237255472644652505 -7665257672333447498088033611049084835973241648135240946910402744444044675553 0 92060092145429435416416644597506800147 148461742939332570460142263472599995457 -12931657764161080289032576559318512753642765011161637187580781836570780498812 0 86655563468340292335517457363961437188 104219194493943518652937310994248976905 -2121397955825782481248686651908658822706587806186487939731673207583342778841 0 107071263278310336285108208844013111841 99565649236747160327675914731640550789 -2472062537124481933543180905686286453593114386582310541773995573590525213679 0 128502210979137483540667528197502099909 116347847190402325515778621506714338115 -18560428496995087629357318901751994450970174794392341767836083842606163766246 0 43097648003552176971522407568515649455 99587274380997894507501395444594343482 -18385340693544763869962061607346913376106758340276366357665906991426246723121 0 155992202743866314893922744098520977329 54040193442403274073106129584452489292 -15340953094390162865381763419830743022134913067683208179409921929037009663764 0 103783439671483222592346482960743157700 13933070412432958963240991080503089299 -19499339962983997386760051103393003270923677990476477710143191028590201507526 0 140069058016249379574008059416335098801 114035003418721194993902078964961539803 -12837259454978821434911310862813590386947120051864159601941253624023309567262 0 11338845634303663465326588512322727704 12324707533171646486421474839479037549 -19331471727916467313059457191938055544657719941043148932123955387441935419425 0 61153462707268254666030720496579533875 21690999347139257754279413721977622855 -19281152190244651572034145071373322383122615735890087284497409245552962608020 0 100321096586904961102241553245934503699 72464675519943670576111327225787757259 -13567120525484238022727128853832094142989508774348273559670592640058334680242 0 84088851800398953561012253528463347227 109105159933106007278421148025322574567 -6964134122309627932292494272939228743748738471525670764523359342216840144849 0 137868692492510155585645988469494560670 99953680603757517721729934028044188600 -12267849772054866418097849837950823012075364002746751186719291914776422523350 0 74093492093803947256523033251482280821 98264972286000122231967773187661793013 -2193459662183386321244192716795391581761696110239124019823916682539855582950 0 46855419267532877524388568895697137423 89811107533878640771255239784975811541 -16495123023106115130921891546358331749496101119975808218930811120660047616644 0 82674521500243954025960819398365239652 9963640846978075794750984683888733270 -18556972810710005134515381667524631364599589553102507617724471419877123544601 0 17444802928506321774205246822247841223 134642098444804295216949179247017943071 -6915010365067176763602882668248828893391499454641347223745028146569557959189 0 6535410018101867951012270143606141087 48095628054058484640055801089936692186 -9443910130531227061840486787177707580875844008877413257262437219740169149516 0 104107356112763339644050724081479055081 57362523783719059228171278247010015567 -16104481728505822612831962401627543991311739739094131128443431918915905607025 0 38064818867372947342010574102364757191 12797614605016434968069069700368650034 -6060952421827203288391535782394724618157285834692279277884270373285760195252 0 66227749925225633119984583277935502594 41213494835693698919498084500738550410 -19650416219130254402331806602715983872175810960597185973141763670497301769351 0 66136134151349174595209993639049102972 55430030791729871930418285623035111605 -4237906523642416629755440383097307089966394692985515441454516601732339561426 0 113628514808213413280390186544604309751 82903799502031318480180398365443142562 -13094351465393802599327926760620294048237620432395181104123579515348367408562 0 134735828361132617290094462167768868366 61664880068950656134714520071365900249 -8940945992192418686347707878882369270895783920547977079265136598158023530829 0 21162127876359676582808122845202134607 76678267763887970602035025774360022593 -18182740139691596096018450692211662081236236228437982543060764252669261514498 0 48721929536945834600203183933622467548 112594946662620836003386560184731184271 -10874339135131065986663497992015686371202833085442521789871766910367830775914 0 132967339262448200706380349023292092489 69537917753026449200052884251613746613 -5940698840414420187780684026864372675271530071288184115214250588317745545018 0 94014114635496161483355636399418306416 145435280883614002835289307860673977976 -20872087261437330783919792457702505383262839253709332964846893970409491903538 0 23190254389115446781001984824962662576 25440100555239409775294101646253799666 -18297879412360880677133675028573962299397524346719374570008101802100355036052 0 87094275353627073357573759896576163142 37620288857581483076658491234020305811 -20387156668578719167753781879347928579362216587357803652206906001110762445816 0 124899052213690635531136773823778803240 22004745637406270586025146667062606845 -19907350827823897135699826258332838237231383298994819898883746696800689811779 0 49772502066886518231755896745204232941 13299895749181875733699898106681450019 -13985780945470195425397131129882814952436736574989903273977762104145489774638 0 59983274371904506562318089589914764370 65839553912965946809125072805563199628 -13858392850428840329722241070015025975529421575954576548885090521450397698213 0 63343225700670303702155696542270295911 58526163270103256578683305882711067352 -15243348516152970149155910000646037517429011751885055566601357475082872003201 0 45245915435765696165906044406412901697 107307836920139843883687584721294860496 -9690006689576824682164257993969914105316987166475128329832836357770717191971 0 140836086027101193083385538552638747509 74477850755017069475667413881463604134 -13882028114418638059368301303375480913773753181913479891732418442060547876143 0 38197379279729644744372980370551777911 21073422773175342521767081250181228062 -2959152406326762487976648963471028648880271035038371815734334187210618671508 0 89727737598220911981735087859888798843 9604325061101201268929641953241129603 -20137754143026532484708082715379335650282903195913858217995888631111463320777 0 51009350396322832311786364095278390529 50599353015432286376057497973609587467 -20444574591373934895201480458151426046396924468857174695912093624703636026239 0 49080720059465345758760342522312455987 41323211761229039213684187578644092212 -9361890782903368642257504171231427046129221479083238653805021054678659870204 0 119358451181121251480457646599025261943 53269492467999596733926245034248224253 -2049562809685422103379250135949276805876672466004808454873059285468322693947 0 100936241046097565363375500145997612373 47985804805579080644618535141995330167 -1320032313256333659804010302499630775361478891078683706100198036321215722698 0 106437326750191567231132831167008706683 74738298053105325871343983553257150821 -15117611426633504519773636628972722693490254510210914605859641156636995794928 0 37088749427215677590377222475430470613 22748186620706117665179506789672177514 -17490343702930070189152723883051227710557640861961148018243813621711245746600 0 72393808523595944920892866905106496898 114188830140979166680799954276855571600 -6796064430388201779232325916427499048952672176224774141471516121464715077546 0 86334773575710482056430740275507709460 69065103388162682613034305138105109823 -11823838814045258154927694235261694012703533277060451335285537832472022513406 0 74614715572608379877574905632403919344 119500546992045339058312923872174428948 -12123161115817914571598395010675598415076576506061976826960515159514743096333 0 111821020320835571899500395479636081641 16963259601306043826668174466774791993 -13426302615388648394347954001960264629679869081869837299541401518383371131117 0 44821427450453820022229964790725766568 40886539870392879402419960436238424475 -4055331283619994657215376526773102493212142457830385467880929718005475263512 0 130722870612500646043972798483643400320 75435721565464353403693515805882904587 -18218755926009446427389104798038187893868770644384278786839647830547362276147 0 146013220587124187643844868103442928349 76489599363524033384583282139142447614 -3723813779110272828459631331893016106684670316794348282024527528067822309106 0 94057027179367834190429802590050934418 34597037978868396611413208385236414601 -17558204449618575917426861524353677829303469556172216629599661634675568934126 0 55661718558653150374619155897398011109 56127364874640241605346925148568406889 -14906000560605706290277708994666322924464492553328319946773040575977798237096 0 30655586764583465373533807687905848817 41987142730755214155804740123079349057 -12824988348354474011712382516848580859176550802860413112356145347186367310411 0 107012140032391673981459242914828833344 112323960843649957389481030379596393827 -9935847954449685981170526176140880704206535436601077173439190017455792782071 0 37442996668633717869873012240629035697 110490313728990491985598976801663857248 -12785749241202494501738050984326324608001023381083094077031481998023206568058 0 75549854930586988435711181294255219634 109220313132435895604828882043961708894 -20666433984200664062353472247386508058350438509666916379899695711462212492239 0 45553074725802470522548968883186102436 46321043830969058189411670939627139763 -8793017542054983908823811197652736637107731153854158847244943539325007911855 0 92538660564881186826627778308469151109 125683149066840178788131291152310291252 -19929337249901897942111039236646382156542011479965121721970228898824522887777 0 108250717044074524768613401354319287306 137072142818278608840473199617123720648 -1580031562098816032868558101172003691459703413324110354717864665588545339781 0 64326588158037804080129189270409849979 135509716361170070040603017231576422320 -839574832782191698429461305438355111729637033979277265764803056410407595362 0 128816396779218430381094819347777280443 109042221015343934739610002235196272550 -4087412384429105099491227987041782062231523926453500676106575370434580794862 0 85349686729609657133900857876620150902 35253210900642346043877986643588825306 -9973124371227685600995268032270502540891332762400339153900970878100069595647 0 81671674504894733053233508392405186645 50665774469609556613736692152151558821 -9693610230470685404216736447856083369077424710161815065305764800051604257383 0 11694044653777370614156040085655036467 37099033075172883976103066211099245134 -3886220820707234665903479205774217829874451564644488408820602663820964570971 0 79316680457388711703045175465108378659 85002721846907948078675660508695525621 -14469597391355726214297190546488077426506058003236986695329367748076651310108 0 95969175509735989090061369985231539123 18080359364058982508445993022962596673 -12939679408031052944580498916816350486234927120461693834722926593293871150563 0 57550492671548707348986468130746037000 113213217939411762761746526955506851407 -21241866645395427491346817793464573860977323866634431061802851172385815282238 0 145611768909307043007032391418300447835 70518339182647947399650908284081557818 -6315493099898441703307302098768554140188730274861882089405620783142847648305 0 102360585308250542346571618886122111143 58241108620166913400705703879703849578 -5754514125913896061857742817618353986584724400468903402335247557260682404047 0 125066471272725510876798488418674051477 38110406894569336934900872743233986113 -15330333491308628638661129858546870313460121586227401588858921756039488073622 0 46215960227879175461801574707637258902 93512174737600444447909088223543618656 -14999035400163714002086094607004441129232273605310199306467221119877484568627 0 128100634562503232924203851259776956210 64808694400527334821481849263312602306 -19771943101939502778025912030363854614169534104618846418853368414432527192196 0 140265798432424900024675200168177340125 53937062066127434581545080315600271611 -11885130506019208015297325203193792010076903779115325474110837251957428284014 0 30960191827503812999256770449210483282 132098168983648653721318531408295981239 -3806884399393882143841541269072612737493425538300648482476924853334315307117 0 124701288667317390307045090902834326191 30446923000239427648308095885180201662 -10683145618499511315617922290523660592342574460190275706066531455152533659487 0 27785764435924843971339065637862300510 26443299384671706430272864259101371646 -18530197834523019168618458434939562017808340898489943473782721786372837632635 0 69533927773508205538109545093560700342 106006501458347411965308054556829763207 -18548972506876899476885384884811629078186571465873886276859145613054978845895 0 42092893676513191785591474591185009826 30249223283427739307266555249046295078 -1523851250489688753488483296585707135364404860287618707249002114740720855867 0 55184210338934379198070391384654282082 106226544249863025093805664164330765338 -734757956918193343426505040487478050078986234519484232186278351884027146990 0 102966068617024001560249578429098865517 43688723478085181923975572769575935572 -4454185266098819476585682867141519043505695101095217293821973928639052842070 0 81107525500505611445751576122712610449 117849243635257175156674314011731763880 -6473906206570780048765450356879116529821914288846657105485467487888296552839 0 101172319957028699377773566719395418206 134415178660947758984317986403201085634 -11900906700761749779358350908991580499556496329480709675232632584042873002622 0 92294999153538717430330800915084521056 143483926530396471419119423452553226747 -14329185771167175055522370841099833577711535833356788404700243429379937325394 0 147900396565318049263300226761538788054 63744953255130759565933047301371547140 -2185327977093543132686873748672036071143041445256393417766401814518656861365 0 28222460343684218216670309570791316085 16747733567222889207757005554533171201 -1184859327706710449514913292841609622400596824947929329496684564892638777658 0 66000660255583102833919886034458305161 119179123630003079144698962506241288196 -5727640583527490782979684871903740387773986790341956245506980042300948942167 0 74405143013584651237736538928047252060 26203765927840743617631341401346330095 -3734032349113863673713613763900270070998891883782242015885074567187537943195 0 10591851913415250369028959961503167565 137149931237722302976824210216673419181 -19508336235327094305987183446624154263874782625152003627868440139310250358794 0 154001214060524748835872114712655763383 74209380512658461070471454162428282199 -10440429737927089304767676967991625651702782324736660001013040566028203057735 0 108316060767101946695054028084117912700 61141842218923630960361839275644784540 -4497311579743601977509626352049295866091674952431563547757784586412004878998 0 150458594739652341280648241841798944516 80377640126353157539450796803889140295 -978504059643053414932378581552920383804913395489614295116262736687066053136 0 112106171955124971853306040353656185944 139115104333654181861846975438402121084 -15366560097235544409806297714368147774464656337345168314959987723664917908894 0 148518647555868082991145037202594013900 133747239058819845554426348402511420029 -5522760104919822015614887203539638531585041328321792363158557576082385483589 0 35394460307238081330158867183155348660 79601702513437690426940314377268286736 -1185603028244230964172557100712016578062999865876737947371463890240776573285 0 54135416728149618571698101818509123078 106454984340304716444798020493343984178 -8381941035415033520556520621272135161235785660394326944671288407426289512533 0 100618798441030434109193364064687213696 102272326718348242778405859250969817086 -19494442500475328993823166483648814702609605203300613736524083672943010985280 0 96880795665302704403542247546911027523 51842898544989597790771306134136711083 -962895556018047279803701040081845153625401206028179018932069662876328565433 0 42138497310609919586534196432213059039 55686154919167513372738367452194016031 -1124293334183065065202006974647778467013161899763143909083931392870825173402 0 104385448131197758912922630882117309254 51701116313166605836867471061133376830 -15763646604832407784669110613325647933316281200982721706426353913740050505279 0 20271862110976611540238339938461337222 114678889164968245252938638726519312721 -19466564705355921903332155699278266719638469347385278563161233710963685096860 0 63446584583976575661444535832487338016 132054458428576899238632961459390242478 -973764599270189927998722357878579404834445713526812989713525633517625813575 0 70435311212222507730327987304333637587 1876557465875150608268655538860445478 -13378798809059193675920675192928779427273774200481292944988433526946232046081 0 149072999414756522347603059141196663198 79734171032216011967221887026046457512 -6603695700383247891239844892684741552543613438775346006562725423309119015133 0 98075004360091001147178683862248797876 45976667122595218776381603610875442714 -10260029475524127617368421015120176079207911399040788892241964808525694619549 0 65963287423193636254440192219813359170 26920654211915524224115812587161625828 -6198344802760175773424336343056191026463216566085694990669053970075022194566 0 81782324171456323058347724456233635811 80165726475845643117056298591116923426 -10123967398827019133633605090002820925670717793082073197216781474116035360552 0 74514116978614904006246697953847806368 62547440938061612231272612678235958561 -21688816483112299266245671776062206991280944506329175320835212446404881735402 0 130823715436777748734971719838949456231 13370178989857586512302143166892102007 -10318542867876342289198393542366278141456326056233143615502996375560373049034 0 19672310812961585603324675915385376496 97102295646479889730601925015687220277 -9286363218221986719559598544977274300918935473961893632992922153794558168606 0 130051240765672859985490283991556956016 9914038615879418880085701846316758524 -8976880441970629712473383873144544234659537040834024968579743808603123237039 0 94358847623176552122568259343966974190 95883050674024441118292270451444383994 -2076665297770961302177696826137396219216396213708180943379253971502284568451 0 106516644573268183892136193635892579582 13600472730058948527866472207484517020 -16466137276632558383884596226140188208505619227991550898610489672227763479508 0 160154228334063756692864461554421389631 75881527468537046537799040774233877874 -804929431084375199851593007896131159670376730285964127519389659288014397428 0 38415677580863953343450042084001135851 117925669474664017838194222560679336483 -9567608857402077778692333055551906628983106548907727415613137192286942346339 0 116991461605973932465165811861156702352 33420064478398820646711587676465496602 -10684235404709108826133294215167105868585195188716293353235782515120086858146 0 34635868191125700977579220159202892972 149112252682571163585551999210771802663 -13754265960823992176547172372605990421096157645710615236573101718878441613112 0 95592997283501312367756987761929293306 18770411210576687137975494784951328871 -11186960834186599126359397784040120228008249722740881264010620674482776594883 0 92796951638483263298909033462849290795 39427113829119745216600843694460230755 -4259296026828348697619140131199753230310101762554344721543847007079161511236 0 65674410329521643599445228471431454370 145485030805309114213784317692800466284 -16249237799205700675791786076922637818484378293869203885489721124689473941758 0 154649465929069556819826361098582253576 145547951295909810128157667346290285397 -12790263745339963022462417945758281013093719856119524331175229205841581970539 0 155326691935729700575248796413024743238 34171848230800799011002945924319360030 -3251999079911866740843148973092937819907688039292287615979786716188509513872 0 80280757227673592965532294607022952559 25333521994698239185034757832826901624 -8374143700857587808428487178524838555639594630733700818412287937303544171414 0 145839707557496375866607623250099977383 147606928493080315953087105918782231768 -11145362710226086605167620107990841898939285828945627644197467513402490437673 0 135576050181535791577752743563553693181 70697265041237873585127720752045599863 -11041505129753175142272086765933464695066902330115896175971032135904403634234 0 17141687924353859473468472422345415745 8694212482613241774644350518482491123 -13785632788686724665790183879628056544379639920585480059223851238091655113765 0 30067859800700424321835964274720789326 49834820444017816799880275631334782568 -768931432237687733158758904311676610997274210076664094569075464806675825954 0 23910748349083025453980850552633976060 73335285477975714453490611012505143724 -4427249150496580234035982679369858637636363496855329584684095754332250419916 0 40567595185277434484215868889473876498 44774888730020277002411103087857673728 -8390148250330919834073147131852277869413259823095778938405432232358942798308 0 101620334008794766901689173918794167820 63868766968520434376119079051075588195 -9686553105815436719602968033448863782550513044653723445290957194257198250331 0 24844662439431301338902205210435219764 48786069450699547917624227369907521160 -11382847672423102907779164635687703754059393767099141982795207309944805570807 0 92499415964984009358964996757630661699 144563854453528667082039270555972413426 -3189713683813137281426348407829897152675698450194801824980205783367492868912 0 78630173361414092861134106884921712214 138250445896283261190396186074809712399 -9354848312237063162827194647945740310410168741824771504585604403035919744387 0 26667689514074977611101767539237076689 14410142243496351938711347187837092441 -15477341479928050946367300387757529851577099496176195054167417091871307348618 0 50699702863653635521495775879808175934 151990131554628745961662797180210415037 -15699719364254499729835966883777630936607742989896515994308939376358053607895 0 113308535623432731185429426391078532456 35513867653353557372601983524629083091 -14500881784314689324404103267819446065147810646709277586943611727382056870523 0 92220690561849097018282719732597913502 13941916992099257786964809901408891494 -756145386381031436247269688860847733096588605504824392030348049109023216110 0 86774061318256441590471319007510011521 83546192375118753005684527706505599647 -21335992630262688519720730728557811205069924403825123846012236083382972861788 0 142255103473624538874747036424763882059 66305204780261451000882498935234138041 -1306199862822152320576933516044634210904529112093098930909055591761788578770 0 94010162021898360702687588565675966208 75467476852241662568179909663590363072 -6573108638091598848641141363998966193015499438000096462746054874933468224469 0 32508188230680185966713256852771514453 43157133402516809410300782788138848674 -6416060665244744854393444906808449999050052424373335009625781543264680790745 0 29013564836868014831123958127131937733 53281540647206714267446094083419164156 -13065916190787450825618707512090772936876788988676955876425765631552520862502 0 76802684218087167930706996479390965939 84903883424246790485151945469031147394 -12970911010889089847096837504368051614422054636404672637914725473645978034527 0 15452400555905161101127423665684004372 57406261531728001442409965866376230777 -14322156245967072388856363755878116912029024765055813937322475379075035005958 0 62909673158628372294896757171167137329 99183517464101582470546645672652998820 -17281548203637030389803060489586330847460043968529882068816624383644958856238 0 63363845328588507361348575208540535881 28201238999055993483706717137746540042 -5360005951041386222509443734484710290082557711343588360106289099461935536516 0 30226421184367649813177338940240943172 60306239166432145439346650819846101647 -7534419252324513842435669996378798081230148613176913951446706156377262379823 0 60087176698655296762613047243505486266 36372745445699382142625345204565818052 -9091939145689271950662409831456417065877907319862782389649064940794737752406 0 132195239271082065141728483865839700315 9352158214790314838863068089640263644 -18848680419893364580190219585622511450733518919340357654958600275312167630120 0 158845843171183053601045532380234473746 127530534835351348743359950916804272077 -2550577578550538657744500079930807247270116144927936885849547022879381377999 0 100191751829210904140251758839568852245 98668029195605373960320187525677954532 -17676282570569871070854240235561914293208769411767859459448234157950452154033 0 88139641429663102311178786589175715182 97971900261707132870107097410172345319 -16962173457881862877751550076333396644155597064945291279347941651695559248173 0 27022434400206402194355297708735296424 152138057382565133186153750703015405731 -3786738636074217446108932538357464104135432739303281204286382734249213045638 0 18090637375516417310369447756880837049 100864582724431580349674175154437838980 -11545778582843619355717161446460470409422921574404052966923083262381161142547 0 140039916096510797884357696868616358054 59097048408961812675347977667031230739 -5995693828446839121950122564344787936410700259224934217866494248432376373671 0 81074421639312698544065680909779362680 22556808215154643155371747596291519454 -19115740813986140094929462512255095215820517208941845956963292443630250796195 0 38166142718353662109249308884459017518 22787109372909583089301570776566341655 -12045767599746414168795305509491560105753173712602007447486084774182946700323 0 78946187544424613551285486649576426938 61043568396033944955277822021219250648 -9495423021974025589125349960088889580431911435816050628631425826727630554531 0 113920090810418034909110472935275830101 30198854887139752170750714567351752309 -10328552391271137556031606450121800801025719553492543537711993791610935416497 0 27096018493803556450978678413709617383 70806790169496293177745470779427860100 -10124666067511065731421127599973546827384419139541227084443682938796538270480 0 73684449432401652247828410096981429314 63337343531536382873440608213945993943 -10611049872696695074987073337158640177295145418978567737371843216285814496582 0 46270910643501308555143772046020775263 85669562976045013026278078228032609061 -4668602035929083216252888829168592789922867019178826682812258041071294919925 0 13538054666911197157738518611455326760 89474288711888239746778478177310053509 -3221737241344394413512064822299067770048179671327721253187748700285052495332 0 112465625803930115563284301749545965893 102298992973645248126224843879971822450 -17186181555102620978262091650637055085333765528553456998670790569492937488861 0 22171836269941374960390180206078002522 12465688836755459354196873179244293404 -3877927468111207247698871953223802965866164401026063552505168132658586574490 0 101392901752985040326138641435558971050 38933892379375964992043937816828008344 -7863286921870105923038452997716883267396098560637201758710985449147962157010 0 31851093003662845820759272165989275666 39777180516209540535367859610451653450 -14363293277307214096512000934281636904454475978369715563594294183185819426928 0 143999435904986519975835880631522426508 122194361584229007604591127929905509290 -16615526080907526046645420871816491821028050800678634028694422091219133224107 0 101254614198816255743687849511075485298 125793949885673571056041883544827260958 -16287002627399310789768872532379332355205871821510275714784506394447370744787 0 142092817313951391622838836976782309055 113817562465212438527232980539065749929 -8566459193285785830182394146260455483523441349028242285417151445725461987362 0 113124879711485084778938412989565323701 47846837107729540300656478710799417556 -14179861264707073818935425371616341735293801027166053219363302098798797355277 0 149972785567711222639226054341712489888 145831779904224582638982492054331384490 -7858883975902958675148870716910568849886715364454290356259511524278163189474 0 149645414587952498153666983099744782871 13704220157276187921141904061843023273 -21028434920930019314250990013010224034369858575113715215394759483397477543891 0 17736353177960572257554579838008616919 97357562102692107966178599988673910147 -906193653432036315753245927574540865920954283988963934159546782416863082865 0 90857130170382129381530938871410848855 54411118090952173603782033651807122263 -11322236879761388505467388358682207607475704953879667783720807918080323463775 0 148866480888457585474444745099253379217 79289955142435231856417045254549167273 -2696748392552638972027659097510490035154395794435139614245678412205641006672 0 81264501804863007369094527278031434091 140719590327917280063700592180641234279 -20609826601643680693328801909753213331543029751429303252847992804552787362085 0 152776939441703449604906230918713887819 155965842188535070769260239003503724927 -1982720913783165836160314339574286717353457454306059763998427584714307322955 0 43966181358377856689203475952886912069 124207127986741305529363145004497510009 -5392031147721428807643929573317163467652305519050877877215080894280560223427 0 112091826699220421992050769522568384014 135783703571666375601470712923979727807 -16725833802377681356082528071134022232406813463507276437187553474142036724654 0 104644182690813986114009126237087380633 43857220882369818512872402191282433186 -695739430316799332969061839721282755095970860406764783034590766917187605153 0 144503862203572312918167278071153175710 31051975597075560950473304669720671911 -20467918353159918244572984701002865268623148524754364351442883233722628224953 0 94281966408350095921695877757240886954 118202148746923224761685648778926007030 -15747740767128644440212475140584996848823465011649998888000918277706602830590 0 121761995346494626400416562686587443848 50357221544980018691176398813636627125 -12045186322678183649258724402625577694625759739897684242152924957112380702995 0 12522472922623294279226247692853521125 37758659290974022231359961533183865718 -7181677998379541110113346266580286253588457617905917699674647961359483131364 0 113766275437612007771243735081847503838 22189018265279060870805922591903392422 -8374982908233471399072547701363401140936099527188368838002187419486217354636 0 120934101065077076388974955641402801025 24645077515285808715820546762433565194 -7179966759137693601236078590349807385376343724629033003019517924068712019952 0 98866585605062428711679485828835724590 119958827607585450921421086648212878135 -9788818274029248810408342517051100109321768923462340510533075711627263692192 0 138895130425703616782248978796102644999 49806256650201810776837207507926019321 -17282502516072781325409008427177437782284905675814711657318433600005792397680 0 154311311301550744594359237782769875182 65903771044826236374296376070737756184 -16431785315377719435229789467317778922957202063749860707323840539451074976364 0 84852896814122437751732492001768747641 96562653679275486350144294779022133004 -10752361979161586977978388558566393475941517855901393412039222124144040299649 0 112247377328402898306587532762210779395 142978576629733618406064007572173653566 -3968854480843231230978978714806306078259809079303430406177006316284887654001 0 25992301910377991773544999352362590116 58642665950124639066725555487588516424 -13732457024095116083263193632796717801547562490657124256706090984027869788483 0 126959979203877896228870705538365168805 113915361963739769482880978422765304272 -10767403391416290190499028526999550035443785009767671378242682409785721575558 0 145532819739032463294438521489101496901 40947725338964426964917049103305640209 -2494795378364660205495546552618294681132965267103659357938727273697221190516 0 96663421525810862301626761846924659009 126705491863341646214008541449876843862 -3814202705405505857427122879108693990448790101742191236013037397932063920042 0 34419019637293564880148604726442570730 137378880805246888416516434011193858023 -10569360575458734358882715370170549980084696735957871206994916071301762159586 0 52382885353924441081853914308095387111 107991696241785166280846819988338609368 -14901830838638823359673086111656057680075776739865935437550064619608934378332 0 114260456529837908012359528572781045629 78599034570255601827213789876281400269 -1242640309589090889312737874098431223995413693320032235349830303256576845147 0 92626492824507117674059493543174558161 75713634495179325993126899304205395057 -18452849347797976290393914100655405888198213852514547760513410958495919459764 0 63725718978994335096810834200841512831 65491846800073682476304465293260003844 -16287943820938649272697148321099819570655892785765358723679103606625479478114 0 32543450429425550739213179307400893174 51773297189290466817351366634952925327 -20813845791724828915910420595290626071663687442948848137872121552559451558524 0 55396013949826414039762061876875875207 57748498938584237889058880746879987096 -3857424081266674152713453863414901377160887336865271763683931549970777447660 0 131247174801095970982972545350240091335 89034633873888702840264228580166067498 -3578996480426991332586299957518757559564501022047779374468899967412492724996 0 87376534324479770316342937712740259261 78901540959793320630203370877575527849 -1329792089774948708023134307530335168460697947482455748298880132582199484494 0 2242015800376193019012466434887850937 34565706390331212355711571925734823205 -20050930164905529460116959138227367858015013014531009394238243904841584480475 0 139872586194429828108124485687251456365 132912192152053043595163370600578071350 -14962593156635227156684759143872404451951812421785866179734543304709869847260 0 92013291300560542706943508739028115942 142576588794963832793094408621452675841 -4377112038350810816990070927005376186016222760743943339888013655875338774092 0 118666536106284601382983520270826992748 14511695119818688868944248479773622904 -7540971497295583090260078443111770560003909366355181253590663090387429376789 0 12531367043949731229768469747044257562 139265066278864904819055402301612045210 -932952018908963391032474122721813856538207130564426172438248824064379347636 0 117480402339006753147470024535505144040 51216051996296197850806470151684102974 -14358225542508714254456927000293790790967991515374823357521583571395776841728 0 66592065569083249123147814471753266194 11600003626152216111089080649462682500 -13844261426616648534706667836280335732364421458312863065806558418397686868236 0 156275943555675052122907281789332727398 9903759859488934242394755669776602789 -2602009587115085014427414764857203968238987983962564553699912628939300525137 0 59552437627322534115452327840348465868 102638623397771246433957239969645324300 -19098704706469628952501634490970415978708181159003429987920983536772844306979 0 104018370472111473581189680931867610274 35220775615257671657869105394694087816 -12476623088358514906391245306281053209462246923224098316795105846247377235377 0 129048773803102581738132300710949082596 141270601517063895908453282141491221860 -13755735286099926114351243417465161652424897978921212313271547058160501627115 0 72229448983796233731973622803550458011 51591104694367444746342290927590373461 -175047565604051867205440590848154654567575072919523864545207052228232067843 0 94052369104367893752965073445076549688 136156281327188277385680317907532570890 -946943762589005980222260667008706229381467777663413883527330481440658625377 0 78518981836867665897231578979366732882 67978625992711811096535235662141023070 -14614078777301068041276973865806323418130399089278364415596447161155172323687 0 133750803461841045516997669794261645563 90381355632719424197697095782398253094 -20218055410992406361546491625935848774967833826816131225182291496934001994789 0 66414483671879349424245480692880301521 24548524262045835124756128997105161322 -9129571262922031657880910235631445613224491346140320244566742727637817971709 0 125606114971928701141723694349928436783 151369989468028575032497858220706051600 -6168822752583422035206841188328064020987446675506356935740422789648121284397 0 121273504240892803986964638769826865917 15163167142101889983355493513455353819 -4748208169759658383303296193330831566473463414481649768673096984937351213287 0 47572104554386755664017410861546804942 62458257804680390673141945212488294177 -16034110507154434476276699478721943751365407884786888269619843235877677515936 0 138995712449129025444239148158279804760 55622014097793879671672766914812419838 -12032282239678454094067558924752476921816621736106923932269272755245456206793 0 63411100725502184138316756374913085465 22831412602592238929954825337211092704 -2578237973402496254194030718716902506691297517386123026391366105987867406795 0 36048602786360063143163634358628240729 85626040230573710459306219864694958849 -19313092822646031770036665905703833494827484546969284553376423960785711239131 0 155589758567920785790000683430766662350 105494186980383165988899208550096708779 -10386516165051896925607922227954841295392431682028789063153732813171549626291 0 113394060952065272380353167115004434263 53784352421133244239717699492733547483 -5329433303795331086799840904898400126593906595274248453445814508833355967355 0 27183392047065542613759176509065508737 77352462353106926685242874255443156471 -14537303012501231516515531772082064417192873686045399746495246481173239272194 0 82061017158179199815468131251611276806 37952138891693304387937828544858392030 -16181855215265429345469492604615520249423921644762891065466320068612115579914 0 63563179865773716465643178153031475564 47513775281653302950875409491560419049 -16927390215451690422887146118519550050333170813358336717288310717036814156785 0 160741430713730384992446099992437433868 105127177076222001219688858941100137659 -17431569216547272982209356934259387851827792186612702551786841574440519443758 0 73174636216094773882533745393591293473 91115713480424480955472681036214889271 -94222236574222229896648715862066253932052797289694486694653089364884127728 0 111335682481465707245904059824684533294 104755678002413318765819414988169885045 -17261845842205114164495882803885688652641598441650321057699465347961722542849 0 75926734923618153265116610638456706302 109082305895808098971688224092824499632 -13743717227465698384985262595260289041851388301995051967197745707995514603447 0 110604936613436379418854580622425040980 15935858656913577857757615363562116398 -4283168142282155688731933839887147640396829044935386044266283335093745193499 0 19358432646827806836843918173651861187 59230154299231573133157688015180568714 -13502081227538851782817745575581920991665901739217503226025613718141465255784 0 97676119155329733097340714852042911673 132513546015191570295402305916350587591 -7978651299477657589531097618189374587323178294918525791795788698398212618892 0 109845531896460998739428893108554151992 115735878459626902478757351190656162148 -10432989259020212061920850389288591543722105065886241722140574315336620213063 0 151345330439917421732275578155628743972 130061836495956931463265377631361666974 -15487998508745790315907408213265647172622516311941607149272766394152707715595 0 34747635367614981252679917810061693685 123313594562729293700687455821221766634 -20912123749755922506172243487374546585742112020179580363824451817103128383326 0 39468018031737871999795646593307149781 23510394200633607410375873661667297990 -15737479051623110886184779643793170278837513560612777584077774000724532681987 0 56145552661660772287665659113916050494 113665646557851424640580738392627314683 -7739427771092944137183589620322912296745308672547361830727978973507157040938 0 146052060320276905701734945446834551008 9708535123199042695355981069250450124 -3332155818144923716425006864362238696609802147748628250339125038404088343340 0 127577581126419191696913380371343185206 93659537552882762395505063315413999394 -7262198375885605412435047376447721346956458347096032548506305531446144507892 0 113005355863905731168386423457164867858 65743618782822733276233708845856343589 -15334643508342097750868298655209340849701778853514900042405237926103968975636 0 52502787718594727601679758535992067778 31347578297500227271437998597922541885 -1383634665416430750551898358420782567485250847533999250798065265478733142086 0 76989479324485844153734949426596762091 139076508287217768721218297924622667156 -17190942895117078389913518130609090572877043303431009426558478772585281331872 0 137275840934995308121269050664738539681 9520305870909540817601971921226349077 -18880577833446776349404837636804471622732647019849991759544365715397839288330 0 148823160109453749745922461464970753762 129544873806613333203525285278180865379 -20783740819607263465918963393738802971436840534823156986258351151313176027701 0 121394954508168709025250014096517244158 130728578326093187626098192433437602867 -13566663693299474754918266840914487848910689348639097441710977753956902069385 0 44352123071927231126646602054291124043 16750557507535973029310587882526204225 -10179565406080644010932889753272595447149870075450489933396905696992253283290 0 14386311677194070670946916383226021107 120835393857323413042790852554358373498 -13597007992295093863337837467174995574235901794371174877707830322964008878783 0 78949489494833771898024711000942107124 11795631133643714589445922440300349113 -20494482479637184021932316757564960428767617463916503465631023626293366699170 0 121201782865264535869607994907022858178 145434587845453293159049499132665368323 -8809451198621911935374909246082314038847449162831159757621011453929837256309 0 113345876352078229477501507968801863935 29243552907450337343271357238301615103 -5633114791604512368052266092685765350089870238498956281129978683053500270734 0 93869192166705261595473004800107876020 68122687991723807350184978277677863929 -14609491961619771792391062115697791926452845435844033137369885941967175751922 0 147075061497605219483446120034822923124 17242279455397742721514422285986160623 -3117058665735943896257347140339012327357386317539186069880079862787697306856 0 50607027043671763666851056380644297903 54024232440615119800766116567706456411 -2398898057644818779520204641614692524327589968068698143492222489820716545401 0 122630300383764611621652352849327635396 11629416494398872206891901934761683366 -15999364355906040736243276457645282983389371635055528458960936667712619848740 0 18602715347467971286115044151669172630 95052143904841222357374974588505777762 -19193066433326715712738299606649005834956038798186692726767697845374711923507 0 88037175849297141368890680833418925378 135443324968105616369755399871087130492 -15713111785558355526083338869564066441795359319960058742891063549609862908647 0 18719034353325412205551045645103282466 102956684996012790957769715161899668789 -3675003819329525560938729669837202776555463195547674246662780656686700823768 0 93855923621066353873509210174074556612 9188349634756614463021190743544980075 -4631409188981836709195644960134868871741789051725741925172551305713102932630 0 65305679104824727496452041370148998117 85927773921048153451377458846843305987 -2303093349719006844954724842169658306283569785314711016568715294791578031663 0 126512120387630928946898346944355662150 121374797576943566885955382890631541528 -1921429612741320483355895653053882474081168548779988178436652234791857226897 0 46752128281649872581087570248655867074 24600405075099309423217052599923383210 -19247180283626924708550984115167926715893297541393238824277558810775845011490 0 53969830703886382296098569909796560324 29886719892851469332337321244486738138 -21455844743513250600721206231975637441144657921917683497133066755381906576073 0 141317677799118497655287613723036853781 83892904472171312154498862934639027760 -19616507226215142979355744144670207153355428773450485502810594052641844296650 0 163269758770837213639218042474188949850 141854939823615851649497366902572001801 -14767489279144171355038509774788154681524978069104744675423532513554333414083 0 19884901812154281703152995233433099843 38853214452097313980420077670638279501 -2619737024127451909918400690119536273385745831932370620194854487839812428318 0 120876619724924653795213300420461151053 113969386358009630874926738812066430003 -10773503479432411696222447848140769798857795955352927109713921437690655963067 0 124175951457093499935864470967489786998 116728310890895136541266465040328859777 -1607285938290320804245061649588169082773442372476026458460873946970790189646 0 137010689957477508390438098659182554759 35861974138083674907979799324475497372 -18725064971259585419969592725920568956396216593592407597291735968580943671159 0 81800680202315887031080690401366979361 125742635102822943044399479581316827633 -7287131213074599027805894772673279897607965662807822035690623596250292871793 0 99500372346727821282124099076113844139 137811445163967929231872804261972050156 -18048411279259147201710597640512306635544996420842944921847732752985450702420 0 84464448005984819969423684271215278150 31859679167615194086434434479817405517 -7712684366262318391631468847344389640515395084403914096504052565277817789074 0 93622524305751778321982553192629567725 63240057865612481853259359347677692140 -14924490130519536368002362063340603186648025423011223476284575893127132508618 0 14880968408532448588515117801506277498 111698688771998702618726716349105985131 -17642553491802784770998877532452075463248594889377890094872699756135267171277 0 114829420591204249084593399969335801493 40168685985017927355365635611809692390 -12031616870347314629318338728106937231247436180364129740218144198178460240024 0 157713085830331743652218362839896565646 149008012702872258972969320552286440966 -16233006021562177163577020552847244690298346727842943187891576158258530061777 0 147615375769575555571149456602492887772 149975773983621740612420703613396293949 -14283733750321224442960859371603191333946737012713984236713058209450777218851 0 46294089866792694716200790345594497271 26887478793413603075330387366079252360 -21716218374639038467408320424999103935820120619960082969934608273951686389172 0 21038379018156566145490270041233743795 114608096730007406823525216783230153145 -9232892712846540939785586720559709306726202460078910557707727217915471045387 0 18078484475545187953679535522462044012 117698606931668916242646247284401920945 -14975272857566211999671590328494301348193136040586018598021367585364759778958 0 114025182759445398849849936531230479981 98842150048793827195282495997684938532 -16417465731950394245855013550562866342410000991655612002290516684419542835378 0 127084648177422738799322701861171329346 68979257507170908918754068396306478787 -760136144576822741470379994853567509187728475389740987713668025862770298873 0 135472089334245082999937317345117552119 34073165790252196828156549431788613083 -95047395989638692459029373663621417506904154138811084424057468095785773148 0 58450600358003271409591034427183635777 41040211119481338551308085071220696272 -14012698293380562241213138965291541704345661401294642745102580514749032500865 0 127208040916926676261436884787872387012 139732546699999600215525649749562191581 -2767477570139605282247511905027424160587516943951671887521188726791697476193 0 52599428192146791447287780619256693273 42823945622333519614708058895122340786 -2206183579060822772939485457359326187398643500777101451041546611632003083517 0 64789843434164254943464539817961545453 58120673153282339982644102991956397912 -11246942289785531886936686515514363917003895331527293737217218221988860333165 0 69983477152691420311846145037237553017 56940584546538405417992065057649926375 -15663456545511047595954689108405546334631586477094453939381073731331233655828 0 157408456330176850797776484112009215101 94119307369376889847835484015891326343 -11303545237783463234177576705345532690401171237332152146952779423271083506608 0 111458136258518345516784379018384244785 151957240153667362304961618271762897096 -6379314440133417349888413656543584723693075159127730737860158057614690388366 0 109870490375177536927080156363359739838 71205998442237569743470277527675877018 -14887603261388853852348848926190100186384194373983784177666081616022424407972 0 31704868865913231731696222869862404745 143992263897232358593430316676738158857 -574535712669861253314999954218993783723235095838829451052466089888985503892 0 64016410725328752638263778390559762655 99891644577262283252600715823099800218 -1697833316827616811743211477818214783575384779807600332653122323252224406529 0 123772127955647412331454454845851796141 69220466158949185560068656886953687167 -5874258862613952965406990383279701414253554200561899915931442971454543605710 0 138698379771989265512178641321429146126 113137704112141670613249971564645126507 -15217783094363468305765514142278933312162396941189667223535360854132094731852 0 126673529925586366900768062398221990744 60523484586745046804841607125088822022 -3280719579163787883144484442005653078651483341421475675295298379310174163332 0 131374581015794430755677704864527750452 33640350034250337719810756472862552711 -7557540119713944248357041292668245075918070375480819372957790396352420100764 0 6975281083703600663016866156557716928 136605444772520735814301168764716431703 -519062686134438967281493335150002014662561562168030528863902382257385307397 0 43623228405140323962081012818835996531 112695829708710741718182559959135429467 -21381776856575523277969432429872000197373707331940938442925959530051717652638 0 23914824576700170967816199845204756330 97940820725031359423236280633171237104 -9809488192616862055406736515276574803069239497748060764284687345257608553623 0 81961000652292697849175914756989968374 13567112847083514280395001862575195945 -16334082264222786426266443998940400159561319371444008147287086504766636008299 0 41559352839907521937447864994809959634 86608121681461789756086166426990040116 -2313712300746008372971757520203374612978567917995617352654453644700806350049 0 131099973288589194416838371306115476240 123518088753181479019798354211667596800 -3110920538794684173034372445530530101059789843873289866553580193056278058755 0 135223880958496022452935261544916680240 25515320347893962039295128742334315052 -5472811852746125176811506279917170399025931474599887498036277822970417833446 0 130120984182005832806372156552008570095 68838627797271005069340964764063824062 -20130222783043462392447546923371880940183301424791856871298122207055723203385 0 81609824609063338534014403375917913327 62276676287306500941995060361098786149 -10174700064534775570635757986145067875560261369620388674077832439044470141841 0 89646439449114727124217496120293827797 129132950395382682111662943028937810981 -9456249563933608470241929231174866736527317168393699246070011020784058920361 0 56700088802021021801407042260815411004 74027073445852126561657868320436591241 -5020634526076495357709120036876983137300172837294060269115320493532659523863 0 13650806895201559419807081489721318938 48421342613803388825949686171067555773 -6355670664648695229755588893358649914817234661711380296045988030227099856652 0 9904288781499710289878501071579086379 28733889794115785646316794528905192929 -14813635541433070767571363969951860171316792668277265933243959465158977099105 0 71933449891201766371215023367514658093 143962668026664068344623679300930357424 -9847558760541612681079210369357852429994331824689401207881057604550971995889 0 55510844728808675716120586480989554538 147648687883994664704567359548065531500 -6215381633781677172309053301322469258058850209010108387608025285733807369416 0 79804904674186709479203072293997668595 57720891335655367225829930883143589386 -13910520114109148801041157739728865743926733972177287749919097280222013727448 0 46675549097347845739090747728829790912 42136066823763498421154169456068454220 -15793757844787881222685587097909465097792699967620165627730292425555215645029 0 73170366143012063644573830248183932873 106365247294753297047383030366599824014 -12638724722422037695531299054674825253952005319518976874068552896177905584696 0 122450236419039927056627522305382264043 145911392070154925224975278774841915754 -12053170666768931800841375695665603918454401892485807443845427993689008281082 0 102211302887713314359029646917858586405 138568589794960528776144854435566924612 -9141939185102999400547144603193449076822235610503826592222955785422425238947 0 131784288739314219533864451216860716145 122115850794110967818777553846505789042 -17565468821175577393360314205710495583062328080779794243295195647002073280133 0 140072839207810720795347568719117980256 126958302084974960262100420102305402044 -10485117008719717199986648049344092243464713904679175749803014550725169915266 0 115889044790524021991329353585278451745 122652016571369081695533003887163700716 -13187224576351437816328434812142180103381823614112387881828657675019048293891 0 107603170681509619247463016565838587076 33405879491909532220253585655587805303 -3452918176796836097087345349946007454427860851281699198723878829671734354785 0 42771119057994474106326051495888829013 130603877220822002893249712790964449743 -19630671363083492898191918538506511510314793855448412059687511710544454054052 0 70555995738650455521272199483559081139 42093719805505507775795080334138112436 -6297424179694732782960236239010721107176015919018520174922779153404174083206 0 109936255979801612816362415467967835935 40189592657289756303846420069537629018 -16916688441734111643324260351377633854901228354848306499798542071292637916688 0 85759347352506000977405139213304143718 137747231823657012055512527289649667010 -12491469575415615500328138260422762187297878769073329500612970297121334532486 0 27132685058149624844255862684241183332 139015608745923688856327167545040935670 -17050991179977246384028208040477013691089607378489609856754362829639905970207 0 154674124376680819563582764635462658446 87286671992621291884713055560848625500 -18107728879077347734284347235549137698100229763847809609027734726979032940018 0 117855077522045823244807254782208007651 75600762109112305034013038957179652582 -18281468059820834050165442337023857078056275135388722705405239715510103384167 0 73326381033720330477022693853875178493 125586633655337173463140232056217215964 -9607554058641610243186579164752090747492159075917300358556518591842091196265 0 148820774984666101949686656554742900238 34902928932385890267194108209257280027 -11768494077393822963124239604713140894855400172927471906411582857904174758092 0 62654510722712755292575847539664388957 132329888419531731175960834449016721152 -13114692655366232616769018450046764566670215671260255274189811045230550349520 0 116961045784294012257912750523114190682 64470497795552974232053150154000899845 -1508165641316674030986998746910657444633298507421469057212452576080889085216 0 85476347280104446499512313309337602415 139026143535963820810739584198809405252 -20335539947458225880177683375277578455485585643843099414432043674782068409701 0 103821470575139634963905129476281968001 70016309721023088000319215732780443662 -17234255739004456281886009223908545426058159706463080407150287986139693880873 0 19015386742995532892888273587091178387 36081581253956559550474675191120967623 -1680995620338523372413340225186103672303218245771433283444119769717957555211 0 63618265676099220233586656226261869829 133161437278467348547250161932171926127 -9446024589866666797561784098026043643676851364476586835295419515198289455974 0 71401030347272021866345119374616698162 23981680396676724694840251193454074874 -11560674309106806598671481713036822424158194656284153072266823933182318460821 0 47884671152119885617913212069185352493 121034284615437087224064184729084985802 -18466024259838476694726843200494125107483559202364958861845549018476463684414 0 100406182164437271647984276729239386064 39362226595813884488773156738653173451 -4706557191904468735252244603343564699525999230193185794129304704423420363057 0 85578476035883062920615196936395642998 29685071809921600105792885140850094940 -1377099653243821910978614033340710503812606571003355876022763699505088284206 0 65020972603254490876059059249644889632 33349207792881784743835624985145844553 -10719984690633042357845477478408353398073901319742703636827855197466186736191 0 64151484044475652316073705336039944999 123884019661469336822894018479703831706 -17825817220856468085757517486928102167675350376221289960516112465413667035502 0 73543658370488518830778899425898594017 95634559672210977451169988141491058363 -4713722054924156168802365119994164863046046222406872897506526029657952639248 0 95884610057407992036137511736870200915 100933021398028013951689243121520154743 -1351390495304477727306945366108082546454448534825289884410436716107859958690 0 135168988849714289971916757537595594314 46552365594235303573313824986592560928 -18742019834019757564182555998663715905458092049209100571348613671448221808026 0 71001620068909415622465415267207072556 27374165185639148629987754019595843092 -1711919230023037148965391808020614099576932525729661746333845968160423103076 0 98321154207213869109456246826964231753 63910303053523921927661666272679021923 -11494200469534503240589725552802719903436083377952490182640864666976761018126 0 87602356977203675231238721343794294093 89395000360755254767932640092776502996 -12112041980827429439186874511409842603836019519259698234644383600004772218715 0 45826936807017636783837690811360074054 49129360275820285880392713432887953849 -9608844321516261599276766330028352541561549321205878218946598502913058005333 0 108440904820164932193845410489576253437 106530034826724340813774055358811978756 -6436448019706174884865996994329961956887923025885620906125673717574167826039 0 117996412965904792696070700739011608437 64817308498377149870418421361883498295 -12444673024064890052304529013899405975026498424534226111897310983616869471897 0 70089713249530250448047989655563658531 99385138487882605238750761535782003305 -220266200692322108117823631367234292096148432065782092922754279660824865490 0 42053003665516676722763162528031918327 53211918728436633411432307688735549680 -3022286362640826312307493622552007563414666124817699700404184201094350103340 0 13704663719653236507162180484610363235 92294754022904149570017880275089753689 -6630154772945129712084629972943943907483588063687611117063801371793744560305 0 129929179260478422110640261546990608292 80198918605320552846901781461065178386 -675422973641915333657904478534788399841232556489858805541397237825070599733 0 62557641937105312736842231653987522338 119772938887313402410501334267309940902 -1407977205010318346895561499860425758051686756673350926488716048638803671255 0 133562250023576694488224985246122084645 70604807184037854049173392198081814564 -5889129340030748053869932405212357922118804052425342563295622289697196967090 0 93203196797605551187874916991441614106 62888819470843945346334648104966716484 -7284234128647237550911991460793802403130167358137662209111430090407182200608 0 56252065774192277914349382816533551794 62484597648471265899180636335872114393 -7144308047260501836338644733409117101753634467192217913319400075590310818780 0 39638631491123742532692674100251589270 115949403547952842256889284338354809077 -4807703040255999510204786777958664804559900827966843078413760628523726409097 0 120840525094150253930180572861511540993 43415800015441705472454954609453368686 -20701193029212791647070045251735207258996330947499809705861170279312766645456 0 107036892008449568811856443789399315181 70217335662881308468866742842745849200 -19025762296572972895540877149775769279289564568067354084468884667929662526506 0 160096229211273299744587527005912187806 46153401618976149456059609849194435813 -18444396151134945499022918958828991544665153071533769360756405363501647881417 0 26480600961338078718429444373806749616 103535914048337677460229167499909534036 -5894719217427695375362266633867398614287414933348049883107243319389090325252 0 30508622824196311963090466274893039939 15909154113483941334939088221776409917 -9118863371133207971705756436862883548236114878004771595988372212210282138956 0 30464807557752197231292134587101754874 7905945572735906880438758392014027852 -6300150942279902331305267239194027677093130647125154877781467663977211983334 0 18350261708259782088462003029845793245 13236719843736815233197079309899340077 -14696883030397160965038303556876606596653650337991934116062322481082523246267 0 33489405467060888126884328990677921952 78493363165667997660690811619385454705 -3474542525951462238181103120294905243438703265292704960213407268988430018056 0 42685638737834131893436429207880024609 142384693915701556145894516625141069064 -17979208697961452478584411310236475424596137362101191950632394617475232093890 0 123407692367508278642079923969967650501 142012200902814420167014602128115967220 -14194559589863971922171781590964380489153374887425076588625816455434433494867 0 49390454939674048641217301320555990643 81608012418550644953617220296500984895 -9322343661403402958141455470624565632804316352194030837355246411833622250553 0 141823740222534063090259748551406634073 89388136956672272496050544655923281763 -8007454490797321177383721197776403841804165973644152912236000900010646514383 0 150650034352630219726318796943380404938 39625483121933814010772034954974371287 -18130650818761181265813555857950542845726583355312936402063148855337533764639 0 142402954905707056236329483745475158761 50806830178344610117303101479792697777 -835976905959853126060544202332928346119690165543531807580461126726711503920 0 124846987667904841689310701803244627972 5022744123070697789217111040682998601 -12268863586969810035142227314726974582602747671233007040986643004587151533029 0 51194782214572904817071814360656382342 85393044812990354995579690309925614139 -5397107722884830124513598996862397810196593139438821297834857338865093059932 0 21061707629537500703929822354679463294 67414128427118266862835784843773755943 -17189919703711942195283108866037956137625421882614519281016986768715747114353 0 99217619281340276217391392902605381126 39344543290956699063182690072593321438 -7603030506127310814333010332526140291318062930514011610020891628388496722445 0 109718588180787186854010465298235097969 131554978876841696531603137604631902309 -15446126815546733142629533695127240738926107471109964855141715848160470784135 0 21933940825608434745124748143932967433 136645333736053804674257455909673257398 -16700138544662966039087423601705478620370454674015771516375459632740690629944 0 104413141890923743561842363505723231887 51951972443508072021256618047389579532 -7082673320251295153700074738537291904484077497739235956989199992733944336872 0 57556401886775930374383681912955098877 111823969871038849117443504862296689053 -10080157544731033909173739671631924171283024989362813911015770855246674040199 0 83467168364350564124690024003891524965 37150509520335807786853289124548680674 -14393642747532446047142417168703806296753852689005526533085326189678087097862 0 38582608710803650831223911734200988225 110235081145154811688214510937031962638 -12427385200632149419188420971647807927969205840888020571807510439926679921769 0 75088160114573292574943183688851810163 71141002773683767722386585059255435876 -15949411836050187414591821952597349193457732754943311260318171621509312314464 0 86449897940710649666554257813365078449 57303595208712960428855630737727377121 -2835740943669527254215575234145772175141043157159173737925835347643175830498 0 125835275607144446261102565146776690861 86171058696666885941103343509805043147 -21014586382350475768270864212669663700500660311772852885401341723340967421869 0 153387610107980678400524968563086338073 75961584939036667398572776529727168723 -3046085087244008155860504609584749833692798820509562358196611162921621576980 0 71092160459367907900570537339838040350 94208085295256201445488988439986007175 -14659215107468791425694356239659775610781337432617037909911069779708077160624 0 23207152090342641387320199991110954590 121304816575251785718638192418308918369 -7114328177422545372950171056101012101926755029630478658631059740372646104129 0 51103957643736197160947563521630898607 45624477892336038867203581565667622982 -8131054382393798372050299389611388298474785452945527454441658012493679356275 0 87769828685418239190684633338845337193 19559202259374517781925982128979635867 -8014913052629565670847801343718568673333745095623457866972282338984467097249 0 50594236402965908720439806600265725783 84803724430704448258498904072378711092 -1532233701953291047460583047027728035231969971563063186175353965776686670645 0 39568027270323537987688873057376472509 21261090318828428222243479985908993860 -16613246479056439200240767711900930418667419165646080632029998980974592095500 0 148414357274394632628708675039392292130 82310027191618895066897682272611137428 -19731118012079267420355925574630272396275190742265922611825136284774763355531 0 140397361111251799367087183370487547304 101780718947322683542608685447859793611 -7480419524059407122821825652591420950423091990780147665476220935567284739070 0 62198750972754182526570963452509717305 110026898954416225213915490573792057061 -17132595578044522502875197873590327996588780148758753584611033834336014399743 0 22220605900661247016232099306213180940 142078586923732370788104939703675868171 -20892083205614989523933848215778366999481384478204665482604419677899906803448 0 35795700456693681186999432012289679089 76630892482435460962803905552689323896 -9259794233046854120007746399553121255001312349141018379932187091544609969679 0 82639801301736637594025448359421318738 70527343681341766759184192344899384785 -3849014457017998940904221620756625477982905520421576450031869064392338390349 0 10546371464577627013258958249169379656 5463577309338164553659877397657789447 -20630046893838811998228830024974589473075121048003492687888620891038465443132 0 124700146523212302465034169181536710446 46868955249138160650163974618277385000 -10846220232795489927422083447846816544690973345585374920481680895568708219304 0 17848930127738012458934756071047532744 45677358949268882924428775376677021178 -12918715329330270630307614475278080889485300121121111700320332678511264951785 0 123946140105155763921926795946168348595 132976370314489418408190577146513062061 -8038170953568702466517610056903368177593505764542877507640609656043046837550 0 39477174990461422169369461839016361940 41881274948261351134785743410835735731 -10162202479916907613432795247933983608624492065094761957036100121262459931836 0 98649359020695808542016096648552337072 130478147918315265134397386504477928231 -6676942927666365225785774472698205489916427788785139701920975553415416198786 0 84898791394754352652615069349505666159 111700163916758638627537021624591746550 -16025763464871994358348152524446578035725859571787285346193048578187361780554 0 81804652376350937457609383366937857994 20259867444355778587487147794333255379 -5779921734936210496289244605054038340015059592258350563042139557427523001292 0 47717397796833494966929189539419624595 111154120777448085257387409222521570811 -21671499397079085744453212136569955933730751595443009180479606310189065128605 0 115597876916700963685350361655657550962 74304628189372000319833736512891965767 -14621993402515164647827247521242310162619471446251855253344921335708662576409 0 70899565327853160835406084497471567622 124019784769874794878226284593248844991 -14212457990591867062512025840731524051593699677372043979367592419330158508851 0 23139407453152133340431188139689448082 113094484448661224145726651877290671686 -1928725727055270127843452073201653499838343772609063545339271712058346720790 0 96452730911079101118282713407474789731 78714913733359526381757272637733001725 -9775470669634334221628734160176590848311711320976245320055968243440367985367 0 89337104573057107301035613698055736246 14118465341542862199805444557972934313 -3592285698188909954678073895788831050391036270757299838606563030220376609032 0 94533234910338449644287746070468945629 46778318548491466395277024428902779410 -16453722887546103295331775033485560968162743938198749021570792844012482181002 0 113480990516291762445028582440343260141 19034744884027386817771044773400416672 -18376699678848851246641731212078742829766290945884220850123603091217280287748 0 46725243124549180275875218813808407600 77289252626263277857338390220108835688 -20596671779757094963256433093897525924370903616770757609793365575713421618358 0 76515504893233915912323175802362637041 158451397512634796629461346338449495410 -17304266831834744954943487697875327656793049703007111927951072437641403347646 0 127656370671921569657032039395195188256 94577708557295345556048323425481643177 -13175886236495284699133969382828850951475591084680239899948884674234935739672 0 19718348632729300124428719225934261378 34591360793544838899277613376057980051 -12797909950765129320184945362836090137355941973700661323360437653051512394390 0 153420043631698302906309658600459318877 47179818498395290012029349023487734310 -19027824686229374811681293312092863039880308413530199306196094224189307524081 0 74697553459032053406972502231969409446 130278818275193973255768884784232068870 -12617616236658346098390790966390388001769101364522234897554024800491459107968 0 135331884759860327266759290333649835655 137782054289640448100832272962757168368 -258706760639005569927988164668966418110305635495997289542675336757546649569 0 41677076318085292352985836978447392381 60059100249355965079296732790355453282 -17093823353373158381138659984010512794675261419572540875378036196917282672094 0 69087434841844857270469906352910864882 16263057546578689888364150674120629247 -13709196197159228531870438468134889204564675450795457317989994134038451149060 0 83057298336494936239503587225587648614 132053380491293947358765035244661107416 -15940282031616826808117640180436407626476057990972932493000944703887192086519 0 35261003720711229107008864639055351548 40575401748028429568990267836137685409 -5346879859459768425911494636474838654258196846008776528433696344293281539331 0 78869414305833040249144434298618048687 26566938023567895084226851988398482448 -13548073043884110465725367291916289359736755311669911700584362427483679947539 0 156244599340467128781259119517038781819 152500682626794171221697417040437849908 -20680775381985770934768786980619546003887976417494777506933307308576302395895 0 54936126807958012697487789020607360845 113598700303788647536390686625376740336 -2599168808719260672646526832663686806019286493935561475659968268858960902304 0 112944904302205314273326793099323193000 81408913735023820180232689147778638739 -12271664880684840329172723776541130288933805075558338016820106049148895698100 0 50230633531507771706557317373984392577 34791238626993588141994530498828286553 -295948218187060281127736819109082492039955280108704058919712607539198081717 0 112342405098078761337236026624901017273 111243956791005467154831334953335069399 -634069668304273974023223063675226392518109714547219262898602761641797979521 0 36309771136444623803207547318160323961 1737316873290664876757653202908135566 -147596843564016447746013438644535851908019770244356027247841082998384131729 0 44012602339403293302042109858686666019 96552737622575502143690555095486606839 -21309213377931755198056710757455131902310095812839483396776686318382056268503 0 142898671114470931764547387164767628646 79299459214241818444442569070560838530 -20853315180269971144442150203759035166803985781617340573420956068695922770157 0 17976083035053011924329901239018301863 157082085946608088485870372359360666491 -4424333419834519218868674868191015285766560259164690660125544525826711451600 0 105786304656770749847945634322136717733 107507600824860893323732496148222415781 -8779196800748484000354814074798263955619801408202816114206260184814496596961 0 124521704504077099484240631988971617578 40072210926411412083689487766975445312 -21075284575688276184227301818122502414432953969075830105489866813734889833868 0 120284211711410019819903754864711169054 78985415312584812509724794371405796171 -9531739190558681867221877392547295734903751870014671194511437892468378726689 0 62357457353425538341805428239294293711 31371059906253257131625263209128505290 -21598713505150209851470711524207097830377089865202016871112590306759206828142 0 60406111817269036239288191258984307762 23115675620052992411530399403597696908 -20305625909368861239343532037774309151312353463806575117150780825287538754167 0 25248011187717839749558518234754995201 32286924639476452091482225186647752113 -2799773693334800735614111979666744302301032075576672690244748407205285700737 0 97373888578365405678477201304689485457 87352864454418005421581125615678826789 -16036094052118321388638463969302792242973756536880122274841991207823773499820 0 81795854561161248407399400170640927738 52854472032778488234495911623657814335 -5980961237254902151080629214697544602963775820268170190953179767638481226034 0 93258397306715150145582516419844171742 8771402525799169914560742087047660715 -18671361586951473084771934079655568954328312548710261760692976496048627684710 0 119519293610238455738193507439318993407 19924972346006504605892438098997630559 -1114168463884004060750819519300098385606376882090473823311466136113150545834 0 62452474146148448434054418782688616290 37867332317586374169682050635525381268 -10106820198315400342445047139925470276832852876851615959005960981887810296262 0 27841131290037767159619602465512320205 150972954761757276582780738574777734335 -4031202387235476662277801988203446006789684902795119688697402619268118041703 0 121561464787027065855503554526092689528 81888357570195005752501485815760880849 -6003942783194355963283121458793820934409702542415166301524541716847043493603 0 61460793551466614063072278457042881137 57256886687430909639082705935692341178 -7713957900172956332176187710363823595160229178132605941191239526574165996193 0 76841291760096725983824196928661658792 128174364368084035095113456581073760338 -21524163590026792167707029120670355073886492060566080939157049743015463215145 0 109852625104500085805530685676003453254 27999482486039972358386033980298818101 -345857085946515896179564631986380141645848589163789963748176600626162485332 0 114252132150461901476805978371173276259 81430193072532772336418044082854883938 -11857625190806555645621858048661223745040806734230627409973302635457145105594 0 69276778055107518475756930748891009615 23946348625942809083975322767489513461 -3008871644317279159620319682565524988559458060956845081130582603810958772456 0 100250165526714859913010885715168811070 38582494267930764840635828438819399859 -3803412693348537265237788712493638219822082379892214663774191342678345854891 0 85656824888014913002631636740674914221 101491310295974032481314306380628400423 -1781836509900469931528186338366918395573840203249175715365679181384367798582 0 33206328260784071811155884999116227645 71547085825883854751646189940851203914 -10704098103625743439708063922759962001254858762278458332504759930223909797350 0 95526271008241115675357887039412439598 126185945568693621663041025033586018651 -5200557787747556003611233354660219539386551333899056684702787359983649515809 0 7101692807653458238150068699170096035 29115632543664211848242312704855854999 -6931768937413903064579254887417001750604128138200456212179002220298928673145 0 67439271503943370751385954446687708004 146716177759194240250163521588796625318 -4216331658952478451775832376017850055994421699152904212430627945111883072766 0 125426489397679591051408186459466992606 49786687905111633568315803738451433654 -8608632406353026658001319514256650745148253402659443513287347238100891695029 0 109167335095764476546026066481950751799 57472249480003378344476665782097387955 -1617647658980019309521230075511533091985727735504963376712064903587047669674 0 6674451037182124862460807800180216645 102010033064025231487296933923643693231 -16765827497215823029988451463295755243276021605450235168855380055822659927692 0 87990033034289610989137588815653976775 117782659508886154496014447106627223232 -6532988517610508108866463848571269572605646962130005208751367451697067224118 0 41021343193908907370824554250979224938 124765510060055392201125999545993233585 -17462756529974897064287333487700926760537058883146158710519913124277322360954 0 39980178905823617258373424093078975417 53012838761846504196476156941787463821 -17719495534002861884418866229455436834527442208339899240124177907723768882899 0 132267372642237117102682683194174483879 45383727299555445368004879015342072490 -14240423849856966253768155333153182762770087297366044363328260204421712946266 0 23708301375116868732996694096839637267 108634813063182282733225048095412904378 -2132570958248122396061738332855991563524378683934704617064753708179947104517 0 28439397841525752338968800277017513885 109898753443952120653479891279485082415 -12951033643596383054832037862029802858203289865461352594666724863595520040060 0 123589325972002486781593622004150176729 9018816504503540973185771305950843295 -4594600987298933763878241965476078442317747960384081993565465893800110560313 0 97130189868373557143129679997631727142 72607712133403386413805016085256431457 -17470655166276153345248774331870913244945367141976738112861832223187568052630 0 109168089129878057607808238973310297040 30520485036632511427974694296975287196 -18337513496160522475942392486138065828928082616973038606903755778159386350974 0 105312487278682078691601878822658927644 20042112451720148536126405327686653358 -16136364663261153836512906569448959507919137908420176066328985679274959630288 0 22679241977437229461624129661641131413 137784173636077969287225905874481423127 -8741670427217687028800705519952484108149992084954391035438326756423392064852 0 98202417575325805443801103963299793245 119166011295673723813804047682313545523 -15071746023244268559039400621930742406449043062528698752373204967299405233919 0 40619000107008352320404593513025891114 11215645168680265064856772553124109565 -5069146700951594526745506124023229988512127387611471454074338119464402311011 0 110464362669231731144044490202039242975 96081276304692915160732135822623746084 -13294034946050463509966536153942653006069504690725988626890961679520979227373 0 136785909230744250870144435189012235866 78276883239018945918625337857429976544 -5566635941245172803405474484776835182694211146335135150257637343191133122579 0 19054600890903436372516268215134897964 81759027125083019491524684411130229536 -15693772801264440460767710816057193795793128850555704976972229991109657401953 0 87777292835589168999061553289079044410 44980968876527331248349272696659351425 -21063661589192226801202951394111315413362170610990736694951877158350724907353 0 133126676753427570765234717823728500739 105800792770228489327204477180931698521 -2233990017712947407152417047583587663476119194372413829939753931097817781385 0 42004698062614968619580051757442715944 55140805501225482885623069339042074531 -14212464712233633711334019901246826701578253404641385446127856573436553880786 0 55201808631173966921703680130135570386 37912966742012418060446472384283765132 -15390541273061290766553076251178263128335331411902827962897029523705676699507 0 32922008580238212568153343419253004418 61467307641350922208661574256310001620 -17837816550645567382447074199323013145620765942753182908383002956990473978319 0 121794411796167952651890904166401033034 89944069851161579236877952047717405615 -4083363149048348832016006470798816073043903871240660041245882110788840013763 0 53190751021370383600316144970061332266 2808819327788086474534143484139808790 -16665816316012041140571882364343877323034079614878061643476944263038070203434 0 21181495252977961841496234426779159460 150945207764257922500314717325813585842 -10083869350481777218687182394325560364938908542399659049303580038296177595427 0 18639968650523909253109395694484555357 130924088028513566014453062678968929432 -13916174145116485920456663721617636398515384835012715328703014814810609851345 0 120913231717300350818439022237103575019 108808708036641120060535641682927361422 -14942871389326112760348428263055676638107111264264402105499238442546681766569 0 145206861006963153652046221870765867244 151248662752759399460410979873337488943 -380113478232642041937470742991042039646083981780051316685418902351333854383 0 79629344363118485804644268501472194608 108109912491979197152023896167324962580 -5780669412886752464734937451428003004999637162856849524987296596586986885408 0 13977136616561082578081206192929149477 49047571552756410220756306333439974738 -21711717020163685915133071208415085499213254920947916054988726310347098262662 0 72976774125200939542568129155096042577 146070365792838152252887917284823072367 -2940630050184667332455014628251036973117457544674960098697196324220364028836 0 122553395863248473268546471611674496257 142108233631576882478353539382496776172 -4521615306840563706089158061545680724954897802419985509927123296804915474769 0 142347595918088977303219768845246568138 10290819705192903775472470469665341532 -20295441997658248244684751171702558602415535947690165689600124600376578554611 0 109784357927781767651861235848790270675 109079926375509912639373679366313443460 -1760549753968384198613410213794075644991929435525702222519319451663936225432 0 2594531942664149030226699132887176727 95170055098953593227841950010278827988 -9128671310626545057287829076703099550849081432876267835180217379782679086921 0 151871093447508185713087912422669840748 14304387241835522591623143837524017195 -21256143045461260785197439292067063953815273368724479573229796187142434659796 0 55644460289208343132026422932395199337 43957432948637467809974772801061576520 -9859307271449737255254842884646555094217119153333256424192556730146914205033 0 106222401563837776383883396018278972576 146650255050051432717818090881283932795 -20361075686290111282247298588076248120206568694799566554337420243727392272786 0 48477817121022478722439109394217284600 127572818919276996897453822860382574149 -20197863205072351957667940718913166012532125666005871325286157711093925224085 0 40407000671871238079206075897002219365 66137836735134378048226673080297085761 -6074046674236633041802727129892204363626509705236938283876075775247677969795 0 5249538226959925526067512787588198122 63202663060581646885832696587310857314 -3311184049411824383134318078041704899425649200423096524201475721981824547949 0 38869540981087429193510804305123172911 76386596681559311549836375275813094648 -1030365272598726380845908575841440773645447526793659746668567980496213507182 0 29960915262529593435704390121124070398 50632049706337632499067769112477557946 -12823162434502753575741074761888240204461169036592372806099999577508956788191 0 119246472734123923545725111460968100061 36653323247766599877102754304620637529 -20046785754167043301098552126254643315149784664640130857469609484767216990764 0 34910653778453858417243277430036127070 20630840970905656394917552633664734473 -15326334123263228775527061258849439685882148106869565386953741774363668519172 0 66500744683300223225058963352885370919 33299359737804689260914556379517239431 -1536827038537102107671164279058250012501899245386546846022428251052243206077 0 91756641454575594718891265740792717604 133196436762387716778931184696517927957 -16631334948691040652655818585524246027973595574537760421931809390805557244250 0 35216793509296283767689105162453876442 51009986355351053331777331899705480831 -310008648629248263675757318269125716471073330347610844044814550643017767997 0 39935643909275605528168247499125826774 93193526020506757036978928715392292615 -2463252262790349257817246462420605689911697219274737975617708156844411580331 0 7157684139797651986643730008993503088 92678754929599000845663986467269459409 -17284406485920881236655133813706512402405976303663558503907854834683600060559 0 45347181723516646264514673965579904277 29979650000616804480788961763876995575 -6482474774432583328585607964771002032035561524185902753733613893206656606362 0 124345662492960828276488359810382182234 97264092822363848903147684642422191363 -21458172575601141568486198665885412717068832372859840866426604752759769998455 0 58800687073352167330659817216034217442 128320042212428601671194335541574262643 -1521412204093457879652095506594144989973431689136036493610215463583683317373 0 143863986568293242598558968031162401866 35193914345694584091796830833653164349 -18967396981099780491841418098664150447532415040261171175112182812620086081431 0 37950782616622189825924586612594824819 69576422446910417539431560512322407239 -12840801243241299539488175630640763675460750870167649360024948812502809060825 0 30069671734494720760804598149124190571 11578020288115991475780922289354748688 -13659311080456894291425385716366270336355100941519581404701738051670827157223 0 74181101614327231275851839396072929764 22373600402897923786618819219476957976 -14971865212535361902888864093738228522006036571937630812220970526572199114699 0 95113838194660450512944642611037371445 40087370226962176680423380879865136753 -17790746179792014954590223167397057998401475418574775448183347791221054701339 0 49224595544494149994701708244339519669 78029117541692663372863262645773610876 -6480541693111832089385132079811951042499749233222751750463894084149741918232 0 57971068577858400240955712096757316023 56375950861705525747656236554406381361 -10747419077457024411696197334657216139653386678990157031656529966251588462253 0 125082033448113404659171851951586279691 59462820978389273262018668201903192855 -9225354699962684185786022969335637997955229241598294665780472613568911989907 0 56347651391149388979476026877858300094 99847101750731977344796558203524167500 -20246898143574537843924603985078325220790411996558030262942947066192243894739 0 129303663503150359925148577207906552669 59728009601109175837976073252999868469 -14222725876183076213457387639656051418262244503289138536699056251877771005068 0 61627140289289636820813640954435167228 69847064697947304368992826552879616415 -16679901590153828885589444152296620580959088044277202147542256155978414501773 0 157762232375646373969579752863657700285 109654336393119268420585438705618660157 -3509558862909900014852847067977352506357030153882981148225364883971050253321 0 64410236925349009561474480890500098664 134719101461404667507725393094582564421 -1131479289025186351795105454265515678999649505267714507137701579622021042268 0 45971283455978390077017097703284740512 66990679327148887408321045060222546223 -13202237181916456215666522491574223369734337299511724702936244256967423023862 0 51787444648189891473304268920960818086 40781390063993877191089394098345275276 -4245373468111017965528521507532593409579731231279701501910232227427151602673 0 39924974021321592156985676604191663589 107602765226706656908067184141549783126 -9200633800167229457777327418788380742780239296629341384552957200874038377624 0 52021714168486949334975240171395400421 76729576285726912636815737264300599626 -4302958212305184184830012307674316737981497271088497726454206081696982545160 0 67133779878311962676750630243491980083 26368993052867615050050016565996008423 -18050650407601925802026431703704272661975010158190154020084914195820191987768 0 152396809521487505366856130349788636929 131724848198859550137175024510657521444 -5135953144290446400929271652604890076355155559442600081001011997077123638561 0 135044675882137410808872665039944543542 77551750007877511977672196963714685425 -1763830533760750783788832873176042423124216623286053514126086083801333242340 0 89271640405993375615750075951757691486 68103656839795995771162377054660596709 -421180974699715662699416313445513743163793539337461359011386490110464560906 0 30413306303721588320563263618348846935 51889285652295541162333134746169613831 -7487685055170244878668616953386915765781923036068896828342096022949318930741 0 32164385373484529671798588737673652560 6611662774270890384072712656338729570 -16920301857509388853964446451965429872358923187109229791730421705080125869019 0 34826822846885276558182303559247353038 69047161675563627734721302969521827228 -6427436323036253758839695308953598476565850341146103700892275775235148590840 0 92562902702077562845891218974200492215 114775888383193019588283747170682767787 -1395212119784206359607418777896584490325049767852891621825610285236194798608 0 134407435233999512527007177967503303373 9914405457481759911629277749959523027 -17474709298616749316471118148852000778169167461188542679052077095740772165522 0 81208599894256255282867615538374387737 36349224568018695238669710673873091229 -800163361278674447545357429339738428377836510283644396957359106603929217801 0 17296837584867811143781436009463247109 76577581327019310692404063884997951997 -8985234309672711659380246905809997696976078301006825155815457594972719729284 0 95437817329605740945481459486361323337 21175209367267417514746180180573053278 -18192827899628612969230263654619743723699111837747195867422408156794656123398 0 130422124428034769092476946136385071789 122088908575943855425234566666606368903 -12749074014932563232721188048428322015774507685803197645552303984383016293733 0 80994323952507528292836596883308213906 51874830477945374678739528510766840069 -19768283869212643843183643647043464164298043783809098922063989418930027885508 0 111452175555404533416009469176934929917 108652854961764793722107935873383663481 -7088166988471681391782377492901703896546285411021596591471237160631783990027 0 44472179831482472026646021758298061014 90132458483810985666685828802729476949 -4196920307434220573974426993630611626685788688571989770906071303463312645826 0 4905009762651119850607151363875528110 4231941445886877474580442315873862930 -5346150384279442190283947786658669475391741362708834390474022348299397541418 0 43040562036515499816352287614224706105 71230681708880501053646036542713051223 -20173679369077087362218946548548908068186286690372796169924990476990342246921 0 54118581707186924725722490059794851609 31724255479438649635268310352852681739 -9798156970502398376823877078049114223883933421793734495365084964149142678231 0 24882434957285251302848630571601041132 16508273467112104165946611068502590575 -7009289112915391260083892637920964761614574168440917704453047585218779398951 0 32306505064857777433046339051001804187 6812183026186677462980247197019529882 -14314745470728325268146986827777882895565399216606059161171870963597610110336 0 104898486430138053004814980242657453305 128138851326309218149922263082758157860 -4412562917343731512061227605323605810190643301349269315370547879142645683100 0 129913289395864711085642678093188854875 106386319732467349749360664253398895588 -12028767715118155962041579459517900153222589754063959209509504013994984268306 0 53241048630474759865015670873222249148 67207748496494885452736215383524113231 -12246750884028308209980448870106655136151752126133142208823329821869768374234 0 152421560332217837756343305589199403764 77344352340241433967009021308896546506 -18228664880783270445467084944182335111523030955626500381371508282438778557439 0 24236230682598972959760976150727921416 99143808039139367211254118838596071126 -4798911225939489447616838669631268050471559751162292893234575150636029987582 0 35600257043616798806636253088878555290 53935491101941428356012369892438768071 -1502452278394666107954752334778079092619084894906925127819666024679712294939 0 71743486195590463179734105421531162651 95399299040829355746559869479652504380 -4968803723853208654429673030676019334739905255981972894037649056744248789725 0 74472721206729155362930571253430852096 53568072880700614395488873844162976119 -12425024562599435002527744822638580794556422148981596748190381010493974915322 0 45443196107602058717986561344032718838 82432441190989704974691168198958699992 -15855635702064282034494825427524547106497263640446962880553941169787887682855 0 56061863816410908791758891816249443922 123447981149774642679053259103524760649 -6868911023172745915234634494620977763151663744660916262990790663438319429706 0 140119805264467227774924290223609448946 83975919280473531705927202193546556409 -8734213151925587275290974719631833863053798983832471364143828835809738148008 0 20970997326406030421250479218493822816 107305716729696026350131194821945359013 -11051143999915734757008281891674328961821936043454015313577218621259792920407 0 142947696093002467937419662331608202578 148236353675604418633690345056734156433 -7344725227296977232496898520547284046759603566412051697523823454686399087683 0 92699831502338285567509706153007419257 61348395677727055077286953342671504041 -15415222883433667396426261505856059995127556846281608779716499670506811908352 0 116837870913398705897280383510833303917 107830456778479514368314276407932197519 -1664154309824540180671177677187161613831766555241101441396263373051464893033 0 63188035904535269573504333963066060650 117586538793932928247531072226923627740 -12929062719903081805005135246817794386554038776361871359846245872639287567216 0 43829383497869529422615652421209180925 144035920988280805463992517012916349691 -8005672939216896426256277788619773159448375007532249139648509779109302286878 0 75715204690327561412284025054919468075 89620187970709958545669121684626375227 -9823860471152812665184246524592480774012862008613798713737767800820830068191 0 68576994052458527470959970437791295132 82096663446595030400026868421118995463 -15687619327887954494357679272464699403263950219266943313750566287276067353526 0 141537622296208364007273901553735687862 139581950098770744884180416235436309511 -13973390760364460869191287025967518936342679359376387196267718747682062054071 0 28382786338576212732305345669516494507 62658186745238145346539845786398234783 -16315879523933246355646912685087844333971760056014452101693642000732457361867 0 99951170051825431134038721097964319045 135218046385634266609146731590482298650 -8577421190893439028114875164247658441981757895996482477629561897942300153680 0 29513504906851498095481639547777924068 89782284556038349930635280843672171259 -6414083401679237555575565160870944176872975106323259027730898503375901444746 0 16866120458289363860093936364182633924 143233267196534356298451563794973872230 -17712603180560147503853792747875311504119345587202275131845536458796955651351 0 37038829938232524087710316701001135115 157151558482068384404863372619156204448 -6765909976465850328496608927089166108057458764793534137710423164655395579907 0 77498274465783970505423886572624627735 121339219156308847147597172811870969245 -14808883726622039734239898006769367524178452850142905245262886603553992301315 0 20237477742775640336957602613979747010 26486594546002555312846547753522513558 -2261068838672740511699860047467241464553597192512582291224577461474454573604 0 136966374827241481601349155189643008099 56050358435274042026562765979436567686 -7979264793125071671225757057347205400488260501091091045130882828366676825084 0 28870529621607864393199046660317197385 80070203491404289997827590164118981810 -2024204262897493767009467541070903804161349851844341416019202729580406670107 0 129306725402031049277631487487917506958 10723069359599266790576338815615511178 -11138470166623618787183129094271386390031626909516857863440692724554405970138 0 20090196687653863486802911971649701845 142425422226546893771500534261749040455 -3524441642461055274391048557771446242024462464203847668137345828521729243945 0 108478210141233850952353580145888570271 133630242646036566847957511634116364627 -16532733059602060690785929847024178341024392103823012778302586570098985388309 0 15750048349203448281711708648364099878 63549389674837878369173696179794058985 -14158789744837569706644992051751601006119334013439239652793718889183339591976 0 130342443209035317816553714170307980375 93338294681056614066154597465555639126 -9912728642315349415539814823546471799303505959511864329589864531153202240661 0 67297894365416007473600081008628427739 131162774731607570334537839843098641015 -18177139082189709695784686625126067716663954837943118145698033777411587654869 0 98236801985913779406218601897206431831 107318565689495187163973730990118414464 -5928319863448351399810739990750522671822151170323139505119505260528471445611 0 15094988335250339147652689319869421122 12281455832326887616997888014003426510 -1738331155022398863568451228562202454351598930403045685991598530559295824320 0 74753932722781472966437210695128864282 33167285109193199897287175844959981854 -5283570977610375369893179584749466019020114453298559128862696321076215886714 0 133525918484362777444700444742990007868 31456084795809114101646208999941871217 -13648021693592051955306499546417615812504358498862863320994439161049140108698 0 150288359053306718239961775392555354954 30524061543281263825304788894162969436 -19582918942383467190667535082137019395139479697998764502366844553173910298264 0 80600518426161174954260236303126723483 149019431346771383851494768971946665089 -434887460715757796763313964314309437643918227064959898587147874550866884562 0 42599271799349352611654079149964726948 95304910036111503330332268458656765472 -9463027529600417631061912432962180565778206574483632161402527365417748736443 0 94782455850607379158918606296709397760 143151026050916019276526303561834029923 -8986759055147288938429642989612379785012771395642261894767280466194781357947 0 32599919852679748837855542837259557737 136155915424744966874516239919443515495 -6882070352328610634895543301764417399807266920780443605639412445541016522304 0 25990973354640593797095180015011933467 71151278237629170455232850302493084283 -20528337398443536064159882539161208997478513789548339699324206644173680034881 0 134326118180112955565082737980006479818 74810510180766695714826655845465048757 -16457636472821006302397441427707964655436622384413834270598565395202133345651 0 57732597487850435078927475892788353776 154769642346344802870976975969222927164 -5610449454817548583074849578741727534370362445301174270947002537633816119974 0 87383178825411049845085082489966242506 102999815378266494405817603985003522527 -21660987615710643886915782486248368327523383824721497747726705107965372723897 0 96252059234912160199857665956195263696 125552514027146782033738160124378338988 -6536400132881142640538729558093230291280483408833968513953114638079365083920 0 140401570093354583676435035254620962126 132974278118569632717754070890193729866 -12278473778626664065405952502391504202543447364209306055434707550643296169863 0 92893759598378889117788981428366793690 30964303441223410759870739796379713522 -16726307773016602570572099051964938490975311395117657959777088321125265212666 0 76587903114551696062069563108606553940 108453305992217254652101320890246347990 -5415144548086459716293252415766813361666559270834273109661474447000272359579 0 97313761947330195025960803350105307084 55432710146199915359232778109783327729 -9226442034538437864523440956911232457106716669061498685140975883260424095393 0 149509008282539693986786060430682491823 116011175760218106891385556028811487572 -7810305135478854636976079590658683428098355241030636106224215947187644714478 0 27379835307682529868588520113401666717 80441540557070700586364362779862170811 -8109137731243254138203952170446452172384047521039545864832824347225681427131 0 90414083338028514115616960705345523576 53646231993016203491267858960067881565 -14030106614594882326603669102197328663543261492731389200756046546633370385424 0 51979271299669989619485789080559379515 101559166342974232415616684607221417414 -1427459672226339976859186341347575628864693739325345196770280809612715239209 0 78811623411536898649283896443892342800 70036252377429626292498520016689936588 \ No newline at end of file diff --git a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs b/evm/src/cpu/kernel/tests/ecc/curve_ops.rs deleted file mode 100644 index ed37401f02..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/curve_ops.rs +++ /dev/null @@ -1,373 +0,0 @@ -#[cfg(test)] -mod bn { - use anyhow::Result; - use ethereum_types::U256; - use plonky2::field::goldilocks_field::GoldilocksField as F; - - use crate::cpu::kernel::aggregator::KERNEL; - use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter}; - use crate::cpu::kernel::tests::u256ify; - use crate::memory::segments::Segment; - - #[test] - fn test_ec_ops() -> Result<()> { - // Make sure we can parse and assemble the entire kernel. - let ec_add = KERNEL.global_labels["bn_add"]; - let ec_double = KERNEL.global_labels["bn_double"]; - let ec_mul = KERNEL.global_labels["bn_mul"]; - let identity = ("0x0", "0x0"); - let invalid = ("0x0", "0x3"); // Not on curve - let point0 = ( - "0x1feee7ec986e198890cb83be8b8ba09ee953b3f149db6d9bfdaa5c308a33e58d", - "0x2051cc9a9edd46231604fd88f351e95ec72a285be93e289ac59cb48561efb2c6", - ); - let point1 = ( - "0x15b64d0a5f329fb672029298be8050f444626e6de11903caffa74b388075be1b", - "0x2d9e07340bd5cd7b70687b98f2500ff930a89a30d7b6a3e04b1b4d345319d234", - ); - // point2 = point0 + point1 - let point2 = ( - "0x18659c0e0a8fedcb8747cf463fc7cfa05f667d84e771d0a9521fc1a550688f0c", - "0x283ed10b42703e187e7a808aeb45c6b457bc4cc7d704e53b3348a1e3b0bfa55b", - ); - // point3 = 2 * point0 - let point3 = ( - "0x17da2b7b1a01c8dfdf0f5a6415833c7d755d219aa7e2c4cd0ac83d87d0ca4217", - "0xc9ace9de14aac8114541b50c19320eb40f0eeac3621526d9e34dbcf4c3a6c0f", - ); - let s = "0xabb2a34c0e7956cfe6cef9ddb7e810c45ea19a6ebadd79c21959af09f5ba480a"; - // point4 = s * point0 - let point4 = ( - "0xe519344959cc17021fe98878f947f5c1b1675325533a620c1684cfa6367e6c0", - "0x7496a7575b0b6a821e19ce780ecc3e0b156e605327798693defeb9f265b7a6f", - ); - - // Standard addition #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point2.1, point2.0])?); - // Standard addition #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point2.1, point2.0])?); - - // Standard doubling #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #2 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_interpreter::(ec_double, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #3 - let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); - - // Addition with identity #1 - let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #3 - let initial_stack = - u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); - - // Addition with invalid point(s) #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, invalid.1, invalid.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #2 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, point0.1, point0.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #3 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, identity.1, identity.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - // Addition with invalid point(s) #4 - let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, invalid.1, invalid.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - - // Scalar multiplication #1 - let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point4.1, point4.0])?); - // Scalar multiplication #2 - let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #3 - let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point0.1, point0.0])?); - // Scalar multiplication #4 - let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); - // Scalar multiplication #5 - let initial_stack = u256ify(["0xdeadbeef", s, invalid.1, invalid.0])?; - let stack = run_interpreter::(ec_mul, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX, U256::MAX]); - - // Multiple calls - let ec_mul_hex = format!("0x{ec_mul:x}"); - let initial_stack = u256ify([ - "0xdeadbeef", - s, - &ec_mul_hex, - identity.1, - identity.0, - point0.1, - point0.0, - ])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point4.1, point4.0])?); - - Ok(()) - } - - #[test] - fn test_glv_verify_data() -> Result<()> { - let glv = KERNEL.global_labels["bn_glv_decompose"]; - - let f = include_str!("bn_glv_test_data"); - for line in f.lines().filter(|s| !s.starts_with("//")) { - let mut line = line - .split_whitespace() - .map(|s| U256::from_str_radix(s, 10).unwrap()) - .collect::>(); - let k = line.remove(0); - line.reverse(); - - let mut initial_stack = u256ify(["0xdeadbeef"])?; - initial_stack.push(k); - let mut int: Interpreter = - Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); - int.run()?; - - assert_eq!(line, int.stack()); - } - - Ok(()) - } - - #[test] - fn test_precomputation() -> Result<()> { - let precompute = KERNEL.global_labels["bn_precompute_table"]; - - let initial_stack = u256ify([ - "0xdeadbeef", - "0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171", - "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", - ])?; - let mut int: Interpreter = Interpreter::new( - &KERNEL.code, - precompute, - initial_stack, - &KERNEL.prover_inputs, - ); - int.run()?; - - let mut computed_table = Vec::new(); - for i in 0..32 { - computed_table.push( - int.generation_state - .memory - .mload_general(0, Segment::BnTableQ, i), - ); - } - - let table = u256ify([ - "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", - "0x10d7cf0621b6e42c1dbb421f5ef5e1936ca6a87b38198d1935be31e28821d171", - "0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e", - "0x19fd9b572558479df062632562113e4d9a3eb655698ee3be9a5350ed23e690ee", - "0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1", - "0x226ec29db9339d7ffb1bc3260f1ca008b804f78553d316c37203118466bb5f5a", - "0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435", - "0x294fe34d7ec9024c96cfde58311b9ee394ff9f8735d882005fcf0d28709b459d", - "0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20", - "0x12ff1d200127d2ba7a0171cadbe0f729fc5acbe95565cc57f07c9fa42c001390", - "0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272", - "0x2a3f4d91b58179451ec177f599d7eaf79e2555f169fd3e5d2af314600fad299", - "0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d", - "0x144249d3fc4c82327845a38ea51181acb374ab30a1e7ea0f13bc8a8b04d96411", - "0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512", - "0x259a447b42bab48e07388baece550607bc0a8a88e1ea224eba94c6bed08e470e", - "0x2ba4ce4289de377397878c1195e21a1d573b02d9463f5c454ec50bdf11aee512", - "0xaca09f79e76eb9bb117ba07b32c5255db76e0088687a83e818bc55807eeb639", - "0x21de5680f03b262f53d3252d5ca71bbc5f2c9ff5483fb63abaea1ee7e9cede1d", - "0x1c22049ee4e51df7400aa227dc6fd6b0e40cbf60c689e07e2864018bd3a39936", - "0x1045a28c9a35a17b63da593c0137ac08a1fda78430b71755941d3dc501b35272", - "0x2dc05999c5d9889566642e3727e3d9ae1d9f153251d1f6a769715ad0d7822aae", - "0x300f58e61d4ab1872f6b5fad517c6df1b23468fcfa81154786ec230cb0df6d20", - "0x1d653152e009cd6f3e4ed3eba5a061339b269ea8130bfe354ba3ec72ac7ce9b7", - "0x10a16b4786bd1717a031a1948010593173d36ab35535641c9fe41802d639b435", - "0x7146b2562689ddd2180675e5065b97a0281cb0a3299488cdc517eee67e1b7aa", - "0x19469e55e27021c0af1310ad266cdf1d9eef6942c80afe9c7b517acf16a2a3e1", - "0xdf58bd527fe02a9bd3482907264b854df7c730c149eb3c9ca1d7a9271c19ded", - "0x1565e5587d8566239c23219bc0e1d1d267d19100c3869d0c55b1e3ea4532304e", - "0x1666b31bbbd9588bc7ede2911f701a0ffd42b43bfee2e6cea1cd3b29b4966c59", - "0x11b7d55f16aaac07de9a0ed8ac2e8023570dbaa78571fc95e553c4b3ba627689", - "0x1f8c7f6cbf7abbfd9a950397228b76ca2adac21630583d7406625a34505b2bd6", - ])?; - - assert_eq!(computed_table, table); - - Ok(()) - } -} - -#[cfg(test)] -mod secp { - use anyhow::Result; - use ethereum_types::U256; - use plonky2::field::goldilocks_field::GoldilocksField as F; - - use crate::cpu::kernel::aggregator::{combined_kernel, KERNEL}; - use crate::cpu::kernel::interpreter::{run, run_interpreter, Interpreter}; - use crate::cpu::kernel::tests::u256ify; - - #[test] - fn test_ec_ops() -> Result<()> { - // Make sure we can parse and assemble the entire kernel. - let kernel = combined_kernel(); - let ec_add = kernel.global_labels["secp_add_valid_points"]; - let ec_double = kernel.global_labels["secp_double"]; - let identity = ("0x0", "0x0"); - let point0 = ( - "0xc82ccceebd739e646631b7270ed8c33e96c4940b19db91eaf67da6ec92d109b", - "0xe0d241d2de832656c3eed78271bb06b5602d6473742c7c48a38b9f0350a76164", - ); - let point1 = ( - "0xbf26b1a7a46025d0a1787aa050d0bb83b8a4746010f873404389b8b23360919c", - "0x65adeff3fed1b22fa10279b5a25b96694a20bcbf6b718c0412f6d34a2e9bb924", - ); - // point2 = point0 + point1 - let point2 = ( - "0x191e8183402c6d6f5f22a9fe2a5ce17a7dd5184bd5d359c77189e9f714a18225", - "0xe23fbb6913de7449d92e4dfbe278e2874fac80d53bfeb8fb3400462b7bfaec74", - ); - // point3 = 2 * point0 - let point3 = ( - "0x7872498939b02197c2b6f0a0f5767f36551e43f910de472fbbff0538b21f5f45", - "0x294e15025d935438023a0e4056892abd6405fade13cf2b3131d8755be7cebad", - ); - - // Standard addition #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point2.1, point2.0])?); - // Standard addition #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run::(&kernel.code, ec_add, initial_stack, &kernel.prover_inputs)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point2.1, point2.0])?); - - // Standard doubling #1 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); - // Standard doubling #2 - let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_interpreter::(ec_double, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point3.1, point3.0])?); - - // Addition with identity #1 - let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #2 - let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([point1.1, point1.0])?); - // Addition with identity #3 - let initial_stack = - u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_interpreter::(ec_add, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, u256ify([identity.1, identity.0])?); - - Ok(()) - } - - #[test] - fn test_glv_verify_data() -> Result<()> { - let glv = KERNEL.global_labels["secp_glv_decompose"]; - - let f = include_str!("secp_glv_test_data"); - for line in f.lines().filter(|s| !s.starts_with("//")) { - let mut line = line - .split_whitespace() - .map(|s| U256::from_str_radix(s, 10).unwrap()) - .collect::>(); - let k = line.remove(0); - line.reverse(); - - let mut initial_stack = u256ify(["0xdeadbeef"])?; - initial_stack.push(k); - let mut int: Interpreter = - Interpreter::new(&KERNEL.code, glv, initial_stack, &KERNEL.prover_inputs); - int.run()?; - - assert_eq!(line, int.stack()); - } - - Ok(()) - } -} diff --git a/evm/src/cpu/kernel/tests/ecc/ecrecover.rs b/evm/src/cpu/kernel/tests/ecc/ecrecover.rs deleted file mode 100644 index baf003d993..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/ecrecover.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::run_interpreter; -use crate::cpu::kernel::tests::u256ify; - -fn test_valid_ecrecover(hash: &str, v: &str, r: &str, s: &str, expected: &str) -> Result<()> { - let ecrecover = KERNEL.global_labels["ecrecover"]; - let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_interpreter::(ecrecover, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack[0], U256::from_str(expected).unwrap()); - - Ok(()) -} - -fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str) -> Result<()> { - let ecrecover = KERNEL.global_labels["ecrecover"]; - let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_interpreter::(ecrecover, initial_stack)? - .stack() - .to_vec(); - assert_eq!(stack, vec![U256::MAX]); - - Ok(()) -} - -#[test] -fn test_ecrecover_real_block() -> Result<()> { - let f = include_str!("ecrecover_test_data"); - let convert_v = |v| match v { - // TODO: do this properly. - "0" => "0x1b", - "1" => "0x1c", - "37" => "0x1b", - "38" => "0x1c", - _ => panic!("Invalid v."), - }; - for line in f.lines().filter(|s| !s.starts_with("//")) { - let line = line.split_whitespace().collect::>(); - test_valid_ecrecover(line[4], convert_v(line[0]), line[1], line[2], line[3])?; - } - Ok(()) -} - -#[test] -fn test_ecrecover() -> Result<()> { - test_valid_ecrecover( - "0x55f77e8909b1f1c9531c4a309bb2d40388e9ed4b87830c8f90363c6b36255fb9", - "0x1b", - "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", - "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", - "0x67f3c0Da351384838d7F7641AB0fCAcF853E1844", - )?; - test_valid_ecrecover( - "0x55f77e8909b1f1c9531c4a309bb2d40388e9ed4b87830c8f90363c6b36255fb9", - "0x1c", - "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", - "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", - "0xaA58436DeABb64982a386B2De1A8015AA28fCCc0", - )?; - test_valid_ecrecover( - "0x0", - "0x1c", - "0x1", - "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "0x3344c6f6eeCA588be132142DB0a32C71ABFAAe7B", - )?; - - test_invalid_ecrecover( - "0x0", - "0x42", // v not in {27,28} - "0x1", - "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - )?; - test_invalid_ecrecover( - "0x0", - "0x42", - "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", - "0x0", // s=0 - )?; - test_invalid_ecrecover( - "0x0", - "0x42", - "0x0", // r=0 - "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", - )?; - test_invalid_ecrecover( - "0x0", - "0x1c", - "0x3a18b21408d275dde53c0ea86f9c1982eca60193db0ce15008fa408d43024847", // r^3 + 7 isn't a square - "0x5db9745f44089305b2f2c980276e7025a594828d878e6e36dd2abd34ca6b9e3d", - )?; - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data b/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data deleted file mode 100644 index 115e969130..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/ecrecover_test_data +++ /dev/null @@ -1,184 +0,0 @@ -// // `ethers.rs` code to get ECDSA data for every transaction in block 16141392. -// #[tokio::main] -// async fn main() -> Result<()> { -// let provider = -// Provider::::try_from("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27") -// .expect("could not instantiate HTTP Provider"); -// let mut ans = String::new(); -// let block = provider.get_block(16141392).await?.unwrap(); -// for tx in block.transactions { -// let tx = provider.get_transaction(tx).await?.unwrap(); -// let typed_tx: TypedTransaction = (&tx).into(); -// ans.push_str(&format!( -// "{} 0x{:x} 0x{:x} {:?} {:?}\n", -// tx.v, -// tx.r, -// tx.s, -// tx.from, -// typed_tx.sighash() -// )); -// } -// let mut f = File::create("ecrecover_test_data").expect("Unable to create"); -// f.write_all(ans.as_bytes()).expect("Unable to write"); -// Ok(()) -// } -37 0x71e206f9a89076270d57e93486946ce5803dbcb76279780aa41bf258763fad23 0x5f7e34e4a781f7572f8192b845de80fe24d77f13552dfefb67e03c94388934c 0x7a7f78a2af5aef01a889e8713083ab77dcc9fc9b 0xf457bef979aae3c1cbbb294a8f015a8e149a60ec0146383a4bec6fb097098d24 -1 0x57d71702dde291f2d7c6ea8b9d8172523b72e93db5a7426c5b898bb74d555a3b 0x47303ce56b157b8c237bc0206a610975ef690bd71912315a35fd01ea428b7520 0x1113efd5c8896ccf251ea360bb9d91f113707f80 0x65a5c566f3dea5f436bb33e8803be619c1c6d3857b0199335630e643a1e3774c -38 0x6cb3fd5d878b8b234906bc447c9b6abac34fec019d82d1e1419874bff40f08e1 0x4de3f2f5babfa400a014739e705f64219824ed75ab16058835452366e00b7498 0xea320901e51d9268a7f5789d305b1330657fc52b 0x3c922df979a5c1922d2baedca63215577d603b13b72862b57fbf240ca501bb20 -0 0xa34136f1b02ba36d0644d6aa695bca911c9a7ce5e19272f97b82056b03f481cc 0x26eb167cf65e0ce1f8f0c3809863e3c8542f706b8ef07ad849793fe208bcc7e6 0x4e80744fa23cec76e1621ce0dfaceb4b1d532e12 0xf4e022d9b83d279611395823e4b436d1b795054e59d0ddc663c42c41075026c4 -37 0xf4fa7bd652968977947eb8a9bbb216735ded98405e95555cafdf48615c1d4866 0x77545ac93bf7065641161ec455c23a8fe2b0e6f1f9d8ab8efefa39e6ae3ae0 0x9acbb72cf67103a30333a32cd203459c6a9c3311 0x2727dd81db2f9141fe2828b150dff11b073a825b88e514b1d33f3bec117e3c5a -38 0xca14396f151b4c21129bb15c70db48c97969809f681d7521841d2dea13bd7be 0x3438d0e665fdaea72759ad3db4bab68674314cb50d902fb58a57ff8f61c89703 0x595063172c85b1e8ac2fe74fcb6b7dc26844cc2d 0x26c3de6bd2e155436e031928d2cec402853cf6797a4718cbcc89b48ed05d623c -0 0xad3fd1fb3cf701e53cd5687926ca64b2fa978a062efdb1f6d78d15c7610ef5e5 0x33c82cf8f9cc1072307ac99ff833c76775e0a4c5cb0a177eb4b1b415cb96239c 0x8a89e016750479dc1d7ad32ecffcecd76e118697 0x548757e275d1152805810855b75dfbe12a421023aecab6da0cfa257156695d53 -1 0x95846057f4d5f4e8940f9f16b17d864b60f89a85257f5256988ce75748d24450 0x6a3accdf56960dcc8541971a12484022ab54d1359601dac5b93072e5c55e1f10 0x6da4980ab9a35ff025774203e721a4f63f78f953 0x8814c7e0039cb36f6050a9c5c72095f492bd50f156f3246c516be072f4a7b1b7 -1 0xa437b58f9d8dcb09593565cd607ebcc9ac72df40380d35332d1cc8e3e242e61e 0x6391ca50a4be1ccb9f7e46d4beae2196984f88ef0d3e9bd47a85cb2ad3d2aac5 0x19ff156cbeaabd04ffa2df84eb6eca93f266ba0a 0xe3ea3b265ff5bbf8746b3bd81c934ddcbfda502ca3047bc2fb41d6218c43c8f6 -0 0x912e235df87ed93dcfabb8646e4fbe7a5b5aac00a27b0c0795492df682311f01 0x4a08239f03720362976203222bd9a60536746a0717e951d20656149833ee8aee 0xdd0eddc7d2ac6fcc3a8bb54b4d329b71f96ec8ba 0x8e65adea948f96cdf3dbf74b65b0611455c6c8d8295715744af921a3d2b73fb5 -38 0x1c1f7fd0febe597836a8650dce0e8c72e1b506dfaac11aabb909821a77fe657a 0x23fc3c8b0e73eb6077a9e861cad7e38ea26920ccff72aa46616b241ae16eefa8 0x120051a72966950b8ce12eb5496b5d1eeec1541b 0xa2f59b3fb815c9d3d5ad410e793ec97962032e965f81ecb07bc8606821faaf7e -38 0x88de71d84e271c4213391d1f78e47d2c7efb017b8b21fdebc97b8e2fc0d121f4 0x17a58f16c998f04e22577b88484ac1f7493a0473d2eb4dcdf5ccd659593712cb 0xe93381fb4c4f14bda253907b18fad305d799241a 0x2614f325a54ba5bcff2bb9221bda406e7ac17515a94abfc85e8764340f28f3c3 -38 0xda16a02577ac6d17701ad03320d1a1786320b7941680ae242d9faf5b117d5c2a 0xa475e4d3f36b7afa9af0e558f5ffb68377ff001d7f842b118416840cb7ed231 0x1062a747393198f70f71ec65a582423dba7e5ab3 0xe5bccf131464da99d721c610b1bda164418b645dfba3c98f01c5048e8dfb7934 -37 0xcefe9615f951106d5b52ff9f5e07c42319c998b70c3d291eb2218dd0c497ae18 0x52fca3109c234eac060b8daa57f673fb6b8b2c1278fd6a167f12deaee1071819 0xc55eddadeeb47fcde0b3b6f25bd47d745ba7e7fa 0x22cf2c4159f1fea8c78f1175b8c7f3743b31894525f5c272f027f8b20b56eba7 -37 0x8ec6fb0236f0a39bca70802551fab9d7258346399f6deb0bfab834af93beda56 0x6f273debd0a0e8e98ac63d6078d23fa93a7b5786a7456bb5cb3fecc19b1cebc7 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0xfafe13916c038b9b01e476ef5675341a75bd39f198500732b116221d92620781 -38 0xb244a0d2c66cf36f35455ecd8731f2960c64db35603ad2a76ffa34d22bdb821f 0x1a7038fceb3cbaa08fae08d5857192e44dae37666cf841b11c6e2bf84e655078 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x17c8c48fb411df67de52aee98dc4a586f3725704199761f5bd23158437cb5872 -38 0x59ed90e66529f9b21ff820ab501e5ee0f18dde32c1debd66b5dc6e3342032b62 0x3f4b7b15b0af3339d791c8e73a7976d16bbd91316025d256d9161ff1ba894c69 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x4a34d102022fa077a95899e102ef294cbd5941145fce1e29e2af39c6c831ce77 -38 0xd2bd67f5a2ff75cc124ebf944f7c26d87172d580cd92493d50002af32781c02 0x6536865fc12e5121a546458a45b480ca4a14b71d1cb899cf53c936044ad756f 0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88 0x8d635892680e858fa7c2564751262f0ef074134ac6a46149b1ff02f16c0276f5 -37 0x274407742377dea657519cee052763f9f0247fb2fc8d6a587d3290b4056abd2e 0x1ab13a59094aaac36a7292c7cd6814d11119bd34363f313c993e46f4a4fe157c 0x97b9d2102a9a65a26e1ee82d59e42d1b73b68689 0x9d22cf1bda837de201fce2be661c2bcf5e6c3829fd75f58232a7200fe89abad1 -0 0x9613c1e52f935a04483aab79c0ef76f8d427f79feaa2543ba4c94a91cbff7f6 0x3d75a82cae83a3647c248f44752185b662055ef9f3f21abf2fe5260f6885002d 0x86e122e39d467eb13d82765881cdd944c94ba922 0xfe0f34ef96a36515135bff2c1fb7964a552c0bdfb88a1ad773414f2a05c77132 -37 0x37f09f9d9d034a9ea8892c3a7b4826de643778f59e217ded5199d5cd32b3237a 0x311f3b9d4f7d771015eaaa0a1c2cd5759ed798902ecc03632a18de47c078149a 0x9cbf099ff424979439dfba03f00b5961784c06ce 0x9d08d5163de3f8437d991d7fff25178eeb821ae6e272cbf42a86f4f1a22023b1 -37 0x28210d2f4b817744db46b71c7c993fd3318ad30a96975678f185cf5e8067b9dd 0x646aba390d36afcce34eeb928b92c8d0e12cfc5ad598569045de59d4b2de3526 0x41d9294128b6c8815dd92199700953220b1bd375 0xd4711cb46f4155bb887a2d978f2c4d877cf9bef8122ca0d30395d5815bc5a886 -0 0x4455fa740404acaec4e670be00843afe7cc6cfa379108ef02eb8368210791943 0x83f2557e17faf4951e9cb767b278f57487776927928cc53f5f1e4f028982cb1 0x0b5c4a7fcda49e0a8661419bb55b86161a86db2a 0xe676662e29b77af78c77311801b74666dc37ca016da761c164a3ddfe971502bf -0 0xdfb1180544b6b942c23ddadbe4561c821dea1727735fb4442c2c0ad02185111 0x6524e1961d5357471ab6d7e4ff52fc434c88a962ac0a8f203dbf67cb4e971727 0xdcdf0feeede933ceafc6131b663f1ee210ac61ae 0x6f9cf3180ba5ffa60436b7539de12d8b11e0343810b0d3f85d6457745219b86c -0 0xe3f04a7f4b367c274a018bf88c3e3929ace1ae203e6cbceb4809f4b6453b665 0x57da3d1b4b3e6adc031deeb9baf408b241d178e12577c105f97d15eb71945151 0xdc5432c573a1d4874d305a5cd7e62aed2b0bc522 0xd79be8888d4ac1d12398740b93f7cf5adbffa332356677973663b2580d45c7e2 -1 0xa29cc033a00f3efda65699bba97235eeede560f1adb51d307a168a2db841d8a8 0x6f761fa28ed8c0aa16773f7a11ab14c34dec9d31a085887a798dabcd00fb1905 0x19f494583c7c933be7b0ee58104ddafac1e8adfa 0x21f2778f84b87d28bd417f57a952fc3b86b86641717008fda61ebfc956dd7166 -0 0xa18dde11da20682e5d3904fce5591b2d499bea1980e113a6b851a786f9551415 0x7f29dab88a65cb3cef6b8fdd633e61f565ba385459998637b4e54de7a69c017b 0x4a3646402d1aed59cd97972474beb7d1c68b303d 0x889719b175613719e30dbdf7c59b0766ae3a642c0700fb43e6959b369796e218 -0 0x563776eb92b4ddecc85ceb07b5968ed78b47956554735cfc212db00e03fdaa04 0x28bac43c2efd049b4e9ab7c46d5961e216da1baab59c4e2930bf19ba146c5e21 0x8216874887415e2650d12d53ff53516f04a74fd7 0x4aa2be7cb95d651d91b8feb3d32c04f4abb1c8c36eb0b2c88a72b1a1630cdda3 -1 0x24dd078a4a604496c592fd0568c50e340c2df5d3343392b62f6669128c43777d 0x54a4e08b684ed36cdf20f132164ec18599b07763da289787ef4ecd21635e7bdf 0x64917e321c516a2dbdad48bef7e923873eb7854f 0x2335ef16b1d02b9e206f952b7d98bde5f453afd6e95391a6083fc4d6fc829e2b -1 0x96942017e0365f7e059d8822e8bdb10f772e186e5f6dec136baac903f037863e 0xd94dc604b36c0b77740a4bbf70a051d239185611ee36f2a6224c8604b638016 0x0e7518b332f469a6a2f59e690f225cef5157cca9 0x62b0621f47ddb24e8581b2165cde8f4f883222c991fcb1ba2610d01948a5882e -1 0x5e5f424b80fb1351a178d36348840d9bd5cf363c78550d829322f21b58a2adfb 0x41a2fb3e6fe3c5a4449106ba5fd2d0d5e9efc10e26e8c17e7133fecf6faf138d 0xa7fb5ca286fc3fd67525629048a4de3ba24cba2e 0x4a3adb07cc5bd5b0cd6dbdafb1d643e41e353c9ef1cf83ac1fda32815b09d461 -0 0x17216ef4d8539f74333914926d1fdec20b06ce4594d846af59bbb0995ad80bdc 0x5806f2eeef10c734ac9e577f3a7c73b338ef2553238b5543a1c2dcc35c3481b8 0x7f72687d4981e85d2d2746c17a0219d8054a2148 0x9a3fbe852ed1abfe59e63eca1d8e2453a1060fadc622360d4f6bc40cbb8e326d -1 0xf765aa91e69fb3436969124c747b888e705d49e8150a63a545343ade5a425ac6 0x19235da122cf688b31101015a9f4c67ddd557f7139206641182187c308f608ed 0xb6e145b74eb1a016231483272fb622ffb68d3d7c 0x2537f24f5d73879d63799050aa0eaf5fce44bd670068aa2b1b997913859106b4 -1 0xa2c13c62165475ed08b64524b7c9a8c1e6f7d4fd041c579eca29306c0ccae15b 0x3fab181d182a0f0d9768d75bbb8461f2a9a6b3416212e8b91209680e29c5a31 0x24c09a812b6419ae0e929b95d562f635097d4590 0xf9113ed129138f590b498a572380efaa06b59e436689772534842d7837e092e8 -1 0x3dd17bdd0a94e61f79b21917b937bb45cac40ee88cb37a09c987ddd346ee551b 0x32017f92d89ed4ac0f4a235c5f6f41982faa4341076ac2dbfb29a7a8efd15e1b 0x41f0bbe07573e14944304c0a544c5dbe6f468cf9 0x7df3db64dad8bf775cd8e6e585a61065cc739ec914cde160b3aee6d0bcfeba72 -0 0x7ab5cbca681be515dce228b1fb30dd588e1fa9eaeee60a9c869a9e4d6a44da8e 0x6567506dacb0bdf3be1aa5ebd55bb97b159258d728d7f2e54034555648c434a8 0xf031dd3ef38f677b55e8c4a191c3b9e343c07758 0x76a21141c922911e735c493c8bac70d04c6c83af1793046cc74d417deb23c8ef -0 0x541aad8b51542ac5f71abb002099c2107dc0a87da05751675deb23f683fd26a8 0x51b3820e49ba8cb365fcffb97269a30b826b509eaebc05b03c37be93ffba06ed 0x9079caa7968d77ca87967ced769bb4fef312a834 0xd570c731edcc02bf4f06f3631de76b4e2427fb64f315e9ea94080125d4478818 -1 0x896ac1bcf80a4a2c503f92bb030f0db43aa545e8f44693955b4f577bc6df260a 0x65c170a802c5b98a4567dbbeff8ec95d55434eaad260c12f37582334cbe013de 0x792eafab3593fd79f01c8dd5282965dec1bba51f 0x2c9a7a5e841d0a432a3d09a657c0a465604467f4ed0a8bf1d34583d2f49245b8 -0 0x9cd74d9898571e0af4e013337231472e033d7b95f70523a10f7122eb4a63497 0x4494e798d8905e9662b48b99c89835a2166b070be8581ede50bec94cfaac332e 0xaa9a1ec22aef4fd5119eafda899a72ffa0cae258 0x5c378afcde7290d00b044893da06469e9c2181a1558598a85830c3bae8de121f -0 0xca04356a33bf7351027ecc535e3e08537abdd333f2907a98e7d16a2e4408997 0x6871c6fdd8152913909322eab21e55baa51ec4bddf161b0e7d71251da17e3b9e 0x4976a4a02f38326660d17bf34b431dc6e2eb2327 0xe814b149e4d1a7404f17678f8a845795364f99fb830b9db51a84421ae4891f17 -1 0xd390febaf6781e5ddaddc823f1791c133b6818588e4d071248e7c3418977c2b5 0xbd2a753d9c86388a052044df07193bdafce4d5dd36e4a4e3973ebbdf0a7bb91 0x4976a4a02f38326660d17bf34b431dc6e2eb2327 0xc20c2be74014e4d9032c422635038ec1ee0925b2e2da0b7922f5f3c0d2d3ce25 -1 0x54c0524f19276d1404c067e69f7288ee5eee955ad12226daec56d74b7894405a 0x4335aa0002e02ddabea351271f791f0a55f69119a88a5dd64048bd7c241c57ce 0x28c6c06298d514db089934071355e5743bf21d60 0x59c08d63ebe74a3ac3c3d2dd65e813ece338b4df7878d5842732a24b3bc886fb -0 0x3f5bd3f07b802df407ac00f150b6b091f45a877c4dbb65c9d4ffacda8cadd858 0x4b02042d475f722e862608ebd4625dde1fe5b6c18a09025d61694e6e1a986ea6 0x28c6c06298d514db089934071355e5743bf21d60 0x2d81d7671ecfd4830d97fb7f2fad04440062a3cff4f10bfe7fb2f458fdc758fb -0 0x4a704b00ae04e370b365d5d1f7d54f5b4554428bac0db7f24cb2d8e52b66a045 0x705197257bc2464a40381a5a6e062cce897e621013ea10c9647bafe1156ac2a2 0x28c6c06298d514db089934071355e5743bf21d60 0x368efdc35f07630059054c541f0cc8c259d57c2de9dd57fb3b72083f2fd1ca0b -0 0xee6ee3b45d0bfb27c6c424bfe3e268b5c88c0c0a09863005e907f22cc9935aa6 0x5c5ff76d39ffc6483116642b4b9d4ce851fd9a30f3a6c7ab963c1e2e151d82f8 0x9696f59e4d72e237be84ffd425dcad154bf96976 0x9252f22653436777ec2df3e6f5d532d11cf7c4e1084bd4e86c0821cc2ebc719e -1 0x81da46ac5a2c6a97e4c58a5e1081b6d68f97971bbd27a22cc3499c31fb557ded 0x140b455d2d09f1af1fea8777b828662b3b348265a5f9b336b4edc149434967dc 0x9696f59e4d72e237be84ffd425dcad154bf96976 0x2d03a34ba34a3ca1a8c8ba3cee10f0ca128c36f211b7e50b8728651fc8544d39 -0 0x60064f428d0d60a5bd28057f4b86e82c0d5592b3bdae2feea0d79881e5f72fb5 0x774b1055a8f27c9307bbe7eaaf9029bbb7e920acd9c0038b0646eb0538ef38fa 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0x0432c2c29c6bfa4242054e26254776f91678f21c99e672eddeccd3b9b2f92729 -1 0xc33b4f64a2183316ad21136cb72cf4f2260a864561cde658fd8a6ab5764a2f8b 0x59f25f6bcd7643bb349495b8816db070cad1776d6d6e18b0b119fbf9d8e4d001 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0x086ce311a8a9090542543571cab974bdfbc6c44abda76c8828b02672335da789 -0 0xa758e894173ce8be91f4d5544a7ba339744bfa95b535162a03acb61f5a9ea09a 0x39edb3cda1abb60a2ebd59636d9375ae08776745cb1a962eda45048fc1bd234e 0x56eddb7aa87536c09ccc2793473599fd21a8b17f 0xcf40ff9251650690129f50bc19a5a395ecb7d9b1bff94d16b14d759394f428e7 -1 0x50e249764da0ce92ad0e832b544d7526729eacfc43f3011fa6f555cbb6d394ce 0x52d85c3e2d5fc6505078b3200676e8efa8ef1626720938ec52ec3cac9f44ad33 0x21a31ee1afc51d94c2efccaa2092ad1028285549 0x584f942852d1ce266b8159c971f552e5b1692809b0fb98489e3605811b7f5de2 -0 0x673b134b030bc26cd02e4cea1ddd63effbdf0a178c9cacc932c2440042e1e18b 0x2aa60ee96d3dc050a0558fde204f482a85cb246ca431416b9ce9d18290a213af 0x21a31ee1afc51d94c2efccaa2092ad1028285549 0x978cbde7348b61241687919e376b531e1e9ab62eccd5c90c6fe2a8a0226e3689 -0 0xd626eabfbd7555bbbfde2f763b342a3145e85407efe2d2616f5138f816b73d11 0x217cb61fdf4d4a342cccfe81da91ed5fee89bd7e0d66218564c5cee47cbb6050 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0xe364db52cea6b264fd6f311bd700f91aa300194d4972f8f33cbd1dadcd5093af -0 0xe311926be6a3abbf18ea2d8b71559b60679acba9026b6a2870e1bbac1467f91e 0x21c747f249093f5772501f380b56b42e96a8ddf77bb592f377b9bfea06d9e0e2 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0xb56f149f71adaaf2674917db32c1cd864214518c58cf5574703733f924e873a1 -1 0xd4b5e0226fe10e85df2ce70a16a1ca5422bcbf83a685e6da2be48bc388c932dd 0x256ca3b5e0f3c0b422f441bed3ae5f851347990adfb2d11cf79879f54bad2cf8 0xdfd5293d8e347dfe59e90efd55b2956a1343963d 0x1d5ffce90110bd94c5d7810642e8283e721e33aa987d570b1593b04ae17b8377 -0 0xd50421f2321a69714cd7a52ff57c4c3482ca01a94eee507afe06828c4e47898 0x27e919a35fdc99ceb109873286aaeeb3e90aee201211a14496192cb80e8906f9 0xe6ab9e371e39d1daf921f24cd83ae3c837d2cce5 0x785488a8196174e5efb61add20c39c19887fa1cbf6c2c4e6f2ff9db79b966c52 -1 0x71e9f2b77a94f63614bac8249564393fcf3b3531ccf23a232b3be77ccaf1f18a 0x445c4e2eba9856f71c6c7b6f5eb81e1b451c39086458ac88f931667a1705de5c 0x5804869f1e8a5b99f2eab8c6b1943bd0dd5b26f3 0x340b702ff1d5feb24988a865d19143b47854f8526c04bf427be42e7bafb37fc9 -0 0x742855759e28bd15e07562aa6f35ff93538daf448ada687416fed7844e793658 0x73fec36e58ada180a2bb4a8847142e8ed88047d485d926a226bb8fc2feb1c804 0x1d05754e9cd39e987a34343cc862a82370942e93 0x2b0b92eab8fff875b52a21071b2ce8dc23d56652ffad308a0ff14719e4dd81b9 -1 0xe05dc5b2eadf7292c2c3c14f97e08daef9dd1aa499ad11a235d77ef4d03956a3 0x119e1653354da4b22fb38cdb11fe3ad1d18d8d174812448a6f8dd412f7ebf812 0xe41ee97cfb75f1646d143aee40fcb4991450aad0 0x2dff8c6bbec665b3bcc66782635d9192025aa733a09e7c2dbe10008e15317c08 -1 0xb1d0d41adea5daf645b5e0600f2e451aae67b2eaa191d2208db210f6a6fc61a9 0x1dd9aee6dd6cfea5c8dea1b93f0cb55c97f036c0b91fd0b167a479c72a8e84d1 0x5e746b94aa1fbf9af9559c6e04a93ef0a7abd0de 0x95366c33a8a3012c6273656ed6954e28f790943c0fdb2fcdd58290b00e853af5 -1 0x76d1a4d73ed92b786f848998c7dc8643aed6d80700faedfdf1c7c54fb8af0775 0x59f7addf6dc96f924714bb1727513d2d546ed11913087be3240f7a91b87d1d4a 0x170b4cadf7deaa017b1f595de5ec7591f7a686ea 0xb69182c459c9e8d84abaec3af791abeec972544eb880845b361e43daf36aa66b -0 0xb32e62641c4428e8d24e65ade6b2046b2deb890b887a89334ed8118f6f4f38d6 0x3b088d3fd2d2842f1a16da028cbff9d1f5014ee35f914e665538ac4c4936b548 0x285b3bc1f46d2bf1142966a5e1c4834b86b82aba 0xbc9b62e2e020145c9fe7933a0de0d6112fadab5f8a6bf701f5c360709668f743 -1 0x1dac69e7c23884811a95371d7b91e65ed6a364cbb5eee706bb560858759b1206 0x7a1b11dcc4f7219f8a8ed4d72357b66d9794fe4beab2902f5049e4e9ab64037b 0x5ae41ec3d27c0d14809be1786f35aaad447643af 0x9b21f4d49fd6e579933afb48b6535fd55ba2060a123bbd0263657622cbfe696f -0 0x51a7b36b6d1b7fc9d2cf6eacc16aa98bfd9d36a40d06f390cc2887dd3b064c19 0xbe87f07c87c4ae834004a8dfd4ed9e3e08b7a639aebe4f3235f745a05071f74 0xc6d7425c44ed9ce936578f8c150b6e589f9b0b92 0x793900f4ac8f983513c4fdc6f8603fb8dec6a21965cd8c7f64771c167327976c -1 0x5965e82f900e7f98922584a7aaab65615c69494054f9a0dcbbc50502c463d13f 0x23217a916260a082687453297be97454bc64e5d1617d9ff9973db5485f7fb235 0xeb2629a2734e272bcc07bda959863f316f4bd4cf 0x0386907e53874176bcc54754145fd0e8602730f9ac1ab6e680a4acd6ad584b65 -0 0x73b358681787d5541d791f3e97f851ccb6cf55b91e2c5640b52b4f3ebcf95666 0x1375414789a136c47ee9df880858d5f729b8da5b6776db925687ab58dad50880 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0x438abe80d5baf46affeca09ee17b6f85ffa3a8c438520c8a805ca95173d09fc1 -1 0xc42bea9052ad930f8041eaa138d9062a57ea4cfc53c109d429fa9fc10d01790c 0x758222e7ef5f17699e36737b1a1bd8d2870185cc80d42a7caf0b189238eebf34 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0xf8dcd114a2a0961f9614764ad17a1cc1b0d22106dd3275fa8d62a20a3cf6e0cd -0 0x1b42517479fa75ee42fe8b1f26dea68fde5f0d8777d32ccf94357c94e4a2b90d 0x4f82ffafaadbba64bc338890ad1f24c278dfe6cc445efa4e7a55b6625f905056 0x95a9bd206ae52c4ba8eecfc93d18eacdd41c88cc 0x164372f5e578564fbe521627f408f8ebddc6310ef689dbbfdf4d7d3c7e65bd4b -1 0xf4894537eabe5b3c19638fffa3416049469a3a216c8e5a97668c035cfc9e07d1 0x28b06efbe60e625e1be9cd81448a9b240b93a76657c1a18844d368c617ba882 0x2f043b4bb857a7f4b6831219184dac3105aca34d 0xdb1e4681868be2072d639150c9cff48b4e7883fa6c2c6665768389e5fa4f469d -0 0x480de57d4582c3a33c16f1049e8261a0b27df1d9e34a77e41c9d45fef01e2142 0x4fb836362b300da267c0273193445df1ed68fe28327d87acc2a22e583e64749e 0x7830c87c02e56aff27fa8ab1241711331fa86f43 0xe967340f3203ff60407a16976795d14c3fce0447a68f4b98ba98d4176057afcd -0 0xf80a9f48dfbcf60ffc4fba1c3a820bee256006608db5267fe8ea3d38949ecbfa 0x58ea47140e36a6754e2674bf5f13f444ebb9efcc076aafc9b925d531e14b456c 0x7830c87c02e56aff27fa8ab1241711331fa86f43 0xae9506abfb4b1aa41effa3510687329b80e824241e7e60e18c7bb3a2768f151e -0 0x816e21a58cca55cb134caf496c8c7bcc247d63a74ceef9bcfc505cd4382fd20f 0x18e9867681a483785254bc2bc23c0a6ec5fe7ab98fffd7bab2ba2f69edc59ceb 0x7c195d981abfdc3ddecd2ca0fed0958430488e34 0x1c416922452e82a5c2910d7b481923a0d3fea94856707acdbbe88842401b7473 -1 0x99f8c14bf31ec945e4bdb7af86b1d584c3263e0304aaaca78a804d5db56fcbb5 0x2e2958ece32f5999aedcf12d20a16d52dafc4296744985fd171958d95d948bb7 0x7c195d981abfdc3ddecd2ca0fed0958430488e34 0x3cd70e64bba999d4c64ecfdca7257b59d3aa8c6f034bf6adfe8ee5088c17cdd0 -1 0x221b8aa1c94cef0192f7fe4499d18e2404d463e98d4840006c606b36cc5ab5db 0x3e26c8ee8ed31948c4fd1dde9721279647ad4e17238deb46fc668feb47582d6e 0x9396d062fef353765721fa3f700fe858703a4a48 0x03b4189ff4b99e8378f60d10e10ad47bdee0e9b56828162291c60ec3ff6b74bf -1 0x28c8e1fff48f440d92b877725d28e5d31ddbefc59412caa2559d07fb74dbd4dc 0x32833c2b615db914646a55b3d16802901476fb2f1c78a73bb4d98dfc8521db63 0x760418292d007fd98e392ee3750876c27a6460fb 0xa3562fdde603f1da197b6824b951d8f4bb70b839129333b18b6f71897a67bf30 -0 0xc906182e3c40e1f5d259dbb4cea62d2ef3fec32efed97cd307a68e38bfc1761f 0x39e57fc10bbec73cc8521dd1d2db3b86d2e9b0f03cc73fc2ee0b5b6a3df28180 0x747197638a7af760e221636405c747aab7952cc6 0x7e689bb1704555c3dc0416989cfc372199b5dcf14819a66809c1a8e2dea8cec8 -1 0xed0248a764b72ccfb9549c1d17c855d8c810e25c829bc78f81bb22ff96f899d1 0x487912ba2a9979735b05ccdf0a777fba98923139e1a1b5cccc1068640b202a6 0x5b8972964bf9076eef6610e453a014516529a52b 0x778fb142afd5ef84a7ddb1f3d71b68d20e33b2e3243d7f4f47199897ab02c495 -1 0xf2b668b356cbdfc191da4079d9823b27dae5c1f3597a70d8bbf8db5256540539 0x2f5c858f4f81da7ed09f134fb95016e487cd2f4aa2a2da1f3076f46888981505 0xe6c6ba7ea35c1f200e72056f1467b188eb441ea5 0x5a34e826e965d0c9e7afa46f61ca73e97efb3f02d294a45e263be4029a2d3d65 -1 0xaa08c519409dfdbca88be959445ee38d7577c3de9674a6db64a8beec7449264c 0x4c85250f383d7ac2af72dc1f0d6ed5de9ada483eb896c49d32c2d56a8f0c84b1 0xff214c36ac0673bc49268ff1913b11e93534c86c 0xfee2ac113b09f4b1e9b33be3ebb4b227f2d2a0b6fde20b794aa834abb8348833 -1 0x98ffea8800969c9916a6214e763ade3d6d0cc30984d42330c8c131f9464fbd16 0x7a15dcfbddbf3c64817dbcd7d4e2c61d73d2ee4792f5e89103e9b27f4c36e7bb 0x0637d40e523c2dd75a9bee55967b7b18e2cc6ac8 0x520396bd99ef0e44ca80ec8ccd1c86a32d47f1c4fda146b0666475d5372af9a4 -1 0x611d611e426f6b2d4992dd556472bedab585481fd74f90081c40c5f18e16611a 0x2f32e37f67a5edd8e8a0da6675c13859d9944ea17f35e3e5815a997f06cce35b 0x68e0389f5678f1f15346a136d3e3096a84040c9c 0x4eee7476b26f9a3b565ad5f63edf4ed9f02c0463227c46a831a915584e4c2b6e -0 0x893a0acacaa60ff5364228c38f5a555773c0aa819538a6c64e9684162b567263 0x4e43e2381e6829acc6e3f017899f2c0f9b82e0e6672dacc38e4399889796df46 0x18e7c0e8a28822b390b7e076eb712c0850409c41 0x39e094c665cecbb18a113d1ccfc64976f4cd9887ec5463d8403bafcc77b55fce -1 0xeaa0f379c110a5ab9ccc71a5660230ba3be968a67de2d810efce78920da14066 0x699aa12379d9f2292bb619bfe2e713041bd49bfb02b85d67e05df54b76d66a2f 0xf795bc11cb62785c31f3e638aca0091b02b051fb 0xd632206bd8dffccc98d5a4b9ebbb612d63c2dbf05eff858324b931aca4c201bc -0 0x3405d2887bf37cf4270a98517595c78c3100e158cdf262ef59a3c4c1017ede93 0x66ac9b238933dd3072333f178e5b292ed7e18e68646d064038849b91d7cb62c 0xd0702fd5da78f3ce9267753d77d0da9f17043c24 0x664d7d927bdefc8bd1151c907124bb817c257246747f703f3de8a7365caea912 -0 0x27915f3c65833e9e80794d5342d39ca391ee459f654c49d8e84fff9c831dc50 0x4f3472eba8a1a164b982b17a37906a3e89e4e97dac88d93e51b63aecb88ee7ba 0x8edaa40f4b511ced61d2006e591d2b165807c3f7 0x9bc40c99ca374fc638d17e7c82d3b0f2d04b8ae9fac6865437b5b0708bb8567b -1 0xea0b758241ff151595d091982295c276316f47f17f871a2590a2628b2016c40b 0x23c56e6a0226e175d6751e36f2e2d59f3846c5fe17ddbc1d4a4b2f89676e1b43 0x4425abad97059fd35ae14c9e4d8ca8ccdfa16cd3 0x18f6e255eb1c86612447f6821769389df7f348829489a10f940b0c06fb30be2e -0 0x3695c2a9ca1a52204a47a3576963990541cc402e1515b21c9835260e4f6954a7 0x797890b5905545293eb8428ec6e46cbcdc9da057c4e7791229f9188772c8d057 0x50525410d59beadf26d271f9d693d1f4191e5a04 0xc0e8063a935706d67cb5d9c9cb99daca1413d00c6248df3f04238fa4d4cf08eb -0 0xba1400e2353ebfa00d5b24c1b41a16751d979e7442dfa163a25d6c4e9c6d51d8 0x6c38c1250a3394029822fa3cdb43e24fdb0bb4324d5bed1291170a348fd24955 0x4d29b7f953ba471fb650fc5842127b05e35949b5 0x17198f8a656bcc2511d33b20db5c6ad0c22c8a9d67ae7c5622096f779dd4abbb -0 0xacc2bedb2fb80896b078301ad3b4cc677200fdcb5bf08674742c2a89c7e9dfde 0x630b75ca131a42236fd0fc94e1c1e69a8fdbc7cd93fe924aba6642791e4947c8 0xf959ff8366171a9e19d142340896cd94e1aa87ea 0xacd9925b4a9f9ee88ec57fc159881c18ea86e357b90c0240ab1d6bd5fe86e850 -0 0xb6179293816a9bd1889393b88c34d466d43ffc96771ecf0fe4ab7233446236fd 0x65b1acf754fa586c454495a479023b2d901b202044813a652ba664c5245acb2f 0xb8c26e86f38e22356d2d16c875d38289b6905021 0x0b97a1ed7e0d454011bcd52be2c016519ece9db2df56800acc626ce39cea9233 -1 0xcf67b4dd5b9d926004d1abf2fa23ed3f0e914423c231a4ae2dd08e19a3e9e4d5 0x24ee89ce5d32a50a4f890b44839b738feaaa59ac2508f6438b8aeeeb8be61363 0xc6554650ec0e3e48ea68b3bee277c552ae0debd7 0xcf807523ad7785086404c6a753d9aa332c761a36b89644f4caf9c465d348cdc7 -1 0x374aaca7f94e7e4d62b984b492a06df25968944ca9c250f10169a3628d05b0f9 0x15177b91e2404caf1bc42858b20e38e33bd99371f61814d01b686d6466d015be 0x6d0bce74c9375731d41d59ead889d5637bed4af0 0x97fb69fa87acc3c0b0950c22ff5d2adaa87f4d1d46026018938f266809c69a04 -0 0xeb8ff1cd76391a8ae07237568c08dac28d4e3bdc29575ad191bb8d335bbe2ed4 0x312ed03ddf43c67bacb693434da730d42b8929faf6c06a4204688f158f45dbea 0x68e3c86ba31081d59373df5f32dd0086939a36ed 0xcbd0757cbe0d6762392fbffcd0291d0c653f0470e3a28f30381c6d42ae594220 -0 0x2eab5ed09657a381c33b279b1670be03708b1e77ac91370087b426ee9aade920 0x5fc053a70338a0c868f11b2351b26d9be087172a77979fe0533c2053bd1df74c 0x8268d3d9bcd233db97641001e0a5f1c6785c3a1a 0xd4ab95a7d77b2859aaf8b8674608460c8e53732d2beba89d7fe76466292c4980 -1 0xabfe7fd3bfcef4f049c6a11e0717b22fba93a9bdf375132a3f2d4ecf82c999b4 0x52658fa3ca3d2a527ebf071c78051671545f0d987084b46460fabe57d9367404 0x3eafb74f0fb78383e4e4bcffff6ee4a9fc3f8c73 0x892d21e91838d7d29f4841129cf9a86539e9b2fece80cbde027070be1ded88ae -0 0x2b4a7137d0b31a7ead72ed979ae69d1b4841334900194bc381dd5f71b6a2c337 0x4c393d7e881888cc50f195dba68cbc78f2ff55b3ae0a91e195b77f582cac3d2e 0x48a591f904d0266d32d3ae188504e2a7910b703e 0x97ccfaa3728c749711a1eb28fb888860cf38e67711dea979cf8b91cc1d999f35 -0 0xbd6f6a6af08d750e28f99357f0dfe768de9e8ed1772f9ed1e312980e1a830167 0x17e56aa8e9630383c197d6282404b659c10e2889ee50bf301e12629420484bba 0xd413fb3e1d268eb37eeb38de04734343ecf6c512 0x626eb6781f01e2a71fefb336122ff174e6f079b721bc47dfb6489a1eac87f35c -0 0x7e42fb76826c7f00952ef47f200d5f5519fbc4c78e7f8b958f585306379a1a34 0x738cd052fc0152c03dac09f5b117ec6a1c21d92a2d2e636ac12a05536ecc44a9 0x261318280a2593780fc0b49463d2065bc4b9acea 0x2b6a207376c1f17c13d8e4da40ed4b2e60e1e8734125275ba89866a5abefdede -1 0xad10c20fbaf33d13bb5fe184a340fb29f3d32a83c375adedc4d5178929472f4d 0x7f1c763ce7e89e090da9ec44153759833dfcbd2990e0beda598f0e041dbbba45 0x0109480dfabae54463568ce807b6818ee156f84a 0x457a1e31e350640cb9d4a8709d91a874fefbe6ed69984c87d72dad406f510ed1 -0 0x2aeae7d1d3bc76fd6f02f1234e5a4cd5a78f3f9ed2ca6f6664e0c6bd821f55c 0x19250127f033b2cf9783099f65801b3edce8bf6d26b32fbb5e44abba2d37e9e1 0x455ce87ee8207a3f8771cc3df3d80807e098ae9b 0x22f009e48382685fa1ed6ec3287b17fa144bee9c04c13c504101c9eb2cd1a1d7 -0 0x86d9311b4a2c1bce624d2a88e626614514fe3ba58dd656899f4fe5e6a170b37b 0x6dd4305274e769c830758afe3a56ee4745e4528501d635c48dfb3f80b3398214 0x6b1baa2a2343f50f422e9e7038826e703c584dc3 0xfd6b690eb2fd74672721123d5ae7cafc6cf053626e39f530571c74d0a5d7d666 -0 0x4f4e9705b7c7fdc08af4869b151154ea8f011ec61297ac1c419a2229985c7501 0x33fe5b95279c277bb2cb42dd9524453a8bf4c3600ccfceee3ba01557022e8514 0x5c7cf7b750b74b91212e97c4edb502b13ef7c603 0x8adebd9c90f7c9f54c9e28b17010732f84ecf9cad9c97d042c229572015cddd2 -0 0x3a105f1f5e1dcae92a1d5462222aaeabe96d71f02746692d946b2e092cb40292 0x724bea7cf27436bec762f4928cac3c738d0addc9e68a0ea1edb45a2fc7e99bb3 0xff90f956c9a7d52ac21c37fe7a34801849887f19 0x425245f8953a77bd17f6a99e85de70472a58fd712c2cd062661febfe4eef0f6b -0 0xeb3165ba04a9df88da13aa967733573a9cdb76836b29e5565f4fdd1c10a555da 0x3387d1afba42b8659a13f020271deb5712b6c3146acf6f06e5fdfacaeafac00b 0xabfed9eaae37dbc55c03dbe890b64e59b965141d 0xc08a357bb6684da5fd835fec9b978f4b0d447492012ddfc5b02177f79362b432 -1 0x7ee780ccabdde306b172ff8e62ec05bcbc741e4fa8d5057bfc172d18b4121ece 0x3401ab66a0b0531b209239bf6ea6438aa5c0c7621d443c87f4ae50fc867c672b 0xbe7ab77ae4126496e193c97aaf83ab9cda87fc26 0xe557ee4e9aaf6a265f8572722719032b27a3dbf20519e7fd932db9310cf0ff4d -0 0x282e2f0d2acbcbb5397705187713d73f3f320afc6e7e12992551ba2b844ba4ba 0x262117d4a997afd5f1e2bb8d2438e091e1f6e9f9e792e424e58bf98a3e438bf7 0x4a58bb7ec2b8064c4a03abc7c52d9d8a6b0116ab 0x4ee60a3d8de3688398227eab55b80b4ad52ee0429be1aaaec1075f07af01f26b -0 0x35e88824f5d5e0019afb1dc818b338ef707fd32487126d089743a94e526a72dc 0x1f9a7cbe5ad46a1ba8d14ca47893b890ada28f4f69530b538d7535c9ca450fe1 0xa34df27ba953cdde360364eaa1b011e98a449174 0x3fe021424adf83aa8e582435a962724eaf11e264d463dffbbb1d5a7e96041da1 -1 0xe5437ebfd6f54a4580506a80e9baf1399546e270c1965daa1b317a3ae119c532 0xf486022026b018521fadf86ddbb73da9556fbce3e41ab6ac4982d23aba34732 0x59d9099ab334d4a3f19a6e125c427912c68fc32b 0xbed8dc0ed9fabdbd5168148daf1218f025a14ace2ffc3e8d8cc24a9a0f6e2643 -1 0x11378e51ed9404c97fa5c1a6d241a55c8cfb09202abd30380abeb8f7fd7c8f07 0x72867b97b9ce0562f5c6b0ee3a844e042fab36a56e36c35e040ff29122355b1f 0x4559ca770e7f95fce15bc54c8d09abdd3b5c660c 0x2224f14bcbc55c2a2de9a1c5a4daecb269745286056d28e61c10e5a745cc94f0 -0 0xacb3fa89cdea2bceef0f27eb974a4be19b33bce137d9e5b5efcc98ef67661726 0x515491ffd2a473f6d3323a318818f2c9df9cd553032922bfe9227daa2e303768 0xa838b09ae1b2223298d1dc43dce715d06aacd47f 0x7a345cb9dae9806b45d6b3067bae813643a5d455f5c20f2e475d6898e3f804fc -0 0x35fab5e947d0fffab73351208b12ad003cb62172763113dd181016ab8297cbd8 0x3c6a548de3e59210c32f3d896fb149f18a7d6ca27a3d101a087e9ece3536d0a6 0x971e954dfc651a4b1743b7132fde87f6dcbe502e 0xa1c0b30b92991df606a0f37303ed0ecfbd3c23239cca4146e0aab00d907476c3 -0 0xbeb835e9af305824af71fef83f68da275750bda01bc9247f3f1ae946a35b0ed9 0x4f075ca8be453f38b035958699dac32d8370de3adb284a5d743253a6c8f1116a 0xf6926d15fc0ee0113ac4840e7d881d92cf193a7d 0xc5b5a0064d6b4e78407e1087a55d81456d914dd106d8bfb4d5d5791e30181a04 -1 0x257ca4a4fb774fa12d04ce2cbe4f7c2560a5d93bbd6b0dfae377907bb91d0b5a 0x541f5c5d044a15390ee23b9f4ae66810b38c3e7678f09ea8736709b4a05328b3 0x17be3026e13c693e72ba8cceb3adb589ed36f14e 0x612648064356c01dcc3dbcc6f8e8be1d80b1206ba4341003f2d122b0d673479a -0 0x3bd7e74261648e61873edca54585ffc1b7dfc0ed11217bc032e3f5159bfb4817 0x79dd87b940b8552f48849cfa433223943faafe7aba5af8f778c645cace8fe409 0x5425324987996df91143b257c4e54a129fd6e85b 0xc533049e9303ccc8dd3b7e38c45db85ad01dfecc3784a1e1b59b54bd8c3c701a -1 0x98c4a283642a6840580b138e65fdba451c87f9bd925329e83744263fd7be5048 0x69410c8d7f022f129f862f029083194c720a955b18970b5cd1a8a83ce172dff6 0x158d9cefd58c505021c54528feeb14ef73c9abff 0x15669ae7e934f1144b36117c84b02db899cffa206710ba72614820121fac67a9 -0 0xfae698f64c9b17dc1ea1c28055c28789fd5857c8dce25e46502e68694aae6c85 0x635303afde839cd5a566b6493a0d068f4796e57c238c35db7bfc64a9d950ebe4 0xfe52a5d0a116efce195baba279758964172e83ed 0x2526f94f656aea49757e6d11eca88a0f102752eebf43da5988b619959f9fde8e -1 0x4982c820b96c2a6fb89724db1df3a0acfae4fd7bd5cfca1d88e40d77f6c5ae9e 0x4e4481b9cd42bb55fd00bebe0b6b90ad468cc02c29ed0ca1a84fe8738e2067fd 0x8d1f185fbf60bcacaf783d8f6436e117d1493658 0x3a14ca8d74432637ec0e4e5f24d0c61e54a82746fd12f146ebd9c7507ec08fef -1 0xbf64b639128b416fbd70c247b7a8e33183f7cf186a1dcef142ae0c288d8a1e5a 0x64f71c1dd604e64c6881e8fb3783cab7b9c8f80db9307337b26429cc19096060 0xeb280aacafb6dc0b0f811f3e2c1291ddfaaff76e 0x93ffd8a9781f73bbf3d9849bf05b40571456d01e47fdc015a70ec6d0a53d8b7f -1 0x7a0879264b7b0aaba53f632acd1c3dde1f03eab3b4adaf2086afbd4f1a26fdb9 0x4d2d10cfe5fd159c64bfdb72b9c39d576fe71ec7d1020741066d853138c896df 0xdec977d8d5f2c79018fc185ba5341f8cba15f0d6 0x97f1fe90d4298fd74c44458f70938a990ad00befce1fed9c9fde7334f8f2e59c -0 0x3dd1e43bb8c1f5c634ab3b4c0c09da6fac71fc65ceebdcf825779ba9fa96dad7 0xe09348612309ac2f310bc0cd0d84f8fdbf67b3cedb93608792259257fe1af98 0xfeed5cf7d996ab0cacb4b0baf2450f6c51b1efde 0x5564e87cdfbd1d4d91051523f79d029604462b3c0a326c99ae2cf1920d3969be -1 0xdf54d1d5e32037affe7401bebe5d24aec562c37b13bba5feaab5b77826c27c38 0x528086fb0042ec2e80aec346fa0b6f08a879c962208e6dd227303ae3c9abaafc 0x927940b4fe41d9fe519f9580a29a351e57203dc7 0xfec469c2a340cc83511598aceb236cb647bd3a47440ee7e8bd8d002ce3b3f6b8 -1 0x5077e7de2d1fd106ade42889ed4bfe3495da0768ce7db5ef58d0211a9458a646 0xaa9b688acd7b6ab5b4b1c089045ce03e22490cd2254278a38f61618bbae820d 0xfe512fa3651ff2289172b14fded9d6975ad9d96e 0xadd8c860fbb1dcb130d9398500809d625601123c8bdf0f9f37cdc04f5478eb77 -1 0xcc606a4b8339906e3aba6ca08c0eb4c0864ba8fe458cf34fc84a95293cba96e 0x22449b2384a73d51d3559f7d93034096727dfb8eb76623e8b10b1850f11414ea 0x120609da75cf68f9aacf4d7e8b627eb9186ba27b 0xbdc844e89608de09ec2b1bce2c8d370e17a394ed6a5148b38b6511fc62c630a2 -1 0xecf21e5f54d766a37377ddef3c805bad94e1de49486ee0a8267e36dffbd824ac 0xb61dd021b9fc6b7d39ccb56c66e48d1670b264169f9e68dd245ab1c50a62af4 0x6c0fc6ccd718f8d58c3033c13a5afa3adb10456f 0x3eb16ea21e33eb3fe986727dab3ad306cb27feaa6fb42aefbb00e66694154795 -0 0xc9f7eb02af669bb4b793edbaf3b0e7c8f131d459f94e71d7cf715991e67aa165 0x54b2deb2c57eeaed6c93a8313775cd55ba346ff11d89e91fa12c26f1cc45970e 0x8ec10af9662dc17432717d72ce8b62f4ac24f218 0x7303a277360590a8a25a495153748fcd4db789f9bcf4d73bc303b4ea02b1fee4 -1 0x226c7a8bebaaecd3304d01ca9df5a9b485fa31fbc44e5c9168d8eeff7cdbcdfc 0x6c170b6fcf4b8d2fdf17569d332f0a8cc52aceb4a7389458ed1606a7948d5e48 0x550d8a88ce3e8154d28233ad9a27945058b8bfe0 0xb04b8fc72c305357f84fb647136b5d85f0e2101d43e3e5bd0eea98519f5d1b9e -0 0x8ab2815963748344b4aeaaac472c048145b4f5e1cc86093638300cae9bda1a23 0x83da77b10ffe076980296ed4276b1b81cd58a15f7c573cf83586e6139deb93e 0x945a21e3277e6aabda145da4cbabbf6d592fbce4 0x889dd7f6de4e9c49153c728d0b9d293bf27d69d2ba4463beee85ce75e4732edd -1 0xea57e0ee8bc336c781a5ab966976188c16ef0fa5a8f36f858d11add5343c8ee 0x459d6f341d6231c1197f4af6c413c469d97d61425c0a17454ef6bb18db2ff703 0x599357d348a8fa302ced7d6b99d532d95457b543 0xed26da3f378bc480599b79a5fb45f054290abfd91cb463e193988aa78a133a86 -1 0xf0b422543404ad03d959f34c890d5de1e08e25ca7b7ab02159aebc6a0e83486f 0x4dfa3945f699962e264b18fae4da81bde6ba40091b952199f9f365b0bff00e3f 0x0b0669b9a9f43a2967a428f838191e8c5b84d3bc 0xb673f86a1928f94c69834fb94b522e18a9424022c0ca00d487a5a6af26a56d13 -0 0x84cba050dfb80413562659c50431c99eb10a345d87e555d092aa0a9bc911a37f 0x3bc0e3ca54d03b706d7ca51bd20590b4c020e1335865e8c945a8558cee5fc7e2 0xb223a6a0b9a4877429f984243c904325aaed59d7 0x40bfd52c11577ec77d87258121035b41aefeaa1da5e7752421d5cb51b640bddf -0 0x1cd25acc3cc1aa3a47007f5442118baef3d61f6f30ba4672ad01f3fe2c389081 0x3149344942e7dd5525f676f9032e2d7a3bc3e154c3ca0469ed77df3639385307 0x1b56eb67f280add7cfe89727aca1134274443a39 0x6f534d1d9bf2218cf30617dd1e0293de6f84ef58390e73579f98bf7d5f947d44 -0 0xc308adac5cd7459f5aba700720966697f3b4ea36a4d9414e11d6e72e24571a6 0x74915e161c7b94d5844c22f3c6a34b3ad49a945180794ce94eb3d3410f45093a 0xab639feb2463513a9ee703cfa33f916bbe7408b7 0x2fffc85af8c55e07853ecc59522361f58eaa7063a10fd0f89205b62ac5cc7df5 -1 0xb55aeea98ea3bbef39431d8cc8a21309e288db69b9cb87685f82f9a3badf84e 0x740b80528ceb16bb4fa20ea0327fe93eb6a37580bfbd149981a7d00728ff1b36 0x7b28cc1860b9fb47cf7f3f000564bf6deacc953b 0xb51689bd8af058131fee38840f92da123b41d9dbc4b7ff43186a0c9368a4af2b -1 0x95d2923d538780533d6db5fb1112fdb426f1d4f16399d2a732d4e140eff611a 0x37987b15715cfd43f44e4395b6f449bce0de642330a9785fb48b4f00c7f4ca7e 0xaa917265aa6903d94a63c1cbc38312e19c11a053 0x78205715444565c45daab6b0c0787ca35b5219e3e0ce0bd4445ca421ce407997 -0 0x1ed4e98c251bbcceaf2a0e967cc854cfe4d92fe978909891054042779c5461b0 0x556626dcb3e0081e9e7152af9a0da2aaab65fe584c718b2bb1fc8c78c52ec167 0xd59f6c5882f2279097e401938d4c6796c07c7b9e 0x23a88397f97ef6f4000c7a666e7a803c8733ed4d19a2156f0f3c6093321e5fe8 -1 0x6450a2b80accb717e3a9c219caa01c86177ebb8d4bb84b9529c1335eb74fceb4 0x31966d6ca31437a33710998adb1c1e0c3c0d5cbf7c1524ece60f3c404df70b5f 0xac10ca20fe0977ea9f448e73f6b670d9ffd42ebc 0x674136946178355f8ec71797957e066bd246cba36a6d8f7dd5a0ea56eaa509ce -0 0xc10537417a178682e6e351a2eb7f1b971d37f7d9376ee2697c08b0e723079e67 0x4edc34693d7dff3adf57b018e87d929903e1eaea0f2500f52afed8b1e25efea2 0x32b53c2434f03884164f6cdc2dd7508b31f558d0 0xeccfc571b6b9a9a61f78e03bc5292971e96e542c1da09ab9d1a8af0cc4643413 -0 0xa0b659da4e9bebb27acb959197979e51cb56b63cf4324ef981d5bd01f815c0fb 0xb05f93b0cb57b04aff64501bc3ab1f84a7b25a1b4956b86729bf88964ed934 0xe33f06b81ddb8042a93648b4c13f9cbce8a04c42 0x76e03dd437fef2b02edf4a786afd1a24b06cbe73e7d21f85faeffdd26a4be022 -1 0x97341ea7db9c1541fdb2193bf0180e7515bf91215f4368ea17cd6e88358266be 0x3a4c5d0715f86221d1aeb7a0bec2a4fc5c0087a0e65c232149f8fdee7ab7bb93 0xcec616376b1da143d9f51738d4ca5ac96a5cecd8 0x12f61e7256fe5514b5139e8b6c24d5d2553555fd0a8fabf660da4305f9cefd76 -1 0xe0024310417f142d87d3b3443330c0b025aa927b307804f5db6a359cdbbfd478 0x3046a8a2c6ef41026d8dabb1d3804fe8b36338029df6de0b6fee303fb272b211 0x653f99f5cb0723e919b6dd7ca0bf9253b963b06d 0x4d2f79577e6e2cff93ebf1f0607039bebf0a24a88cc4a406dcdfb0f8c0216ef7 -1 0xfca536181c563f910b2593a75a68759a1cbe9e17add9e5350e5a9043bf2f6177 0x72967ecbebb5062aaae304aecefd33e585769c95785463f7494e0c685b22576b 0x7f9fa6f0fdf344c7176776febddc15d4740f5540 0x5cef49c7e6514cafec83cba5f7293e56fa077a92fa1164c60878e36579e70fe9 -0 0xd1169af593cf12684f1f1dc5ec0099210cf6eba379622ab27d54db6afeb0cbb2 0x7ea3c6458ce7d2f454d79f7a951872d4baf1236050c142f42448cca41c4dd65 0x50de7b9f1e46f80becbf9ccc1de9730c0fbb17ba 0x6b23f37c6bd54ed9304788fd2617d2775a21dfbd19bfb06177c9068194ab9608 -1 0x228682fd3644c4f2a2464fd9179b1489602485b84cac0cc434bb13be6f52e750 0x4443018948d31e38c24074103e882cf6628facdfbfc0f1bbbd3b73a7abd1b8c4 0x7a9adb31ec1623e463138dd4ce88df7e791c6f03 0x8a33ea11489e13b66d6fabbb2abfdc6f13273fbb9d95a4d936cc073f3b370473 -0 0x68b4d46ba8c97044fccabbcedc53bbf362f85dac7d5b6f20087909bf428cd977 0x1e6a77282107ca56253584a6577d937107c13c914fe96b00a49e66ac06a8a878 0xc17e64a2b177468f6d4c9b556b63d110499112f0 0x4782e3d06c981933650a9aeb4034d47557099cd5bb82cff7563837517982171d -0 0xadeae9e3caefa9e5220a88447eeb24837f2e8d7d3b9f4b80f105cf8e055ad663 0x3728bf1007a5f106d50004df752b5bc611046734225bffa10d4553530c360b70 0x74dec05e5b894b0efec69cdf6316971802a2f9a1 0x27d57b0a0c8f8a6c784d860f42f7eee5bd459d7ef0e76676bfb41279bc59b4be -0 0xe25df026f631d49fec33a23fba5c81e63b14b7255dfda8abd1faf24603460935 0x63e42f2844b2ed1a81738ccaa006b4c74465d360d529cd94df16774d8e38931b 0xe128efc01e4244ff423cfd6cd41e1cc56bd6866f 0x84013bea6d8f9df22d66fe7959145c0629e7367c1b951ce7ac198c2496a69c84 -0 0xa1784d0efa4a3075f07968e3c0969e064b986336a9c80a95cfbe4e314b13ddec 0x456bcd8e76c21c691658ed4c28158173c1f69b35178c2ed8c2a602b76cdfbede 0x983873529f95132bd1812a3b52c98fb271d2f679 0xac1c41d319b7e8f7aecd0d6130e8c94b9f748333bbfa03036aa69ba4376dfad5 -0 0x6526f92963f2338eb771ad90fb772d11904d4d4befa98b8c272c6a0abfab081e 0x5aec457455ba8d5aeadf17a688ca73754680ea1f6265c34ddf6718c85ff1e1b0 0x19e34d09c2664d4f0829b1431ea73dac1b2bea93 0xc61a5fc29b169a36e990142a63b49823dd06fabbbbf54a4f52dd25ff443ed640 -0 0xa026fb5c2ea92a4bced092ece32b06ad2664eca004ba355bfc9686b442131862 0x4dd3e552d74fb306c7e1a8f1c6c4f041d8bbe50fff482389e7959ebd90d67259 0xe697fa7a3f3165caae7458a7e7437fcf80a327b2 0xbc1705ec12a7475ace7e60c33bfa6a64fff88c538675d302ce603dd572f2f14d -1 0xf2a1cf25ff00c08820ff9030f63ad96aa1e6d2f89ba9a270953610a7f5e9ac27 0x14c87ef1845068dc7d81686b4bf201676d48fe2359a05785c40bb674873673ab 0x6faea455ea5309259cdfb076caaf779488ddf8de 0xc7f21e2ddb3c14b1b29e355050dab2eddb82e1e8306ed184e277193a53256043 -0 0x9f97613b227a9f5c66d7c4c44897aef59bf482c72434dac864b48089906f4372 0xb35e44f4d594816de88c2e9c48662134b69892690ae7c844dfa35c73e6bb7a4 0xd6f440196f5060e81a943fa5110508c8430b31f2 0x8d6ad1adfadd98aaf2365632ff00dd9bd479b687f3ff43d16de4e0ce67c90bf2 -1 0xb34b1d4a28568a653ac8c1695dbb0f8da387fec9700985879beb2a0871eac428 0x7aa14d38f04dfd090c3d688f4847d0f55c2a8785719c63ca12702c3b2c755ed8 0xa6a688f107851131f0e1dce493ebbebfaf99203e 0x874c23e08d44be9a421c4c1131448c5877015d9ba1b76dbd94dabd6809ae0bdb -0 0xc513f6281e4c95bca3e17a3ff8eaef844b09b17f7d7fd24a2637e81136aecd1d 0x75810f150e589d54765f58a44263fc7b26cc791bb88e771777aead7823327431 0xf61a659713d29221646b451d2c675f657e33c88c 0x1cf1502b1f131cf5eb85243f25aac443dbb2c8727c9e4d718ca614012da4f14d -1 0xacb42d7d82bf160d188dda513982a48c9eb055c84a700bbe425b96ad5e1244e7 0x47cdb51f4a5b8fba9f4cdbd5b4ebcf42438fcd9f2ac996cefca27745140802c6 0x710bda329b2a6224e4b44833de30f38e7f81d564 0xdd0aa6a391179f21e01e5670e46b6664d1e23869a25b68e94bfc203148c8363c -0 0x10077281c6b1283c4f8f7b3058aff92c72e5694d9cc53f1767d8977a50c4408c 0x3a02b1f7a35f12e0233f307489037fa3dbcb0aac33f23291a659068a0c15295d 0x22a98f59ded87c4143cf26323b0bd779580ead02 0xbd137a0fc25ca845d0863454dfbf05a78d8f364d821d569f27fc85dd7aaf6ff1 -1 0x58c87c54c33e54e868d9f6eaab464787256e5d46db9f7fa550ff6daa3f0d25e0 0x7b716d06c4163062b503e32650faaca47ee01f3ec269d62cbd9c9d6cd37f0968 0x9b2c4b8c833278b71789999a3d808b1bc0995fcb 0xfdc089847347bca14dd6a782cc102755a398f711f213d9b50548f1e33a66793f -0 0x6c3f7f4cc50cb15e4d5b1454088e860fcf3484bea53979597056238bee8d2eca 0x354f75da6626ded0a649d6749a6e6f570a353504e4cf86878397771993397cc3 0x55fe002aeff02f77364de339a1292923a15844b8 0x9d1e9fda1dbec9bb273fdc32b4b3f93a478861d0e4a4f44f059efaccc4ca3aa2 -1 0xcbe9c14778b0b3204ba7a286bda1efd94eb79f00141fe807168954aaf2bc8887 0x7ddc9f6c4d6be095ad846a2d1e12196fbd31f144bbbdb672c7471e8a0c64ce3a 0x299c7265388216f6baf12c04bfaae0391c2b1be5 0x6a247771ba60f3e5b1ff3a95ca6ce256a75a7c8b4c2cc74fa02541458730bf39 -1 0xae6d5ee5e33792e97504b10f2a11e5e0945fe6e540ba0cb530ab49a3b0a6aed7 0xcf7f67a2f5a6a8297b80ad4214879ed6b8b6b0d5003afa6676bc62010aa152e 0x6dfc34609a05bc22319fa4cce1d1e2929548c0d7 0x33460c004ebe3b3c87b07a5228f1a6b163eb970181ad59ff11f8d03b729298e0 -1 0x77853702c849e381674e839ff6779d66ded67a7cfc325cec9dd8b93d577f1d5 0x7809c39b7692e2fa010a78ba9d521155cb6151b316daf15098684dcd3973cc72 0x4069d8a3de3a72eca86ca5e0a4b94619085e7362 0xb5e51e980067f75f75981e80b4b2d60da80dedf8fbcbadce9ae871ea02ea3c89 -1 0x7f8c8449f22a576d8868bf3e8bbe14dad8de838b4e45ff320fe4fc794051fe59 0x1ba41d48005b10b79602ee801ede56845ea5b700aef13dc2dfe93812672ef053 0x6887246668a3b87f54deb3b94ba47a6f63f32985 0xaad2bb9310cf9aea16caa84141dfcd3de424b7cbeba41d20a319b3c35342c2b1 diff --git a/evm/src/cpu/kernel/tests/ecc/mod.rs b/evm/src/cpu/kernel/tests/ecc/mod.rs deleted file mode 100644 index 19bfc89608..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod curve_ops; -mod ecrecover; diff --git a/evm/src/cpu/kernel/tests/ecc/secp_glv_test_data b/evm/src/cpu/kernel/tests/ecc/secp_glv_test_data deleted file mode 100644 index eeddd62b83..0000000000 --- a/evm/src/cpu/kernel/tests/ecc/secp_glv_test_data +++ /dev/null @@ -1,1048 +0,0 @@ -// Sage code to reproduce this: -// ```sage -// p = 115792089237316195423570985008687907853269984665640564039457584007908834671663 -// F = GF(p) -// E = EllipticCurve(F, [0, 7]) -// q = E.order() -// SF = GF(q) -// -// P = E.random_point() -// s = 37718080363155996902926221483475020450927657555482586988616620542887997980018 -// beta = 55594575648329892869085402983802832744385952214688224221778511981742606582254 -// -// a1 = 64502973549206556628585045361533709077 -// a2 = 367917413016453100223835821029139468248 -// b2 = 64502973549206556628585045361533709077 -// b1 = -303414439467246543595250775667605759171 -// -// g1 = -303414439467246543595250775667605759172 -// g2 = 64502973549206556628585045361533709077 -// -// def decomp(k): -// c1 = (g2 * k) >> 256 -// c2 = -(-(g1 * k) >> 256) -// -// q1 = c1 * b1 -// q2 = c2 * b2 -// -// k2 = q2 - q1 -// k2L = (s*k2)%q -// k1 = k - k2L -// return k1, -k2 -// -// f = open('out', 'w') -// for i in range(1000): -// k = randint(0, 1<<256) % q -// k1, k2 = decomp(k) -// if k2 < 0: -// f.write(f"{k} 1 {k1} {-k2}\n") -// else: -// f.write(f"{k} 0 {k1} {k2}\n") -// assert k1 > 0 -// assert k1 < 1<<129 -// assert abs(k2) < 1<<129 -// assert (k1 - s*k2)%q == k -// -// f.close() -// ``` -// -107686458338979513480781602362120102289984183046072577606170007916778439289747 0 356346894760760276087626226488432151004 217318197015539988822336610002398511790 -97731722947559024681716452282219957975263978740712958876927310887143319903906 0 112952311639597105943171331608306471017 175336324915113800172956243369347117300 -2257829552013235716068488356709347701442172630826500503717521931809955398781 1 217177198954269660712428636031296017582 3684030775039237366050299655648599135 -71761682687641178423116382761747336210578685609908626585985077055172279137544 0 411613887075019707474159017744114450590 151273920832553029973546975684718652297 -62783928497728404736644220003419467199424172530427306334104012091526950581001 0 356354992720035849196985968820610194620 121830846826914942895951448813855579471 -112971472393035011922242209868007500713878640043238016872448018125944618781583 0 264487018368704977724165996943173512042 285484700390970001296784595154826996438 -25461175730825076518388883710792204517183536466495211664518207534042643023271 0 53545034099897480198264594975145531554 165190004103626257367815278381722433459 -10962584108805305863094865199814437137956130063005282662939859589523585772615 0 90483095476203826306018406018219770404 5326811612449241559045288462600857043 -21408645369894329120094779746078410565876089108909007382205008045148662398111 0 169498534374881290135975052358458371275 199886353275111787414759613295836178129 -65326375044696264566560466301481064601793419054459415922031817773354408022228 0 326889546721088012701270218031953215910 26583831855696219665477220232194327011 -111842137291305858378969699712312320844113500301818822648488609509780224870030 0 358588530025521890293873553071904842301 88674112578391012970148365669818035400 -48388182292356997229143390949095736048040642837574748490699810267691249078960 0 191116952754346254899628671681107582298 3408390702158000891839529313245505366 -49911865742480051061735560563065284927074022126277784335249746631933669970897 0 264037823145107342980854497957288112985 145855430071763322302379346046587506549 -88011064228057586998752667343704087902618094280685377517902958647570583123745 0 290085543470595515239894653753370693430 14756770119558000118490886433198818485 -30009968535060400275055240120220617898678094191724689489436884141916706936534 0 211620448108618886279172652130665812535 74467405322271372584718259590825243476 -34092438719895845022868954519742157397608392841511147838641931896689655705253 0 253712681046270797953577480659601778665 66858007125234224691217453473504358854 -19933194854798030957468286196052665333686276181935348426681986462367228827670 0 375367433699417734388966846244889936297 153372293450489296720756152887743704611 -32787073799415355718997863631125631233718703175697269759886364921392938882062 0 32891808855102024456953515253011980368 77940487930511577869859814604957376151 -47161791275725104506288614798750474473280618024790840884854100012964742065946 1 312347252717255682891067286523421318301 21856946250000738457657820811270992835 -58396576252135286293850441649962814493101241331772452868792542593467258981246 0 386435860687283118326199191789962070616 255307079693023397139205720942342135557 -115060515468385408144672212905490573998274019736758361938753096758293685607743 0 262798188520125902195962934507858841959 189803084283929572864130171271092212858 -19779012972836662453985156894338304952374603293381723464387692605961521693557 0 171081274587860547547912949102174979013 57465589301127450463945502034367865282 -59557764191509441867598013645714777503349852305403580697469336648469031101694 0 429701868031669440868051107540793792442 177795303910926751081426844989960403303 -16019578563607143196328852982379858266649546080168856864469298112235597680474 0 98958663551074463176660687631490644568 233393791878168160719378568078865021477 -10562725777839522399058666198047373981313723669074850829621258512867268073802 0 292926780624317082485256650443783091710 170012541910497166675603099940072757057 -84254931149527559985613453333063169649668630660679537074894341142629900907576 0 132837232740090251375656071644472384961 244976582628543094928301908837538413276 -20380321447429541046345292124123014967993322597369899508073271459317646942626 0 111539426712964967017058439075348544721 136457255271890938279674096934490549597 -35823478843930347084388595466123917813382623935222519582824342772355368930668 0 344747824596064962262223971586512093242 51297481552000860172020127988198453167 -80241848325182036157993488624805524857219782665695992444750535276735583413862 0 82340229918369821712300343919820498725 63733637513653433109167300290555281634 -97226356911065213675760229768195495128204471731783944444577985090257197910378 0 446014776705927330915460883244932821691 177469707620482546783366938995769886978 -109428260880714527563984277495597265036651066654038657919872567580138165291774 0 161672361239146061712035067011012533028 135318658267642259767200100178501163101 -2456794270872000782363234481512922390542878827204431899833426565233895023893 0 74828733009148785536127298517662120591 179295736453864396519345103177285033687 -110358259814550367489159660516984924641020557067772775167486584593266233675942 0 138136909202792061850367051759964362547 264843241982079857099222090528590000425 -45020250230141050051522559508238120581888241267457937696474728271972253722265 0 87549999760912063842087076828777369676 238515148236919925661625570698391472724 -95065683369763634417612024676489081265800406235065297734312257621781169765813 0 118370857981563183490914121076784546191 326659433929093684815387239002458454431 -69584751392302470219889523867615373496183607643011599446665472330683539767819 1 377919218162491144365821268230495796416 17705788605085926950263754377896912006 -76415630284491267089238244223629171147644120761858316199029811577615719231449 0 267622097681648402314140457745168493587 89588280990502222245207445170640720753 -65364085707349281956236549866501748823856136841940266620950177684430018843445 0 132267725211117920683502313183506048518 264214091460596903951215959616574011471 -68496349228346023078504695033699882360944999476909408396270127390352298425491 0 191993299428900172162222144049340318833 79634457289786421733400673100175598545 -102993915200052545800426448078657787436216212063164501066260753636143148124940 0 163440127586444525288823079326270664890 325506850940071318900779564616253601267 -52741671333983024545651352953613405692257890552619354868798426411597863112154 0 133590669472473250569301044874251397694 237372562462678299696726017426468829440 -42639987468269725754102300157336909309545563953519496854334708256248948394766 0 431063745053160358927691763847782968808 193669788629057692406011889207843014678 -29206368839571441042561819182046811696223127479198968497360791862630672671515 0 277986923316053516002841054408403183641 59592687435443357148382971479680131059 -71973220179522107822101404659171226257144464633003514672612098121557338672548 0 322909954267474945791248655897066141536 293050217180619359512277537010235537383 -56189834032086453332601369686267448852455834917612092225939516574071065332626 0 332660145724338149855163033301023693251 12611492445534493307445430497075200035 -31982793788179558790671330796787049422460889252209689229489001432296602499953 0 269464330447411793712601526912009659097 167182666821688542355385880931284236234 -16518078972553593549758733820107997479660283577033904866692689456727784786478 0 304102879054138054435565170351520810045 142905791587513981498092170520643879699 -94244716909336223464503837369998701109892007583988212494377056030355472895376 0 467093138757680915509388829449293013646 226426884035766381418834144446771642547 -44467883430830968001822170634058366419732129331210332931260658447763801150864 0 281703191174596317562742425945281032238 95931914823602058485208452447767234443 -60757843444627208177611775662180748251067331633733354506962740528567550678786 0 364792843578455602540859258722930806013 22985918465963329342631241716188503937 -84104433821828104212024577481718029429333421426779064758944750873197256369894 0 194568855013271786800809508380541745446 229446727694933983025749486384962695170 -108248973610767750691070182098073572949318237420294605687534705238530770145606 0 234790458844778294547046820000183347960 136853423736681350580492611886974941498 -34786748041297346844402381070612885237669631090846858820446664948156258332298 0 129736897365921587142334978346099758200 107959792757944633156449214295086334266 -102810527058268355991029023336552190810340318709366271113520137917014391654159 0 148985653000104585855012217007602848810 285556390393026559166136104430077303381 -107236246924500784286686156332239333699163162126136960941861933347432937834765 0 313200289458568331799816761858395780373 142680872231228032149222767938200767657 -85553968400939667567930200707730584737604794801667126532720383360162315234514 0 307472696532882796737703457423188514874 296914695978283876709758037260643553508 -82837814572589462755293438031137912897564739078397083645207882986761988791535 0 405681676908352708549992171820572503559 64874756718813142564650981326069701866 -14834821979711174342347418586239866307795518884311084112398157923083605490093 0 218906139321825148960327027014716059865 5398134016072562185525333168620524647 -67348245348505927914271664564446811443588683749779783570350111017143847898097 0 254609381839337906765057986784175733591 270406925366933860203965644997129234345 -2063429500958983634064267943275200028101999214514376275082775637608230047487 0 285864921863374935451402978288991111210 140974939702532814318715017185194082867 -43625545265751642319530548668823021113923533296212745434154635093925340318646 0 125478893142043257031943264609007785461 37430901158365833149937667891377811071 -60254767192676745108319277057026673966649840682353512960220473255788137185527 0 85569735097631560223833443532550979934 34248462751654285214565728640379369301 -22601862891817703523530064842405371545632680395612261401387375143186171365012 0 283966364359137844268584710239840721584 170741157684288371503271360770647850109 -36688882390308047125609554909707677384136557823091781950850198703673629328949 0 304937505424244255406772251632073662854 107941815984703440592696953275956588043 -33208816598042721195745544971211794044038312604167126475397073583741011379950 0 257404336173099263206930483012004812962 170164618789543610213365586260734182170 -39297649570333858765003888694639533352497515176239825056560515607741259731075 0 156145052967030386105499135884582210078 54497667467930899753813922299913172250 -54637872245524887881032600305350015290272875899972729610331439185620118773290 0 395077376010241740810432918864166035177 93852844258976704940002931907949151024 -88477016900610413158940577652743317396447829713610837687077702531128464718461 0 323324411142508508530456441228668291478 211151247246769617452166123756966142392 -41598936008198731079884859239223639848304990290666848641634620437372496446222 0 85805582579665251120480456720466090942 143694931912132722751347446727602834816 -373480321144091356922533540606283894942988247604407185340481756647747484585 0 345549321492139170474742394376382095527 71251793039915354846447941102663431084 -84258609088809671502152097635250182843416245478956553930721477471568942918630 0 359958445115437616146884546791900422760 209622554133411960664551020684164294079 -9440446501513893859379538380329446864351560840221628307260846615526215927592 0 339246878875613927684268691787103865751 110409799457409467134833722481337492812 -55925186015874123052867495961134073970363053110642834575553633177237020070937 0 281806784121012042937659611423662380249 35752974295355739414008327687425901013 -14313518312862202971090156290618992104192763570936898894813659282869959208403 0 172509775212271055239057363437171917737 59754434248169448120987066734387889569 -111268754146758837697264983055593223063084625726338412602745581213589619484669 0 148588766757522357474370807946949472808 183406433701379055960416600165388394509 -33583005638673182091493970585896796225218364310267915560147416484149113721070 0 269477523980104579175731301759168643949 9676709873142520048074747075857741191 -62097300932243039207983132599103561090744852675653078622645608620927335304774 0 362208007634632770907526194795846985085 125485932487517280707796055413738475504 -69470136884318670533235450273081862983478556458391822259275447541346132086775 0 310210316710143552841177917772786585488 159613051942327765254896657661971890408 -55554003436990105290716930775529027270038014504123775431060453476565014226861 0 198391601153947071608821549634517134760 270804420573327015379180992719114487262 -63521986644990571683711131485684886420035901156096079339255962619956100319834 0 362368363525137175376647673230424566061 125768744712639967948794763559892058734 -39440924532772377254918215550310495861094652949498307275772521934026985851631 0 414440413855403119148203784033017710604 106720348967568700157952776507351152697 -113650350794964295667693497007295227479992987161195416203529076544156258245391 0 343190891658990042243000059774980232540 189993165912278571614396125540525755774 -12060978044921297112545705497869188349722438667919780638052952059493726821175 0 276069257613135208945773030655414028504 136285308043821734911594367112649257958 -105789307135807612086098817909267525375954832198992605847542954772931099584768 0 433325692578475708153991436382791427019 129921641458780719273520811559813133899 -69433632214344479101211792095644966808721160926046498793260353342027682868057 0 322509369037623913505107743632239544075 113299268149946372520910291713402743904 -24460402077968983409763057622899795447251649366692157671392145935347337291340 0 377175009018645852452875383590598444502 189664810756168184247801967219877824466 -13105151318101002473679798160027174567013844662394185145706518743566167126478 0 323031433307377102344922565088285052842 252869998470404916934473703725896742636 -108857916118591629460855844580279511079247120430501133673969373400115772057125 1 426082041095436570749164513448724776222 1344996918599353003175194108411055268 -30899793031967542499393325401869717204968022446691456422484518182039754464155 0 41189065439231721965660637070014940974 74657786728707478548459140791844785156 -42660487964938323261980840451036707322486281830165866447724589559779889415976 0 245870233345930804561144731219490223224 127141851294877882025178836436796455630 -37387140132555614810821730754277949363835605068841039899738621980294997933895 0 150159182755561417060435659522370429791 81581054221512957112735258396165241956 -17159447254990270892119166147401308949163847794289817104630361802056282161232 0 178479656888104473931660197150990396438 267664410606094914100059386520135392190 -83456707800243832322540744996978769631335456434082303223204532562382353057651 0 189539581116876000304501442051787974114 162910289043751013042300136827979906800 -113368421840773273729985473673927531323544016485117672517168688889432454633908 0 154358647219745178004669717477745117496 307142436563599095163767756819125424615 -20869713211088908516610321654109922950750719114597192027196125805185700198151 0 163910418799610197682168483144539230176 269759729726322927219190687653596003404 -26735315184114124272334548425279443508385712213611146968009696504465911588206 0 205240627494169192287993034411152245884 55210888999056597595042722926199789722 -76797233958219684103435366228834420519498314052856011503922550071298714493968 0 353752913925531198606092538070475073285 100999184689090208645863968218391875151 -11501316262162300375979127025292112839206756428095011600235204382430772976348 0 195671623720846234345100097909164659134 259549311467580394168940695171794399335 -5499954749584490909339566957350502760759610806298213247834888213930980529510 0 209970326288894062270149213135164632202 275990158587359783835324624759442792678 -53847515309899500393679932310704128764673276395570498453241526064973054637402 0 386748687160004843705523391715572288230 183566373845857071957970382941286627183 -94398654494023851108752608167055715860772634239165124544357529124948024842340 0 318202463785473336931564575125916415816 215994684981510630809277694393380501973 -36656493266386369358485910842591170308555076019463367278838084994120459557571 0 308392141620030032329036878262287747065 64988565224703418797277861698620670809 -75480014152354054865761886712676991351270344326843864004260186153405983133998 0 372437256584161244703639260690957361080 140504990402702255500316200785438278833 -67845944893478136372397715294465752305976041976019929831679688643550119531129 0 197110320955908842776146885729560089629 288003601122109895835307983914370681478 -76194119006688802604288385379861769162591789626501159400751522647724594545567 0 232558586344143021136166563949968790726 242936528625421354837084801661531293926 -66263326507660643835841406515807849024321176870372435186922902565826928481575 0 363141205821968169207217125362702694057 24124123552981932184055754629505844469 -100510734743907488806160588666365988971386897327222149443147607306346120261973 0 146736382026094522592000203597444045683 251130708647348225763809270898929689802 -106838670139740114119336784417179584792902530057009714546831487361012906555206 0 156729733027065941832300837780302104252 314861685206513924841930242904808419007 -33543180143488561525058529457587069373805642245465749999484886718779422530027 0 302636364615690574441985222636910148420 258201795009911603182967969270428344982 -42376293550195555517070764212355171491212649845139146053182718419396563879086 0 214505598767744465807795931495247614301 220250312117221124128132161319846219342 -41708315326527927606530605609929967186544623196878484244947991822616843065461 0 422452959016497408790412858352857214388 193203881470148065100005062751331770200 -68512452037079184245084712141521930742510675547646233864503804508664757066257 0 404116759055926990018715495649759339336 211724799995620267979461639457416986532 -104278993513583527216365596218140828406818577821077366637253515736225107924080 0 219309700960175345765354469995710958723 41957493761365642465468141776438224115 -23637687988062074535536836626460736022932610867290146670504972195502765686749 0 411466320159322219953604506914875629030 144850037182536950770779086952733885072 -34454085065583771863220809391041817105488111965216058846489348716533968860820 0 89959921400489954802373905038318511088 120246207887238084347671651848073672275 -83553466658358116983877563026067672426658017002912831240063725191815978852290 0 285297639590115733445449398893921571018 16618371484094867032452824560460987383 -106198016004554220782885861301842171203986404151380864747699232902842796851626 0 82665368441597391846158743996519206499 158699660772547503142947674726622999636 -2412046251728349490904636523731547054707439872777184064590980716301678379955 0 156029064792326411997735878049914303030 218327007734106160900186455975212329478 -85549827248433465299692130794783213012323775338788528838269829150138773070298 0 400798738417935822187015281301838737917 126255752295172999358790232265876709022 -56352906677674206650190885055609903018155000067480028198287796592433882235873 0 279904281291796913379742004083696154037 263833140725893713509007441691993020269 -81826269121952853408314729261928669609030833610338260737908228645747312338832 0 134892863149796872258368488750128817992 162517329111328234573209639422681668758 -56192008211982036059885991452268511541572369650877121131529085746842798763033 0 122089862950970893509061696914554705584 58982273009957731101030239303057896571 -72969593427223901509521127044123018193637515344888377582668090558118196608574 0 132240554445150133654143114818269875107 189669618181584005553841464996459584285 -18328339301450858873090279430757744015200259200501814832168986949584625725490 0 359202512377664498590475216792029932971 123257610887559928378144140918120429530 -60638789934299733936784399627375548216969992414721042760121400070452307937624 0 114153834385596405328713011002635197965 252208745458521911410440985341855391000 -39302655386201146230203589603929357089209031319943145241601153862248390961598 0 245881750656469107620797822329785123154 59493301861461980289711225197270903538 -69396760039125483326058793292015824350124552955244762939941142697368970814268 0 351157470476796464652698194217452216874 21643874324289592596755357037707639110 -20535950781569867376563125582768411086661820770085237838146059637004394828911 0 431953114624320033151147002488328164121 245815169393118219952697522361411364932 -95121134607611326587738731974176168869837605985253199468365088889980426998940 0 267010157077067600510381420530670486662 183036785513882089881706100332512907168 -19629727298714885008735622662701818401578464489410057243094275872510923397981 0 220358951735665276554304676012010710476 254145395943712100158581493904225904910 -69671638667537117488131073889392566761401068871481382531070263314464613860678 0 69056235331427993483760583800081151105 124754089428314407649437400438936726289 -28993459126274780495107896804579658024211301127568437350377718845634520894713 0 298455815268012824135884557287574759551 4794090264119311332396989692765353504 -89761943469243289476283906798675689866679823313433228732020830561517478388824 0 399463084252418649889895101742496902247 245564026403136720960406465193122249679 -109621469860163359145663670457995339397120167407940051359444037531682453172545 0 187706625031856063108595783789212967818 45267692027740481755416519834423823344 -89533059741796556701309514685872486857355849436478824201757288604908496315520 0 96145768181638766238564699773883789513 202859337082096288446273484607087706525 -7052142626234127131278711767465087469620224499263816620236823031378038261583 0 44684112013554671633482441103257848950 108786762018060784431611146634573775133 -76372277700927543926434312039671137469190189818239218068171440307970896950403 1 398384683894498832929840186777610581837 15060027996217390549566490505935444867 -57452572127789947239197063776344530705110919010085403933059675941259414747176 0 408415118116495990283414083492667118445 19229766959546016444656260010354720097 -41074937408067080469651391810021338217110614605478547192515247788168770147453 0 110829011585924686654593902114597734686 14514710357218698528567906570472249737 -39565873390410328712511580521194688755520392853001546259091627361794011718290 0 147931211474428516348954052223008014957 2588686350403333618370043585490009986 -9826678039325364490473689190515508401563329435284668997320086698867856934255 0 195797482441694993396111250000283635857 154021363903785179735743928631177139756 -47938676311893291269849706683448233314183144557151810435959258051065283751283 0 112924845918034404651101981769484556558 77045379775970796699414848534633760591 -71280459448672541213810878643784450624893365773407140136482510584158209506372 0 412415320742932991425965119325100414895 205398485939974504492701585811034096583 -56136494246493873092362864296885437030888105729827194451785041791252590886555 0 78271412631416603230407001933789238649 156539486922688533421810125234614178561 -97220457038178489409044356585078630069994270515711296427064275102646138023197 0 401677981147679741428515429286157573795 234901169159723393706688755534272933512 -9936686929793661024205298602943782206842448991340446358953916652771898493785 0 166682409278865922763927516477134526839 239939588260149385681946633859540110130 -73158125280390226328901439047101480593831683011232038025208679646122436209708 0 322252110430085864632835552835789590145 60608131422872334079399203192201475401 -75596514392433941652126887357147719521411447674408113482569870058884772895033 0 427813700056627402616435280397998944735 258925339245893692194870314468013162915 -92104326898045663311563598015572331332221632606001369655192275731634661716112 0 252475817205242971313185174077361301842 174574447298800844157595944527795475139 -115648415624298922529299626265007807924889984318586766673914644144735596839791 0 334035635946906900319782684662104390328 256222817555844913316965571302356597159 -68528965075314528868241019864294759209065875611047065352248370959089715748312 0 354249326587048369706079780379656990841 9071609176030518254552653890532698679 -6493720826777272010219754767878621856847833056483968151344682344527356401216 0 64690257836628859433491183382415517022 270553441017767597919733526573570548335 -74737977389066359520246737039956224501630934516588353461735475942440073505099 0 360722839308287381989237886713337233304 166365158168343739025019532972824801674 -38641809621060324783204452968806356511108081265403385317083815872191515260014 0 233285441155530910933312261438140720116 299072699675056268654299289065111200066 -113241536051480593921132180009716394974001276244270387047659027113495058805273 0 200094400389145243570747324794273688297 207565658324395748922212794711812384196 -46374949107912558987654406526413966453479234253965515811883186349068538659838 0 276535711503275908044142150450954657673 33488856278112708449059869370284955165 -48708364512462655022183777800085424406100552839256300979576485613708305348367 0 368673357190852255015466191911008530182 258458976139480343642907561687340601100 -58869771928646734995023223900369996889213611540105781588570072958329420491352 0 411733062035113837895936835540976533185 207325122073538485869564778454019659921 -13039874849372162693811090207647407157421912552340943237957446384961246230064 0 393467039660542937313068351274195467751 226387756467557126878258701523160727843 -70708709078413839327827592321009800838513525105589134781912260550211615101411 0 159043565849294663527112552234310939292 107186750345985662761735768868999797689 -111449249851085345373519329115394426512320426814411922923678842772841815203101 0 384430572339810087022687951908007631113 294436832047561625049851639322195119209 -95212522140045499358195003386605924239706903286944070930612552309939573361388 0 360805494026108653222812742046739183697 44862166059004280846249627164412641709 -76311362581257297595862774964350279200160099323972425271945563196205835969094 0 303495255639381138175053652058926850240 147946062066742741097451151008734845717 -16590523621523829769736593743756664853868516862373709939503106609882913004158 0 208261604824176268699340201077875676198 255261112577065668550437811354614953137 -97787882548901035642194649701428196976089279109645493340915956472244481600994 0 457193015607441853999894851510410094474 162904773835346057042994116462157841191 -114663704823400572069278633380201655908495654121725667785579346463920283409296 0 381271948388930168696016063222370669246 293816564276425888233725092499868077187 -32985426751861889171403733787250464591926680611683109721051757428207468166329 0 312327761779950991192207269050114559296 133372034223061973875210230339337366902 -103989099299736521783342108002543609878541574647480035010516899214401264986340 0 278122146841053418984055387878358497002 189872418767479637050157932551944381490 -3527133595506876107097506069805339889787471681957627449340931450676004364 1 266954934219050095529858292805700730612 38769145246993091471242421809643171689 -13293370375084288077583501982003508466678165982077681581390924288013707959719 0 281885492115153704108213224890658215436 144277596950497586325414740907485872954 -63450296552934118426272732881796529680506909960273359652483834825952267048457 0 147570686581787226235768457833119577223 189369491567100560972625008125086070690 -45972899145387151825246815392258217600518994758068477649815755739062352487430 0 275265603445678589526962279084078148213 140946103067560437062669985558435954882 -82330656483096018106836576401633239841977324384788059967721359041151169815702 0 305881519407200485164507576246510546748 129621832635451677012234904332380345053 -55730540626150645624256897294914682332980222970065671326473234966747543161399 0 223108849385083607428776806817565846229 91886705720723966022110420721142811017 -44549283782336095066238452716458632750509605976491086604093617185616609321588 0 367179127610181365772433399828597662586 254057959420006776575046104480279539067 -100245925008730237050671134987594848308169345183310656690160154645245942241248 0 375950782405668918317371559504373899546 16046462567449396240191147277777771289 -10646908943433990942023973332754302884629815533731958102349973386479579029167 0 254576667978949556928421957844750313357 130094179138678853803864965597631880479 -103587304434638432495116769991849042633391884154267399888847343227656731796230 0 188636289974654588804230034660890169670 42835283162122713714204269814294488832 -88354376167066575993348277160366047478718348008589317514260427740583590451767 0 333977769362414711117489325103889529860 3039337832526263682474120172101096144 -59024305636736502771300237642590340767743143372457469824671155661780118717445 0 312405637581568286052804871278773694398 160192164323873130549045293350751787533 -94120295846078920748954808534779025685722969743180247429061130027478550319817 0 415471766457515594715891531916663958370 72670682074673920288480111642618209111 -8159873138309217669080563866862964228785616840459204112884786799068353284236 0 264991790935708430339066599317919617361 22186738361411121555499974304801333848 -52719321052610279439415421995994876571564786532533683635082504687258579273492 0 391346845607595753595173305494480742811 44754223666222313665665544158661768114 -23755533938295840513568664280226440163233215948299092080129184330360754953103 0 126719629975820571690779749155191070733 6389752298441695314896424171518775881 -96041133724811678016498890729613160116243727246941689453686995264339094021783 0 425822421677633660508611736482147594514 133047033613216157013477363845438358287 -111550678004130900090137027475230635587623042441677972782459359411079562306194 0 439245439173329448889757715348494611404 170547327020930585965723247980207024812 -85597295507862849961520626818755736043255108881084765891998148676628130912592 0 314326011873035433055974318642554714459 184775567756723335978182565985407909717 -63480653689230567298356544478524785649072832967243691165871284141601742632144 0 408722207132465102620636657062028496599 133160836742033690019711025263407999322 -44128672467597561191063169412197191961681390379875897788090765542488600527723 0 388656933322648960404871060071110162021 27441283370309064724330502099862559979 -8508168076264790501177931721057377688711926058288101824626891346613540505202 0 404286789837665582775443868595396044126 122346768973913406811218054026773702701 -106138572241320038775844273503860569608390121556314749223216383807422451655797 0 142237978982088505094108225079735033134 196690530501022213144755982978643732447 -43242866282711402637861502494953456358491333350480131990239757677029074861028 0 164528650577110257446447365373467264375 69529445183422487554587394669912649153 -112757530902040184842315569368145208175378565850819401172996496297225879953673 0 431753026705079743168820087910862804978 171446391194555038630041205692488894695 -105370198210309305921020745804531137540622814092452001704306473766507128918211 0 96743192445993597195497518326029957073 128431084865230553348905670748476837325 -104696139954594227678267100930747649019126739746322431324669657411673392405729 0 371114422415470899973510609151617775668 123824986235892760467532351579450350107 -93887601427911491343622362187827573957195345757605647775636857588041850102494 0 337802397297045196235328163014820584993 199124546356947105361199869113271186440 -107566187693841972947110349437562490379403282900606391209936526732763903904522 0 280736928421498656548959168024476183206 190049362461427786821423962153775906685 -31136751478910238154400899764515824065197387432736527368249369326460064947118 0 107399523982468840494612649863871260742 56410518457592487419420889965632838804 -56085124019652481219857414373429854459680925869409274562445683838554277494699 0 84348085779658897693801402118758543257 266040289206282920073203689492605138400 -30581042392824183602384576500718663933419623974779695766817030251757303169378 0 394133738549190533731105542895445560195 2813545682008475161500710815072240873 -36220236068556817262838964064076042342893357704707404324970547132228521525254 0 178188709195959854724236317232609063559 255707234378619851501256260421725779502 -112434276493587271472876075314059077287514641175730815492471783212962304168963 0 387495785974986086219064678923071259674 316907330728237894331689604690945877659 -11568073584361057247705195448394696933530703750840333081588998384508055500850 0 255866225708373412565255901699280098841 224086658880774463604250658186796700768 -109127419757921833288978970948289662700822926624037530496607146146689132040710 0 285151843891186109392507652727112597226 92480153893169040901975788746955864017 -14651521271637868938198478150536313531898240759147126493139865661725396730639 0 156327089176794614758406687860249017970 191624082951416090097328713786075341935 -109610374189911464765469224410154821510183303978592254019157577206396010287534 0 203585910497860057711674500265693895639 285249710760659889328501524875525663944 -51527676522803236930081596798334337350633361708843276761212592065670384674905 0 268622388889864814655586371595714830878 271595526673508040605382666744039859277 -1171561817458399942248327408211263040193791518965458842212747217520781261863 0 49642267426232879385892903148237315772 25710226016428389584834573811218212767 -45238871166405028891296625815246887199664448746683992497957496697058194488294 0 388255770540696813057349031611046593739 151109306525479818288354427038520625102 -92538574916351891311609746747904116961427084979531477349179305741514235924205 0 191715153152090426513276595524662764262 87883962510531162921264330071977338752 -82796983592157291269526822075699992955026773050894333679430340939279131767342 0 278013205622022726598518699889707522076 297207735574200586318221814490845202389 -9980358829727683725219447364329120067324289170793360443286836576787847289891 0 91883116418050897368964424182464003179 182026432494666805687713489327319528886 -32899223288309294997321603186616975396638809127021105430584146095357594607011 0 391627127641067825359673840469633154819 105277937933364037132497221798542775681 -53276321418805536619402405060113547508177908554209184046566553261164403995594 0 437538891606232624612412966993451908818 253182703670448114292465674285391467310 -109349736141463266351262031011368750981220399964923963494692927726279569699119 0 306277880622527683383403994562080967546 61022036133647203457338007139905050550 -20615273748313120861371820958997675993396413501334326648438991518155953991832 0 217301439330647643057286625711368300958 159327456733627018843857026283919464437 -33389450328090883869556738038494628837495618257244161038057046079198919708244 0 108008891101912251717133883167808184794 203436851827263450989042116492822588268 -114154061361610110381464776869990282533268457219949406763958219121322885313848 0 157106555012329336571264748701781970019 348801244243997075098575442744234903173 -27774975674653116608292685356278567206603207845642983586277988130489418816536 1 253850978211435384390617370288024385775 19180144465940813841545399078697924283 -28787896695223368398740698847136455312760760370654783934819917521763993627013 0 226704885046832951022780177660004379655 164336558145751931136349085388232541508 -77797617712116032494837477064436159977131414225458798982872485561960009325230 0 113468220815808998860630478125707277612 297404524110586511539415554962278178629 -95734172177327343073093690859093848055221936922529463305465280873526099635024 0 390761854138908223483044245193810273196 299896964997534231260188185913508392202 -22473462270801729576657558660397300694288462490552860851003142933286545112702 0 308041764812962657074768537293251251223 166759699720181689103121236648854763833 -27075403632520717661445934873318854816899714309606236288900451976696999780499 0 410675962916521829041397387665369631680 89118054148437237189951219291465281083 -69179945454776190017629484727375755450632756082374692251488963934581562570575 0 296890011864318086634110305485257750604 217028254752595552648214809539943852512 -80672737875447305255225367393465898238665100228915358964973691220803983440817 1 410187632064935788357771551705418770379 15399599527953549684467376434829745196 -90267054773102523225314257045166698192729025397846775924790517541330649065473 0 112291240474550678866704576973303209313 199108203116889412438719620880152571891 -68832774023071456473637730076207717297551492236360217083019234618613898535155 0 189716908750150013705158152022957514032 284407128938961874551846376703083392257 -100617437689910315233444683671782492140838626250652434665017789578560786652025 0 271652800942490239036690328842494102964 271166537949720206644521887095638989351 -4704374881025350133822559153369843785313635704114606558131754380306129043576 1 319167195191720551856092201868931817199 48473673198928130061786748137867092410 -92856117454743736297214360959540958275297835771176054738565658885795071735751 0 117528869031500495598253941478072164509 299970525644086871574807718679590948838 -24391918712144420531111620200927801930543116475868508950814262607138193431947 0 366223830270768982344606294088232694827 191299002864363316251113487331137399715 -75564716910762322240328061134743607609549062446828819450247110181628288730858 0 316735449175174805705951733619757221310 274793457516266512640323670230090782016 -17069738965035422871878972525473314350932732642012356673452809359409666126394 0 96862300860567391931567018084252386180 194433301241845512241428751412928658172 -97244290271257456723705546897188280253927149069569972116881403303761734275409 0 415524337650474535684649931317057784859 235974258839048944180017313629326090415 -68279559178717885765572373556932571905373046310306071354863872692489278400364 0 254141011574931048298817999293844820288 132213086613461580579188339617230313392 -87537703441315585749623637015475318648379787139837364690523067500578205556212 0 340992231944883893794785039659663662962 67683862710956318989936328024545289251 -27806406326384355365854222422840692260580644133687326261741254980514158721133 0 83003908393309256341038799007395584421 84715445610494674338764148842260501999 -53473609963631164134571789889626998126296004647738624664118960093233400273126 1 361794401556898802885917574361279878116 11387145600983334436849624643319028285 -97853767302100788186976280625365651514045145484903172443840987271612524518421 0 463706292565990545336667677507393767130 223495595862146138011893871102183131531 -53545667113681551615084037715229869153114230243976427420523744603507905547576 0 179570911601491813094499312364405649517 83147465731817059043608248167165294694 -15992876104139990804712946633841357279003207106514593275657500171814228156948 0 130550567458574063290551791350560840266 260038898510224185420294064673990120433 -57891170934756576285960149062588086617155826692560868728697620669992665747218 0 354988344829305432663405179029191420939 79883983179415007189936958647305334447 -108507016381054457692788215618375336404311919861012250846327012240253285546506 0 142005969044858707066140370944447852902 258139457767245621593068393747778895359 -34054347371554773373316818139758914762484654455017095377167885054770920401297 0 375634625106018473736138025994705678825 247291419939099559153217697039287295928 -59597629896714670379572532145184643039705475644413319419129167341318603216882 0 176108689029100630868857627599050548440 68107086413453068272790430668000974162 -42315030619816620809852301724683326915644126187220499709568878515089662315379 0 144903314194145041834218440879322076708 189116136178919054900050272715586706282 -56834411481304415843522130950086922421619362050592573716679350526832322000965 0 380252389749196881158091075930843200507 236812928260057185841647870707647721060 -11867207783764866737987093105195201296327750856029932701176927416062704741517 0 244284781775889891027956723833212431534 41650495023990117453079585787684460124 -35315539890117624738055760639807858560004132841046765837168897648389233536675 0 164823624511608340311176257061845358376 155961175827524867513133721686007187611 -3344530483563804661884175505002990873580272838449859025682593843381445618749 0 339859211689612342056950279571010826088 256700023325155898427371831005639332444 -22691371900184277198978827912618527724562609013621485112788235041394516871934 0 283127754258342790431519993137979102828 200737757764073198952380023629923878910 -79166272776749813097399224670455928084825582739455623988159894875782140845837 0 104315741649374408457606816844173588554 156323031756678840620454085108998460538 -56677058757066627144778712395480371260434450240932691397322756917028586525307 0 129929659047550361053089998578592019994 66833904592763194883320468396366148861 -90687971780719617056564314366438961434540371836327216986843015134277084620801 0 335713453426586521528312996125362985221 266252023815787543502342141606387312565 -36751784448339212037213029825997446417147646985336574701632283573722517343857 0 333049532604297401020589849245996781843 179751292726401592437449330046256160602 -111065674725828913383252595260151362301104598454368531025314618798811650009211 0 187138549475004608977836188729650750148 223492885439666524887198056200454812102 -107613826009996711881626698602320589157343872850812328108281310638551732647433 0 174196840374911263355665130107700501709 216812060883092074159737292394717247874 -97520717981582986610224983010595972807045403077566498673988211011818439321030 0 112536730720610162525110474273809258217 60544940081127947114448792136348067287 -26767016443386334144068893869107110443678162458964981860625418541819643649623 0 433707108703394757683854518218065592329 253389158346706890987543715759017722925 -19745577366471177348802795218172366381313919336946160581461958308390099210157 0 127572574276687689891908216657394293260 68683048905492198910801919864009724231 -87030630902514749320579030412538277230773625548975384275447568862658504078428 0 60513015556377249753968673222436277756 57520918186392943868561216803559313232 -67403064376974762083843218995221256072647274866220739731113668300403733162095 0 250240341477039879062823463745817420236 109880742872719503024945580513140210814 -88604783189623484649937365868308441267582371786558144474682694276017297324050 0 245115663586574502316334000160610484225 64867186214115567745124734928831143066 -107587952779282975972779691364078055729609634197262528133469048378034890474945 0 305311571731845649405521306988089829138 47797138626619611328298088064547990447 -81007479598469834788738917285048235764879585010594516381183259077126512265851 0 178568901987452689221856466437671296859 290870889952503925029339827014516531524 -103942025879762871662597723363570657797302094566714381634901334031627161259626 0 333917617694251558093962249268498474413 186670201686913279417639435123177270645 -97045702472617009864845827245083516948396038564998709616505532898528886907578 0 450392927533291686012863464835127564472 156327574346536786871262371723702051238 -109085446571378964942039988870696908827672567588306321185711107431326221431404 0 273049594425224660146058694196938358141 304966597324214901944267592957457288591 -67448657189661408366311882784607010321214413657223210579844447291238069827517 0 223863768422919417364150052448185822661 305200237504588556987081893470100128125 -61393889256004662054393003338727535429045302421015987061736346023494321138359 0 139642924730896618443633222952905927132 24033198059151249236312549769566980936 -8715806587046228670100976915774116762176066239416043450356253405992727140374 0 167657474215549422489992093597878800494 128026819086591320356377808920257025688 -14060658874146412233268151448104245707350901245208108859691030831287852364423 0 173044282121100270795277278194300694120 101650221583689538683584947425285408493 -35225253115645440466241496916483435932928514791860818212619726873315449869552 0 242551618718528126834239181606507674664 238708035903968304869195046468972430202 -23233697089724457977726507086931313865507512571300354437181990411973185102586 0 153574766049011547512070084365625403679 197644661747207386576810181898765414303 -95854802733295220445401176703129257979904419786304780741487907271434003999042 0 221002538074363918844998528367097103700 217364558192770902162595356050779805386 -20236914476494018322390790508611193496705670789092850990279762648257634492978 0 139781861532401280337441786419235818713 55769131816665875911034863627494930753 -31144727217393170677109142556098410147096548725643930810750442677367778673504 0 360397055341832956780593321214377035117 229271358233860232947307451894621896646 -52848817354371605521125169394037981136943251189338590336091525627014496041866 0 299409694808639423169004770945503479275 124051199146467131804689593741332887096 -19741999897234716176027921070391126105729673520674483248917647325405678617186 0 195917543098688075931678258959917870957 148983678340481785007133011141451535075 -65186794784873365720168005611085300394188228730188268238653132245602307223050 0 312516362827147471575752182981559920600 223486706573969708309902137077296117581 -72651221410476954877641403699478237101078768481444610275242048578024433953683 0 446643755841579740093164783154756897757 190542215644287544721219888315486297593 -81452621420321559163530915654058069229553508999787466488719982111390050085688 0 91723257236904013224277156068114739464 97986541700674684470058469923563380594 -98114794561930846401585184299110122698043355389677478453731525242013566461291 0 119620097629672448507834226576223222879 98257441974619149958732884924349692241 -65092365420388180353983031354569613905635378956369503636200907939627874893585 0 195572609084996478102781590835720345218 293361034443044462521402627570900595153 -110148237012030542248977227522026312678430723828092199672004130758096241708475 0 162395984242112140417773676434814790529 279246569702567301096107735348611991252 -104933770737046776371710229929443919017256094313130555957749040457626034902652 0 351847248085991617517387859168004833714 153696619721981517166435756112501440904 -83575258934090314911410833136424360540451023551251819116340800037009918013064 0 199298866261109788468905294886024754229 141264777838271494607398318903400275981 -21982970512024105983093366420465810710211548215695715678124204877504320134202 0 90158547548423856420351417213779843896 63721308293894342579810761854730129493 -26808519663604875623045178783955672034103358206123550799135443660101079143318 0 195621826964255769778371165610053400559 289803777136831383581941096298703780296 -67239563196063681058696748279784515075571858257601106514282947421860966692404 0 161654811196005087795084300190146930915 117084264602809389981198277192855483039 -75663214383075831846998228879121847639478615434448352303046991409253284417795 0 148470932993440643902855529678985075043 98485018938818847347119031550307015125 -10621550517447982702503384602629762526995711943121643672033092918244519339685 0 365468073904445110844019672350047053875 201152634849584446496735222072693723046 -39229767314758376122402156414374562096163917674603761813062937460939307894124 0 348908297119684232234903092252388275132 239934985920735636040783494659502037354 -45495865392833497071754303494759832282081722446593528739399291470273486234552 0 312110388795101002877346899093726176169 192825679983450652236203772354949123371 -79670892905531085419913521935270631700250613651663770195728323499024530660873 0 210162370697217234421215703273070531188 306661763725046281286237434294494453369 -57056541014408286191704504965255228231251710571951332051794335629460906071087 0 229052872606918140739769355192410595038 104295116597138801438511495605935453418 -66489689895324104511841368968615553664063611682577226781108448613160958924201 0 238714773885308457296436990778600061265 125662083510384262149277702909846353581 -19349960800007918960284071220267576512063472875403953805581369560340702225823 0 257577893292649278672615754861042172977 279391650566064181342968875768574150392 -3770428481696521695629637228764834585351588681161806266945220713042533081372 0 258450694855479302866984563703827585137 90313277051068148510335860580316427090 -16655972652212658524463703360167721559918595782166223099282033915750450634747 0 81431785526151253337716301700025230736 211899811907795372513723606620533662682 -15281261424015088373447545160415443806667695200490919712558645069526538251642 0 148585836256956469096173074348490272839 113424748040469605075531348358738139495 -21183029428838089598253690175301223224817605936088691207691660255016665943268 0 367060752524512353549718413498687946998 56038973271675173053409043913844406554 -44115837842825448434549949698971153727817154009211720076302692326226875201072 0 105535877913609561555778969605702677070 300844973979534346807051177071161853634 -78626327798504642984962645891937758066838862558506382338972235645638971174856 0 246165090119111545612927156646896385235 288677534587881032681246041105690796961 -32526769424985383942363243247107228518936860340074454290958474458143472747346 0 375988943561208757689727145153563547758 10647241992119623222307833070046655469 -26596047925884207160144942573264084262121866760597878600686419125712929074955 0 391194367272001125511506972928061681173 185156507180242014924612668541966890820 -10949458318698722084252765648689769937608906280707526590838295900667580749872 0 127898397058044503652873494754606540308 46676927492687614494429007683603752090 -75184508958478729844952482971037946616826084228175436560488375335772573876751 0 175309963118677505049511762573350288585 275054624955694171155929459886735937868 -97600460072181753064839054791721345443851631118606954916263583270693056643053 0 173966795124746722274255686047893934613 303492989541788391733397826948913520251 -58115611490193754297947555010642570114117580581063748055913447216152881084357 0 258268569924055209490839969739329420300 77043583040863117548193574554572392591 -85124180393272905674915895558753296355842801342314284641824664301011394328803 0 320835982456193763627823729044206973704 270782301780965196307389272723517897257 -102595957646687955122836421386805497109907817596423189310814391456427203919651 0 265943019063605750786866684403729444001 232403226746343395823331947852924002898 -26954293366450377880871890468501758986583997109251630007288149160104473772815 0 357527395391405219209861949305162610172 69273280564708665908132122495504797431 -107673762321871862689059836281272445958378427849739910268210366204063642153125 0 381333688453210199235170123177305488914 253566769985225334229081551742023353388 -2570827043287894223445313410527861125027345783339384554262531031550039089624 0 102276432201981604739955738927783151785 194459983447765234230981257872572776019 -64453762035452472328485255457946983070304288166410353704568097425667239697112 0 75282156502701009381083553219898820748 221110973823590274568584000031069581065 -53014028355904453624047429228965378200584519197563621350051599011411329466072 0 101717764127038845316154659074359409224 74140189965437417028291885941704737827 -93649369363969857206746345484923818053818402172900245534166978995388028644990 0 379141370903111375224920202253396861619 114666578072558715000687143417006338052 -46718290806381695498803458419997739539647250783079629691308147261958398634111 0 118030142133865350956764805221650615211 85523947396671194209442422976080736768 -50102811083770537176486468930592548716563783547187309146472688586744875983992 0 355916093106751144903087503777221970719 161881081744259601269266863676470165403 -102126936896750865753682634175161279974642264535237798516064599252467295788903 0 225487820838050029442282561131157857170 121029265991836280506932224877269267235 -73336428268460229968389815320071684211629119689484427210985793274986198963017 0 163234570233663145266544410965654166106 302015411525368225865768337360671325832 -52223815472254225185035199066604959031863310541526425429840237864106277253783 0 386408598333924184119072238368372098044 146144518520017711132131583837589962675 -65717103451629307801017952964793798981525522282159046218949060849254759711352 0 75415877314585113274886280591648887691 106184462095920188580280485498688220535 -20965627569147404438871545189520920538630257067785837611959919126629266840986 0 254374814982339379953677291193842281811 197402344816223577300681183117286787855 -38614027734800104453053543208698935335451220618619530903573321856072097084841 0 231317981656504437841554829369736295978 283216907503636797036029728982374938917 -115177264500695842216266088290866194903201106452459915024795908326694645594165 0 443895921491118835030184876644808146107 282561279654298762021618786702522392582 -76432165085513422033999283852341910293539361404939993937579377734828329948682 0 351911701215885359178312570999694010794 183104601290172555418214284121554975863 -91898805803407156978511089451294491218938267573054915025623562102191747231440 0 204720505723755238155100441734779542748 64488561835186737724838310286426097626 -60167047172623434786932240761881609761323879307553009306758784701499423813753 0 191071213235230912916909526857908979571 25431402129725525388787218508992699716 -99008851708131347166474228806886444392979327756180663239597130492070063950191 0 188186868556594691893314444832966042671 91413166356694945391683782994700922343 -37162392069291919774367435762908927629673117170839583803395903518964084632829 0 305192194391793702065848450681584266886 63791378121801402865198612282033180199 -90761653875218596870771925495100503285267160789899754390060712335425617233165 0 213968446878519878383695697547837858005 44242735973518711841913680713481115497 -106508517836487849202297598649896352474024856280345186373673774458679869816886 0 110233599740521035272124801672929637979 240185726969900384630099114396711616057 -86767363576052643973574784810585838172057379484701103810520227865587077440719 0 134133927555660412462248765942698580859 225263802015785030367033025556685628254 -86288787795019852809558738385542665195774423438956224531504072877088598640519 0 359051606353910557570442014535402106620 158794399369137264814225399108720671753 -23325318434750646932583037475350318468527745122097168835363089791450133964208 0 56783801070032041852455496726417738642 216640028534859642285663550149199382669 -8882993177171056292881197151120616949156998889255643332108552754407862491613 0 64664670041320660742759718433065761842 207793673970147999331050208045211921709 -56784551382436173319670882307183698551497681646724137356540528783082507561570 1 340892521698081650483241020763078220547 4376091791147313809274764932422927033 -103655921530802330921736126527297404297312133515071015082662759096968700483219 0 395600540180587795207905049220813255226 112449101348775375228038057868833592886 -108834121572330844660460785977135461125979244380633483038746348580214572493901 0 359759298402859010036267971625689054008 316592607460051296404092364302217766197 -34367752295176310918849708037452320330865770228308018069119488172058512006522 0 170722273810370559705134521189597077691 290286600369346569711684789834921233189 -106323973657583189988676842093914127511005107664496858821451873284655474824724 0 432254175722259006518728444176490289180 298667442826272252363241382700237490124 -77047279031648853248039444323422732910534388560283245485779420058103728647060 0 87091238395411236863479952403877359332 227554549653532519207613668897293012005 -29164966927635254730462914585221686560437994051060066064782357539280963430281 0 125214793497117814694133665139140656808 18191755178671066657296974570787587159 -100871325706559405808750766798584109699311653300053048286892990766993526112676 0 261402323670616826621516795517292195510 291457235237668242384611700369159805191 -25341764543975020045484922060278498627443818472196863697872592264745968486088 0 304697595354882817395232053588095237352 122382318555347061704443376325281787439 -93122348016998855258683148577513390276710973590756380055098877001403425060853 0 395482620264009489906707850350242571217 14479282257231624711583363325003034963 -33133343550129014814380530406447981458815179807125451181297453997511954438529 0 276134812657542808927548602024536555897 80668155120192242413395190965648358882 -89625209938935029032397592704302183090443412036595538596161717613487454056674 0 213301313158948342376343914240823556871 147207374826196912135640063700880365096 -56152072043954799410212582661348949272421816777123574354875604116161057930596 0 428296874216973005737375623891111270670 105366445481902223570020786193741365868 -49508546006256596608782086604979245508055904190917842657563205907600118564063 0 355159816943398752269668389288569968736 22202916433113948895343068026368153715 -32924962443801255988005807801243686938787054750257294376605603357433413352780 1 222703777814167956871248237281047413838 7903875573541315669235593039689134286 -74864036252412673320720691104472709483695220925662955333251554199480540358196 0 62520755952116417853291233891379303544 112348845691504252809857891108656187385 -44555000793169076652023294937256241010064572173303379968906513874141919265006 0 356657091052998327674557614397399272991 213583091115644069877218576366600930420 -55020272950497323892153468326136909818697821493271141559567768392770706164958 0 152603318714789091245903245929310847115 160638614513486283924666618038421023809 -42801148554637377306432994885371676807714756396721687353467891354871628720825 0 87036494798591887097533900866188318294 169085512516472840448734275080054840165 -15961640531482230402985184996205104621106705938515189707268469572950484347412 0 121270084695350616928284378685429962774 27866526319634631496867636651717974721 -40323387382237525548667872586840553323332771592782639446555505901645954607131 0 229621504432506313171947905141174535711 136341963267689811074957222770565109172 -91636426773415205178703712836493118069562775834604946065775455441516945028105 0 192779283444198354917307811729537263748 102609021898887121286837104787311569022 -23952253728092363526302449601461467480728906066765520931029631158343979390352 0 184497025751719664620493359678973489127 209740247184245572683525359691574647321 -84385833182369313991650137685328182071794024611335168152611495792032273756803 0 438554849212697966269652187808382992945 158363992383668149756742685012061768779 -49930895804218851133192981583975288308721482851596279898309465976790873544608 0 157429214579696008859192128376654241135 97471858925503070238403324249368863241 -5014451648835867989289243887980638089111519057834078477725381874192412262422 1 262052636132062185233877894590668206675 38619602836076605869022943235214163617 -81644396119528266082585643299841965032272010962589398227685592362758977096371 0 264710246796889215486805782733470318298 39562067146174296591721641734749883439 -6844240981972212373633473706945454773183091067491888888056063303790113524839 0 76503923908868235319175219431276533887 73324868930953322340927627903152810488 -113483801185522361607572341114147933826060120434115562418652334719852055239493 0 239106492814113346987033672767307139418 225627640566715477031144920233950201785 -81405335245930405058358852324145855725688040981240440792362736820823311152129 0 214688725142424259957718391133547490546 56809735000289657154045179864107608774 -20642490804878140308818971512760451133484990257518513677882457897908816961868 0 185268268201761755328927498713347423838 125414360766042813299936944572595608368 -44557163535792683564777736441487996038561674684882365578665875863856112197606 0 205630139898143916098464630675899389908 193750920604675830202484002901683363858 -115278353912757645328890155606912213333094722032668590266128760539333292930992 0 338594576331151144193843032648316358479 117953680074683378381622619747598508476 -51209499219244761382775822933065726837670000230677476802997008495655477297142 0 404386680014662011607714121166934267573 165498309141408806192944486421978695442 -9567965049196464596612584779631578047961932694866862058976575676468200416077 1 208113747360217067421446161214919357638 21811166641162858620890089362538470107 -47501488374181683826554902914693414244472273686766627851069882645385510906844 0 295394859582268287365680388517587135899 259589452637284502528659877868040399068 -115727772950750806331497074997046625963230863558536653302128035507477206595536 0 348396452466659528450001469692290083082 127789879253433728194222428682848791639 -51027368772174211512668718809044413396271280912726563615788715161859782410533 0 354362376759939442621588711100516483783 21611253834236862554952408811660549829 -25105878725042329609299329436350834616134417648174490684236182258786537539157 0 246152323280759151857278893775495381493 260641107100361248041892764272352672339 -85898853059450862092636163577155349614430804084119292648302715943400786388725 0 176618971440936648812018857872907270123 308223900231312961297370100928299023959 -84122485180295307795134547401946757664268665105795214024273206749624045166034 0 114356497472911625633020500171282625959 302716711127860419385471309616837717265 -7879689483444819349552076588079959739472312490953665956519160835120759994962 0 145807179719617056975662532883908114925 227007828503585525126886698075379896997 -36453550928374327683383610762479851341313174506781760870336389199846369070098 0 346131018282738152232585165432981969571 255644333849677733070070189532639492552 -71727706037840101844190931945305912544213288619315870793450913552282715150691 0 362200932311303941753536381684595511282 147667317453444191459809979785902320176 -74380323058428798898714957384418986757484788616195293653049391036025593375874 0 252371476905161635656338914080144374530 18863853000792395540130186099909732298 -40656260367348285111541826776373606575352690215253121543778508979033811577626 1 339116163891667166196911610029734524065 13445782746241833186287597150652059912 -41038169348874997562729726189699304129159030993705315910774415088768050603331 0 166286850737512129403317485621076511732 231551055015109340103130071440275990598 -35453996121288003579067361729766867564550248854127344511569296604013437115872 0 352880810979135225420919651607085956522 141817075546195874862001194961469844917 -59307692411778000109438805346380281933067080712752805391606417558806747406113 0 87916431017539545835327619216401375467 133157274287499120763575215430253316815 -1719874047982963587912996899426193506200401116547797632908281925036770571356 0 306352223637219349491203878091814820824 163699730971862873996463387321198192908 -113769319475258097198604794372521838545464847209028555938513424334462922110743 0 269279825668491238190491421250392418253 291527923921481633291715142259202709288 -99690494789517180557157894527007716859821978357972001969795079848661464095055 0 173394825216935602171649853463992503767 324056741816239857416928405738420879162 -100262570929243863575743455429664707952932948180684175364562392810842002330899 0 285462592154447153221855078620235403610 191154038773441489260626243490611264857 -55338905403404148425566884294180213074467327966796631243795135319846771288138 0 245376693844791641689743743095263972420 75382408104192905969582133490626841900 -44629600213957694410524664832214523966479800022656941929191730151627884796366 1 395930816788569562069791172641177847136 9005568385118306713552632284282013672 -31738448221138075212519824575971956162724305551546609480359617523492446601297 0 224931024019221378968219344163942649449 264110601641078970191376310859096054530 -107791940995456777797436364445372941158188985812524136036192316550195787843859 0 128027600738870863405909810559507074993 215995082102652841307609503583879134660 -26140936920699054369877009445850291897186807778418314382589865064374177448361 0 164988420752281807926568311360879703983 105966961875736976805722785866836769547 -75531295543497868067114205027559358983254141035410487295582035979223457768146 0 148400442982066307608721072701345681970 182736759942413011253540093923476380514 -97234034660732674985842629144468950395861662666180223813440437680562073216369 0 289494070196529110790074916599162968691 18907718973690620412654339448460739009 -27621475956222638170230750207543476233608733925167330051543407426007294887365 0 355568908773213958207898506771055577267 246151396518516832735924166642474565065 -30749286524315100331705045403623098841231039186663782347623818181538081994408 0 385833098554812891799774225573730654999 203811281870602054635814011641165762592 -22599504861630977042137304681022012220432367005990022013727718677053680390501 0 298536991807845529843125530923503901290 5002705537714231965370242063293815888 -9644285581499559007385492647807405905468401136129174872424139725648550388011 0 41379260300332768477450440632143240381 149757849909351817002723983326890813701 -79784575380065916796336674651106662409589255580410606860192042657781070215099 0 210805563554233631363020258390196398259 153479311438203322145231619886782242851 -109834105808285333715772579996445952200730791225540277994728689375436156492534 0 191412505890247669890582981140222131595 331424981110373640814488894871060943906 -39182629844391906537221329941437074232446514168357190320810438837086934879478 0 78943366716835314900927887985748435540 193241100487752545170890153073012601895 -63547704406928919599057086922039280316244978103291718398382539715504446446450 0 321449013088412991188080860867780677294 149893652848837665217938050660592856648 -78722467367080824485171519214462740340898666017713858637432722788620947272853 0 95563703734265791844331278053162962618 253588856652056338450182347761163437346 -5322707845739853746910672768681011093247176616835822531189233937314438577272 0 159458635720413664903281651718642563281 252327711982909790327247463560794995044 -60126673911492257457036851265242771755753107147934000134861715177507091926741 0 378821743046106382087442320447779464283 272776134557454564113774590583178442907 -93154976017644256420088969384064641549293701302930781273250361171766697631038 0 231826226580083142648600762593944346657 210814685911251089404442213916216036237 -21356618620851939784401439525854921513524128522520019276288253589846720851515 0 270940194558707207914461038002537041978 262036204453116316580271530149041758779 -56297686180616227993826723603571740356864215706803296653440220188328411271784 0 148220342246216938378041721101709342204 234442906150326303124104279840848841262 -94935685469764623325290044211964105634025713779154922689132815992661571807053 0 378607314884068198625138632506759275579 155252455567985522155313310075456397502 -91329203251471192055457792733492809881915503375411845889129888172348167227513 0 284017399406523130621420721063793371029 250998388573010673106489204041281541354 -11207496406972806856442570559820555577081336352595596876918775178773023241278 0 230156497339319594265686723285622912471 117157470483899769696195985348001517010 -83175870895796706191199265326171740461931881505745954057428782929889292461693 0 160342581538550327444404105299752887002 150738001195954517788737609842260671965 -97476656096907894268079393919386499124008904887457205563331674803117628008045 0 62005759719808570184941917642926058421 67972248227269500064589990157800960981 -88011175900007877102283744659116300610050194686122126786244397492058495618123 0 83723701382753793273581334068913519993 143535995058731834354949181265824439074 -68511120902451265046606661652110236756970487570540924463551735261329291456731 0 80624787906411209560963362195523331013 119802525893839176918367390638310023071 -49338809410014019930709919528457362587351916979551042455795308692778761852955 0 355786534348377680722335799794846261900 165943692288053164755664734668051032268 -13860762726997297462079775497008723659556298082947106804634672762466342846330 0 206349661057361467509823013107472496122 69663284159562761334353823855474606638 -89072153540891670886763249665404124707564881025455289892332019096682886167121 0 350624455123260732961213556521903929836 45506953939106465369002516184214158495 -96365215392718189993065796130961112928741561723584501939857728104126899493612 0 390558648756689458750852900188605175710 48053864815320171133249562404021221559 -3378163908284305214516617456703914296085675966725297690525995225669719823524 0 244144949134294306946605804084137004196 120672310931645743872686604544069075503 -15119847777337401487863409094394855147816797483491774698861340029070934091065 0 101486503158745920342464304111330743035 166597344509127962426536660503527268667 -28750229545304109256719450091973100039174852155583148105283310740793933847601 0 313964879999050690898365084770755635409 17388501199629747212265902492448654177 -54316386516879954955143602983713047494423057372833903183801650661915217751937 0 390684650378197476623740337039959457592 130515502026059635809494301258817572688 -113325163019662746430357569533362431611217578674838380118214542409188394686561 0 337004403245714389765573285229339776302 268920747518322513250045769665563023706 -109790267739285060198456758238161152260014442777436413392751338094520263382458 0 410420539000701774986927520962145803397 161902591648095608810407307928784491507 -17323700323856648100589269944005779297004596813964871206126621947343226677420 0 343999917849124256268242615333285753499 104626795763962583704062133367264067812 -99625161184518603425286848992428498031935487157670087511976504999264748258562 0 447091715465281946404919143057625805819 147233496201419630907143412696672827663 -19767217821257916435819256389938276894668020029365959975851301773808477639765 0 276317197176808790107904813523192826176 20293314203553827021349219813031543851 -33288306255189154748355610827198716697714256681539990095654965427210336360825 0 411353885215647178267308664689140456599 72694595118213582336700026903886603764 -4825307244185329682300154304518342726890201503647695419883006017192047396947 0 192854390745491167218179334467760723607 52876907112410305059670131107289062239 -15059135847794636108922587178069497236588072875426787466528215741854135422441 0 161550459447706461009767962082843869415 272698956712257632810309242569797145150 -100350119253238111848930011718842214268516453707364771735380826624609149631957 0 332001726528692831346778571051011095870 28459486378077711489526613312482001504 -93683729367923247529548543563463066947202052949428732819227573428218706611437 0 434940704775381732088982690380125925074 145316724620785135674364360509352460873 -31988955533830065578118048126775667446371586144237182188930190337540406824726 0 350904939293610421403142212140726348128 168988299059209013541319173412713572258 -111658720667881941422499943986545252060149624690661325821653730829615245939090 0 356916906603731329222240958899012598545 108086257776845416328675455639393636876 -6664534735248848901748678068202083816469285425155608687193581443250704204943 0 226202511278956774693771325571788149947 269592790787167163773094391501083088913 -34482295467967172119970148457167396550362083701617023831786130535869442135246 0 181700281651850872088693804117697098919 305113108614595234746277629365607280453 -109731748438053456086073341352408336449756468969385189500811679496747519805057 0 189363180594420756250068520400009823968 47116030835988404095365472217296637686 -4805725073773920565627223189323061730397028701795149845048194394593234208347 0 202693990977694803725425587569960305014 142478206985210105362933128293363320894 -50854711555938338298642042679831145526543224019417985864613750748875277544383 0 317057991373791913817475638670005867425 144890762501261358154803816194148314134 -93402360775194005883449571282624973317668751546651753916285541360430077597558 0 260658386832050362494958271723003073829 291790575041754075343143464195468360842 -110915012940916190892896711713533780701650763638677102900269197299393143643511 0 463953670574065640574098254215769615676 216655554621050423229048504197979816791 -4588802006528376570410714416265629726873705112124517613552812238249349917639 0 85776510463033730978874275350059827430 144982292893618097586200903961488963396 -13342135581600564673308182521064586516727663579595706251320257789163263744720 0 191435384993643380482556708815999462151 128837894638733372034484928982745524114 -31664375973200218301884442627588554345427575526248869372888349361216745266568 0 337087523888571369345728919567182556934 274102765506885030244891309543662293216 -27517330187922365387954535169979960044463196732896186343057675700262552705995 0 294090152048656955672670760880349630566 245481408538868931281631706027742971432 -69195559085311141220586099119437836855345732205889535051344895372156143493679 0 384150996535821894437411180854422956547 38607357231448963462539911393931415973 -64661885612355790683473477264995832759693384773428626750635194213308169397167 0 387961337912653145874332214941163300178 227476114132226191947425472596035666964 -102087512765186308264509014151299537991994691241460508792922865824877809361464 0 177612010980338159397100068519971031551 130473442200268919269235434079422209757 -105316782509275296438880624286793317431421576660362869484979609413421081438042 0 488639085875349333087228008277377291136 296580076488125169467648395512258117707 -62347575040618224022379683412076325322138665645525801051886659477295914433173 0 182692386081009394243753951868527224098 23502803107977267828033679909886854250 -1355729387922619962827298288249526855297198348130536407553863282143079365037 0 31321773689191716596588419573354170657 55805413947938339287833275560672587305 -2493572742746721530961960666930840089337285219095701611866416589167364098942 0 124536367567514105110282119677007611697 293173975574284837907022806893758512269 -109764204455491437309502086855300465228232484889061045920888512591793278944085 0 368073835731265170424757637853992497660 78744788960418752756719748463738600487 -61704336118289686460383183588071469565979262957045526567212398059695465704237 0 420893934630725537310841252325648882099 209904629379106200715410506344244865339 -100017244055424184521738206191431880848333393586174899207100863084561572240871 0 444616051391645874926332488154855265884 201914970284451290138040501434735278871 -75946829954892236434479367855297795036432394139418080946975129603309886373579 0 74841895220960337488755563937704136231 46317010462904574608707166858795555106 -459749964063109813516518306269083580585166401183748043820875282452005399017 0 99272165093983248737654230512511036238 127075595885065953166216194028356009200 -16336072435757528007930469153051357462914215695232596295640295555811956938105 1 277521070635226386662549321063152236532 5829276597247552805840654353501736861 -2991947677479483464221121216361073034264714510000931451738437439578460244318 1 131520562277157366268375959544046811388 12882091562952216315914896664208877557 -28264618999578134491045235062229091611675509505111014353383828913234459339132 0 271806192731236850299252879119617843681 15247504410696281993971358150571052521 -47738075636155378121368665709361816292316607056347699937288801681294171120567 0 176875775603542367561667239496533797451 203936350932614007196253420759753703924 -92093948214472521539248604224823775884163645166633430401890374319439806330521 0 370669153989950674079082243082348875733 277846901946468410635204147878066734300 -70996688707750892957975721551826448178256748357240191619265927898410903919772 0 433041366945333195947629476181233308654 160204506815234492754619410381807169405 -9918910283306704172668561140979813208666979393627720825459067360468433745403 0 90945818380155875689704339709110590984 224935911367335061840719017295225175663 -30527884009727748136052537048161024981352797748179418500303530280326317592119 0 234487769736867575931321267186246678016 26768352602970656473669062291868867213 -44580485104296149069292626388335049997316521092452398561214291432896731385977 0 375446209586781356986399493054004062224 120232365026272644674002298111796325777 -66953060921252975506916941738884981737749497791184370376583965839700643957861 0 255776916181114386515859118967458075105 129126010268513536882955392328404407061 -79615845873844340481449061692143090207527973886053041009384112340499585480929 0 266637865615987209261698178369963849530 264803800022276097659568427782226681795 -96753084884998997037627618701481927961030660720146862744051811197456786485024 0 375420482591755821325330836679224199602 251290230891810449606280513648626819986 -82109497444191113522349688323935053761395758004739728998015759227144487247185 0 285630914794354171983638649373487347628 175265594616096233272280073956344836051 -41360175903619953768296966660239255998122629620981954855809251811741903379107 0 295886550111595569133074880373530082294 138977398502656435077344069575322617810 -104893005334848300475488141609086607733911671881136035192488032759425458748049 0 402818797684029998830063718929199253805 142144716170574726595229225317802121716 -82204896591079174050484222551881529273634052820263479200460617596729942076226 0 150143293872973824605668855682471833131 269344926453083409420126213255347357292 -112319432158757354985947023444809975333941585130344203855362578054435141905797 0 302772041523865504650085912503177218926 305631340373347262972098178447343574195 -76357025804461380479485840026517121982310871834962706900593145346525174604677 0 300994088768453379299583100927759309277 175729205585396435499147194695960922836 -92701368725603321870477550715675103141096602865268133694318628547269593571787 0 127455090903125178886115303418818946348 158600200429914580130875694024466749933 -30366570691769796982876120321511691088583346786487366789681683572885047322416 0 97864486336195757261374795222430117242 157548945296808283403082235784697656218 -82827535489871339105390846239741419695472886767162961465214201098192979324676 0 135515964095688238117782863237758876159 77986666142713762986485825217720139225 -88201568407787582468633282284290894891262928424364369572410269670502154524059 0 231643220301371175810318981984598045462 101038547484748728329627647279276397219 -89890231174678100470861032021512297371068994737038622501343728899111503205368 0 163657499519499529738087234643643053027 334867309182802483821659479821267369588 -99143940399542535485926882175711688290467131655787106287176333090132199798361 0 323595042245657184386559393572558161278 90896228615594768695709623256958861679 -68652457726470810797054041683538825810459893183861348389112229455272085403220 0 385276219601046184931060986054979943732 176534606246756265460726718977031208396 -113019181577238971206061976769723137889011667503179029986089064097478224818439 0 228061753272126180169954071876821609661 151068210531336838906791636084900495746 -99961552203080381421963872924035714904078318350676344127263639343532606232484 0 127490123986424026229510520151256605526 98069383460288897058819973462695432334 -44096864616939528108157377585899304128258791801829746866432815031336295899825 0 389150996375995637699913045875344505675 80833917397102770121007991410012885873 -35664583367629513423634398043596461918365363763258554696841607525412220379271 0 414026484209468716852064338937873863718 118142288470327059235520095225234420422 -92096324096205374859858623233994377375534109550306030792609114836085665144436 0 290163208464608869810796024523470431458 111104603095616085969435688138740006025 -107606922167558841029985625987774137156563930178830677833604573522055564981132 0 191464891356049351264615676229021211869 124427885465991183283616415778258546452 -105510904845819680158503837767413750629999954387819132257431443625212021028824 0 86786642102173649819544809871554759390 117790109367021635805526187648161165947 -5398585960352522728378574070927682328415753575205389566230423346770420703314 0 39637059636871892371815774737711355703 128924533649149842350999040593173907385 -66041797859056160994552888396449998923447150626888623558165696328564772702111 0 128471908157115025282489468281448230420 168895753020183874952825659370860005632 -29030681398382064687053997403555639359499940938501513104867132703719525297836 0 126516290521697857497924558679626426461 241834344266193259666812223360066486000 -101210265692440511175776893682878407182849248083537725762992485088244276243393 0 317947873438645236264500221984442830631 314243165271306717394627999330142977717 -60777453606065916585172193602644551752747670096699784840249049283380207362280 0 205786569858492786628725953610938188330 122665030839531186031602677379014864365 -7831561338643043897186129384679068770905311821359874272993960143299919848023 0 283628191597583851257389282037740758903 1388168388747006517890713445012168457 -99460161590693020327165745545640702054494382319550225936764570100407625900159 0 371557484498267512156621553111362482878 276627812343284976102910361525028122188 -95294149675948540131232277121591987735687837930211194306745729363392655291724 0 403632299294837968549679470779455563201 84663610069996136592876718232264299749 -54591824719805369762006767475661998377959391653248896414170843491332900784436 0 204243986230697768354598323159362622424 264045149207039798457444793792838782101 -58669266342349309623227276291085718940622836695668402850132211636716249505058 0 356227619239859734199279135905703448294 176114844039539097880245207281439323879 -45791520394318792265185973845473005203308474345186045575148760580387205816712 0 290613758286262058390341082124563425114 190070708086372775234165242152115415793 -6140152154482115974992132192169048256917183784811897187890196297654820303342 0 45032738987432359348721776751137449108 164843215373171149273393542995934292383 -78698045666200483654952978966866005287864603318633340468291781008294141138777 0 324127962403987158227569958242559200045 86496278591532442136596806795767705693 -12056194848471113762007341171230198683501851805387562543935496285874604285929 0 143672048950984076728047680991634573991 256449886264700068979593436531109754571 -56381737799364298751700887057521089740137936353711930816208243632048627251281 0 269398453158762442505165475487560658463 173325066198857405501808985634532463021 -103130475613504946652182329656645774924873591731544239085947045024897491327553 0 380156535565480773497885168783453669647 228570364072038864497115213009521912571 -27902085695334907428799703674633951142730656473424703073346197154332439349448 0 344704217856955730234422138044604020732 145700488479355450125255141168283845247 -16135636924573011751643704097424547692178875191440927020623722441189944018061 1 196942905380271763837716958871912287371 19029788967191373672926579338201910014 -66075322469376679768901578718596836239362466403415490155841260805467114101692 0 161594168895270243506521943286796455562 158078615882870342408874445435584259841 -57022047053781076563463155651519251603508009652669524144016493530877281078085 0 309001557801096534081419607221947811036 45344393972446008897404514545464722812 -50796168741464189605666384066380929776192092045233464308685330881437032244498 0 68722178303617733321838947738083101455 95835088132611489795123421384040938586 -17598527837484307856817548238134448914802708685356112143155071602225858508692 0 304478213612589432229736130168319993836 31302229470981611824629273251179524619 -86718557405134110246729448706577056471395077434656749472678377341587128639207 0 200826123578921082846686780106939510859 195792670257685579693444070336103325572 -43743332333040801164138604378355929925867438656913962175103999863584985883706 0 147757333823301115442934983885930185248 13819620241655022262490162523171409494 -68194228258318122992280377369686145609651246243399673223493090392101631090793 0 330712859378733748653517369519679675547 121402925029605332401434700324261219268 -71505516810374947083709397449720862323528995471220226846675545235935223968340 0 259831184801153346577924113037258583678 229485533872527939451310348548808222760 -101303958796886260834292279866151019800540009767677010337504991892944692825254 0 377340770077239404480490688865139140578 180315115923450904700123316721695225589 -91925311182152028946939545443869971479886807452617039542385108028429077560793 0 101225331091215524224331639781996120647 42731704605444039215989232893236156332 -18943811171533268379045499024248548508389892902824309457007990736616158884520 0 242280948424852596525942793832865938922 226277285172429567847255153266398394457 -55178196116111238630237200094942869266838050572740095198495498594808345525443 0 363489291303669805186073615060486052361 93192668558919463564288080874822620030 -50237499124723031362474304798219495567735510441240632258782740761732058848557 0 362342996548046694871672989702763650550 106891417784922056656175713377569886491 -99397494671491197784810661569750657908848924761527724871300830437497952614984 0 198474248901893667543176891550628412047 66236381645845317989184659482643376295 -72691786566856705892171680324993397517494414172734655964670394431740422232357 0 246301740756889526731883383037933918941 125813603802680153644864691722404928564 -86378076272727588836014001002970290871766286594647378985965065764533670071062 0 365937739812248148536883556388440946370 100223372851123112042623412855533989152 -59728386893814294562210965373310665555061290634021845590678216705226305778232 0 169307513630088670606173987225219053557 116504761778312784178095212098058284502 -9325120897557892202114844264231528322413916576997678857913183862408522250075 0 282089943998229341515870368487332319419 251943640539880048996859085801801098829 -47249104223605214367942618421904154782425193375091701925157173216308684489672 0 129315836039178656492356705848932264652 78497932815695639146584151284843424805 -97108794853350977799623456264139342808167055538825748243341377953580083597049 0 333150787617359717266785333190277356286 110792954702785442909118164019539701831 -44196848447624709884455550565170945784526107780691932277445576710293179397370 0 265468586735833741128063257808142923551 12187210571525825110700130092089938090 -55613875049520117795169013029269928220625934959452153825034379234678672393381 0 449928736368741019094477763393271051663 221461831357293183794304597552124918881 -86283337995711931825280412486268661283095429909230262696190201394596239330421 0 248003976917075220914202962968692564955 25488320581740967697339587943741218135 -78206229071551277989656436006516043758727808973623259310388412170619057999958 0 356761354171995671176421056965368365982 22437153988102747964727476587415865796 -3537534252135498895878659681106539374719330478573289764671206613223143901927 1 338303374454461998860732834670326281345 13127119464645935655934373024150151792 -38594155566061472101611440766611361908089583663045352992496381163410770257322 0 166974231465525019216689647666848265176 222614564178057168999940966689179222073 -13555036123393484750912589388924856161721815349756512548767394415532553732924 0 235368875680428756603496662519485436862 252509703218402651967764646501178215992 -14799914772537120584773130202948749902432607140632260415080806155223089474334 0 167889582051601968412785477800549591034 280257847109832268567178528844527703704 -14009458104930518771645693700324602025728674342840342399445998539398790535689 0 102955643769091953077251206466241634783 73610128182624115435584077103776211692 -48881104197357167625505311491301026815514808423406720694770620803854394988866 0 80880417112116112298446770545201707033 61614430678446780586211875817108539161 -93896626323855541683519550061275472757994874341301799902539430365457366511920 0 84348626531567851462863476211843772702 106518183048480891058160478061151494453 -25008442207128059649033685897582377985987786735992595044703675681441369388795 0 384953149772527368018103355726470953396 160509468033657678825397061944799942052 -4147577668406408103664523230004261974800983399250650719325864856645546982751 0 300477052071190186617946337192346861241 144531158690314730215929351524035431538 -86149689369700488121484094581877256229047142467062227866470139798644728176788 0 476538687921444640611325636796374521674 277478240826400828017643243159666685852 -9517911495207339657723060865229113044563052320883592837746574409123633741220 0 181465702062042860027945132503369633999 25200587029858759061685657948964702791 -39747766644731866787679521372686786927996487787392826880938033511650172198178 0 239645781930494284412145055547285378798 170081568353992213748313060783621827612 -102362842011167433283302118613928647450524177451764718395526847828298109448727 0 109566711224115823608840901607723560052 75597236948626764875042024583618393371 -59500306682240907701437681774982748330305767033736051318196148861216028607296 0 223057623308850897607694537729948232328 150176426874848330365790693907638489298 -100201560157205314025965374556898625046107360501445138700992532223824362602417 0 341265024006865736854330927637475099224 216975673540100455221069431363730436366 -65681774504318094564498618894178951908119458930736000244035621776315567522899 0 264034867392452998852672088695467064590 282180963354092784653758255225591498956 -87309179064271470719481658696257458107737546027786534346287858315068632295596 0 124981066617937058114407597864989798879 54581154051886176277791516615972366682 -1776176530594653553958127019133845098893499561924516369157197219761485537660 0 222658518891480379036125088818554114854 101679553310940070843798260097235187272 -54337798901368702675533089056595311264880959913324600742919065926446000676649 0 437983988910502852823180767874974720693 189144644807700326684069217964613425608 -24424698445192353050453896884231068532038804036261513965899781138111481157479 0 229181298092006890914268287049541979359 54638288020485402270989253469415069875 -114243347268273075089111137800690143413175978587577975372650760470482778409669 0 278110660080029406964667363074242330697 335278116185561974345465602797469703861 -88224738381078036163164096668100908003065812471157287915819867887216256415156 0 225188005202274725303309085389790285872 186487200902004628453625593385852094846 -16817059675278602477179883426533386716301738657186326761231116111494911448012 0 109839833575239125767317699927415498042 191515990761044450344744804925231820300 -71909929297780137116033813992175322012322475236028263704088572426629474751990 0 81480910899150858692217390117877971270 190289157817162653835687928039743343944 -26328193708332695145889944807805565150336387984962399880526719726023256697270 0 134339020431120841043467718725086743333 207133762335250094713450812767300484673 -25408409477591338272814844681257731301730204705112569578131055946784976660113 0 407122038075289451069731001231560106577 79060280821517982645107103173539158963 -55410243529636787176596225731012145135430534562663858754864297210695236448567 0 322123338735550160426757950457948793446 118912082328126001764300013944482331434 -91380965174416027837313181380227598667574357782567786019877653116310687258464 0 168127434350998627092975407829376852386 310932428079562834251233970200649147497 -64166964642273259635080339843015402477608670685604653466583285374808271093494 0 141398202619975485550768547415629649784 97570137941674333737482597698392757547 -76966141101145567072385927755768005845352148690856228722511610510751086576716 0 287309039071847939347226932310853473845 168958298682521800538527799428571169549 -98568265914508174999896304689082280768446068917342819793180464640851297171318 0 232580124543424579314409361039265584199 49722764222740520973071546985504933510 -23502511001983048474552700672136275471146442915161289907100236972502787516562 0 271733257680388500687786776532451074649 94536122366079014798489980963915579484 -111447300979657585620871577629521832701610203701537330818530547614570995260806 0 366017970285388591698324114895186952800 92544681693971767903018090439662941939 -76367464158097450429574283526189920968593363679286267310051712125362302182458 0 183670025726852430524719767578700118249 58430109390448344507882656092560309783 -109886362476478094266373594262038718276650561235618375703229937596791227370184 0 137881605731540703095739213813674863686 128193184291917143803389963463084027312 -85444079062576012123051512421298901146368902476961816269477450430266868662599 0 214401201600361643990936863282361794348 279375456261393668107818697946400492354 -112187036989065392147457888205837777417432009501839290012710327376503032380396 0 150834861757846852759991693385890459793 215613210786935090026606284026882991040 -4297395442790342502480130821306111769674190192384677975087808666751924294160 1 66313158483275745117238639169152209990 2712623184680199258551547258422651922 -50107745145777419460794591038065001148464039071099655473969798681364663610765 0 246156234891746857231394380206614696173 15766308816101177581049279394052665498 -110584438202876442134728408013858883535000313716352291814451550495753728033897 0 194734681853947969663733708893427278200 235914068910493873208405205260751665001 -79774053708652854025708748763907215928421410593396074241536292569545435450476 0 128863821184260900697296774817475863371 115479618589216025725728378454952441742 -23508031315566212518864128403908274634864810799989283661833358600592302375961 1 132325220348619946650986428470260852347 3854417264559997869510694754445974765 -20823971995427935063826552140114232037888091073678105814897114037705119523202 0 127394155376441186311437997988873891922 103046409682402113536889651232093632229 -73182944641903295303155905644995826213734337306847047801733317245610613524715 0 403014356495481840184685422811118356811 224919660630779795191585039327871104926 -72045221287328638352240897253368757919326416648008649020315229561126953606170 0 337899591771009162135679905427628044594 278435260031440196024688908975489761558 -812332291316628048340952340400473022992521483473041394576757608106798598072 1 331531565091496889060523919044259900885 43228695951802270352996130113607463311 -19567730435044825448670751060602160804923896981345115101301856046871268806574 0 141906502330830374861460371585176688845 132923110974258078701264632790431079697 -101080006342886361346737325723055238440572260627196247662507490302189786617131 0 132539407129934581417321025757348032589 216959487401066407800336538835180351901 -24123865599802110467042655409550189618843658248996112001914845202417775524348 0 189865666861129551494374349059249162123 83162131453821237050459323144066115810 -71827941954769159652210308976050841072751631227039910009942637085668370882339 0 220389419717923928057622027181560814070 206832997831245332693187053571705646319 -28770685224694581260963039727849646093886116433664077772150238819344893220211 0 98615983618257427345383377527801635312 116490173749735042464543222059531166925 -30207401842194314203445309211398819694200140497569772910711610649030495534638 0 100251959849458698931939809999801516840 114222438470115288217303911470715595027 -69248191784814809788866677436667944751581739584749237492917154802215662473345 0 169321607360098448410370863697165777028 74381968054122326736171878094268386610 -67325655771150374597516422524527502619270872536959707681109162670085113843114 0 57364970871936065724782719231183041969 123382656362353646559422771427618863995 -49548039690222482473180240474416500958858218904408478288911095290955453483717 0 421812494583855167959598606037923500654 199928128743829636596493164259867392533 -65858783301168160253899007651302867559889949037996529761868327252994962399450 0 101500924298805373613068303284569907607 81418848037533937744608233362630317835 -36013442553122965536070217742698873074324702081057769797160661612950489365299 0 384292932919382244413070846448887114444 244732844194896384338159212135987764332 -21150635357872194831609814645564949375597466787295589928883300915834143724387 0 146778414851604234798487799289601640152 152036572567712401714140406419080532474 -50694726682313018116750449534285218763439495179216367302599589226884040543012 0 103212808463689816999762432919899243490 19349761097269183348932402215443525911 -3885502946740578594146851634928202121691482441068105233880496109282768526724 0 278891364690162055915844752818083343983 69502249506180413098403210068492791652 -66839094055757810884617354634651045613648704755658550945269724750447651717392 0 217197338299891170197719394303939567005 168519445080408348336437930003471726420 -70324618174496674883486544204162121922292782343118780082412865308770395521569 0 392874224827544512634107941106913624159 209291763848185010497843091333736197246 -7682667475215288328135519199861733003023336944236567754210403407260343290448 0 167070412432683110502165231066760969871 290231998536255587679273978731821299397 -59570320365661280552623915315153332726697124036042554399743053319696267720670 0 340326302213525173232353939968296375260 76019911137205949615781499183448485936 -28553968454404261510457401253392467350803319486569476808331501838005386037137 0 252464753317732720443895126836915521838 66370851270462261518441180355656216768 -80849619059445685825156692253322680143369763137123456925694957986577007968135 0 314081460590161579105665992153925663364 80738461991961132803197747402065181790 -90980158894075014113713456366920450776994648894808696574685437143543587137964 0 289118344657137430025336637491307586992 205950018601780352097696871152964674117 -14090391523715668949892048672255815746608656739854809332947242429053269196222 0 155408419193065367540214760802284805509 281708199235176651895437353982366779090 -18310529492412753761493068890642875711390046571029534937312325290576218321776 0 377040710385925897694056777776585125064 211029757801728780943807376298605535931 -94621245962774778216112400569396944255616712906511712028168149228862291454935 0 403775467043132924657769302109994641092 47379298771035040598601847274791343678 -23461157919588375821594566632580563601067398909149047120299230704437410950758 0 104832909359430315038188475177652230467 161186546628694091798450293080404569918 -27639333372781366387449421149642391641843911239185371157945368224339376212394 0 386717594309322920546756136596675346706 209383848702493283829120188508839990304 -88318527389036219560015512236930484119862805721953617977158723060273805520885 0 424721550799428185208235295001567692938 40812002072394156647825080334717686968 -102674520119721936614177238794461376842631939627315701334170541368366569778638 0 469900639208936817647851466746216554210 285056330443631620221595654745660742321 -58250328356461824615410744207340887353249213194441100488664508850778166606026 0 254645317026876918910561943311780969159 42796882837266473430649511398978468895 -86471203138744821122393895485731179098242854384647379292131403460503327248545 0 391127254517068983551496891376033599366 96002290414248464193523405440430066383 -112638179992518841626506191670073459854843307489207890679077229959841902442681 0 96172138002126714484828768554138604461 92812797359549556867153387010126382874 -21939325582071430014625527251713394098345950378060920051914107027662807371717 0 366210047293069963394107128921062547042 205713673187365310452072249759066404120 -74508119341225157200407933179224272321441783660508219067617424275805019707779 0 225114927365015153502587241872907162633 151589293108058335033772682556408697069 -15348310375170218689970383155596583504935808203691298400674305456870261749939 0 174543572810967823929852108668556465651 183563935171926623066651388618495272443 -11279277196179967704824595265123996397248345871677295378998120428344896448617 0 367806030756726113034134931774701191646 152018874866310372562758460823782718519 -16941536347697385954279434224237978905694184976110732138805059136769667134292 0 74263431761143796329446534023379253120 36982977370447855780803989672069227078 -100154593848286086250815406099342773491003942614168461251293789050494026188527 0 97114829259531324402024901200037892111 196159059816485162753853922690537642019 -107477265976015581961602104167687766720321875813032283246213630102111083735279 0 209848022571575679113227047278984230845 73234640703422497548011109234459002614 -12973196860047661071796553904887029960800349265978379823059273671067240718242 0 232641047928970686657398351750080389906 162676545294383019982782446596046995713 -97650312072463682355284221039603200148053254762403262843541152626917973072752 0 429396041144419417005758309839405336183 278712248116847115703401743980893769909 -77711858475028801212999646978547648651721096534666077392856609512752036499706 0 386037315565330426599158286795431640507 240913052819049208111721336639375786307 -53451679758225551206564479385403055490646259444233783559000067077365730759346 0 87970507171364682856394310509890277403 141118870847496248239690585809547715950 -28701400514183884533908442807053270288370329831951364792245399544567551612280 0 338074587408805776702852279218519029890 106426913017728453856103422997877968238 -55319823865976025463155988336541561838224751371757302813494608978298010813029 0 422817719177274664261953540309339750087 212745774052331416640794776788705155066 -73643678869349854879469332195289435944370345519143664200847243277687954808898 0 114392301660398310504820358560630531571 31224568780210599707834629691877662979 -100167644269637618397479251097692111717594412420096304874659765396922133824112 0 385645339468412935612173493466310323948 14160460012640216219442496569391006180 -21974717529865382130440561538673625926884657445805314993011011741592370245140 0 306448263481668048703713743542754836482 159125096870149421323040189461534562828 -7410028173011832003865863544984776897445953187568708583738230634578160696254 0 263845010734416239182377788100569726898 152099643831574833871967540155008605336 -3640274783374585632282756593129957910885579782832994205929593722538690562434 1 350308207530513784258876847090894628792 36797830043518769747890859525201279523 -98631858405027183977032534221315952691924397146404152845426101309992758387245 0 313638743362907184955936907526968650062 282427597529406337143245081284783270648 -51933338488937027580310148576133792193662951275388064159057709815978179354855 0 91185968703014400544186992905908912907 149988151821817171562384776198058983526 -8581832570657185220567156699696042488630947552978981183413532429710567428367 0 84214329170815414745640569172903364628 291089197525675055940546776087203546740 -11424823446268602560146214960926420825682298247903554634974856478595497419687 0 321492236083476939652082904662881887321 109064773252684787947824411590783652261 -99109924018248998416132362194931586306976767551384145955578408535688830443990 0 125172622949407418038833650860790681131 108837116444043262349584408069971645758 -110730767445750159515974727696147210130041629759758173848629088699648911963266 0 331211266562451646803987321909167430353 119554374002282703275428098784580248921 -74916931393802791940145875920520869826147941009600977643574132830746795747455 0 225394560414171963228326868297517711325 48585353911581721903284716816058558705 -12355006545423041620147136104074979782728735051691020222917477202748281361214 0 173267149133011072994865539695998803314 252685887384249589187696545406927106754 -25765138965348049705241388282475967758815664990574485951800838895434940588463 0 133975526067569218892439994225431860158 246861746951492107448306022703033616475 -12051341169856696102846028899433165588932739919311175440041568035646389953821 0 125724950374770419599254114966779165786 69852789110431623082299116619704767193 -101983355393737812749600438366309075831006439063790405434819944174067219127429 0 317184272209169691288003908786459557118 305145352000096632534349758009188869716 -87563203867741542724188001963023387274145142676314615563597610417835100205362 0 303750273412939672418349263114400551488 124104591661188764086021432815671952694 -29617492587427611217912148339766542383664280398379389808902958642701407017915 0 290565056532287106071890679074927915165 228846512174229169734869361402164523948 -37759669087027552453265301300196764771397260752205682788243173111823945873726 0 405199957706485135297606183075778178644 89094478538767697125429585922642047094 -103012564424087750444360849716532844735645411403789984485411207163427479024408 0 269129397613334079515932388723783228269 280051290846259761672659834989175319402 -75729303840399213446566650932875641977173714266512048226624894760677697634559 0 216463109481840006028771171581054511573 163984580682789089213861554067365433930 -25729810370315808584163165956870170061181211477839954187588770219046655371334 0 269276699528853231612170681392402880579 105350051471187917098339624472648028360 -99869723536778280047928394876914306643976363620116612374939510781160458953711 0 245097521678993754629135694536772718492 46199393882119520220689909475120689319 -84999849378403636835375686448281502317226991024052947991096876627515027629174 0 420977442673151095152008676996322188356 44375308823416959584766312649380215006 -88542230579858391200602806920605674067659027744260182089433356399174776842210 0 211285898328948772420404877379590305476 122557482670625008933414917125167410190 -19623641759243111734107831580250608959874238452190557810178371389585878408146 0 376867807414104903590319288002215204568 103088495313807848579981308454610166016 -90246264558603552062452615009299931915869008454340643469546620600793149063998 0 174528479843671289890982020490136787409 258844087118740671100135493373344644495 -34348419612938131099955974927853167375527338608416744110189930666519458569415 0 173965197434060171610333916562950000289 23771263213724920846516829514863928786 -3774193998088866880354620445642700145690911386511174898256690390407372116201 0 227795238194769162411492306624755079899 217154769492734248282589894355098832562 -83982075444891366433148451703690606865902622171147627973205413259248982728466 0 237324726908475737289266383990812625831 120104454591125559048996914949726818492 -66633208600057588235988682012712117420379412847155991080031395530406505349412 0 418616943617570304233495436476273189189 77614298656098609091634881970662819842 -97816835545896964843769127729141113968196779050216256957269301543170305140054 0 220170928629916562425166677115398274379 49423799545919666132137649009868060869 -50324056339579956860064965973472062287140400301458816053231629769408565412587 0 178004029570495904952627700328374277967 163411512573079409675911175826379271302 -114251264981945499314760280635795571848604780226536275794122963186504421931242 0 116847214116641631830318428729309589239 72353229886217356925001752068119260696 -4095922377655047322923663981179139457079537583233254016288439821179851274287 0 380910631455772280928347565620796327736 37948295202464634248706623690912295367 -52913529319658156212455985231686108542374370515800359595001084836696723079452 0 396342796193044356557266347739308131600 188602564617353519168192750300425253484 -17381811203058040619882996938159967940081365598949888475389061188591548137006 0 188932500933712247324503084771799566177 286690364026086261685406687627757563118 -93378024370454409285231146795641299293517725936001171330227313903097647606398 0 299562667832020754314963301664129724920 50408158120505731713597494991330998732 -38007595007682666689525991579815379739539349346364972463036834239255259976533 0 281025844037831930275357092822684257096 198895943655760203211976190648780128872 -5053799255699206297832452485921015363145081341123123403733250901554014400577 0 68421271335227423586897696433564986736 162255062998868383734993095558901452281 -75230689796265460558971523488104678014477622312500973341383177271681595457038 0 231153168679703280750251567464285084582 56322408738148612918529934812074200734 -16815340598448119589339532619214151667763217473384669452134025537791325365259 0 363125862020223974414319780044666396102 13000098040228386042814465585395638489 -102777176970931823204637605367239319925106475814325712039222026980910362071240 0 301412552625562582608651108064284297709 16259837467017794851653040157821965187 -113790292218158940072521078818959505816307163403286210665549496747071266579272 0 483508823459991514129308418789433905214 270850167542260016183306058458723277432 -102136359029077474825332399989421466625797875195442901888337833456385017194002 0 346195789316221694890288744832778330012 116751393338105282955134540338007916312 -67047331509698126590525352680305524003427577493957129352562944971081491842741 0 368105589072862457456365237001052516451 171250574248463614028387615569841490416 -88429015436031619019213009735437497395022675485561547302779650693851291323041 0 309213841236429212680420294120622960520 260048918826113599090387946663594218929 -27668120726240872019461813749957142471290617428493995216848556366637398758794 0 381183880398969056966663160136818933947 88376279795598207069979804812256660797 -4913641083226758890110947362152277823311660317205251684510591481262570952691 0 93522183110886676105561442639169363240 127990896795284814098748862686808901938 -8590853446468246394799643233311083495120708025086620403067513641904753469986 0 248980276743154940943174180960563176633 44080734943633126644864923938110232187 -98339664907965757549966452418160061140835855198201780679140899771850367128442 0 306183943066818652740654771773317152113 245906751015124382190738243360220106103 -33802208049952414598384232466670330503585698463021251135042119861097720022508 1 248307724168330786575526325172380961875 7638294125592454005017054746267126314 -55980323797395458656117971086868466460924340219510613027682873343577752177316 0 400359084552986301362273798493476862739 196530643687699985445700286922814828589 -57584542752766370122664595314996462222116062699417771013751590678583154586904 0 113994672264300891393315969712801547464 285561955949326022084065261319858565243 -114940051637288169180252737538057335339019746827214305685395095946737417231253 0 107305077542510781188751802551428412307 228314796617634959108420131382968261342 -111061301274179009460899852987390138085154032332807248097976874314421872039340 0 278664611092730228928864151408456037462 176343572654805702915459950209922402633 -62006033034297128480598264631423193903994984908308549203430774140663101514016 0 103419658389300283636379767482347468843 45943120464535087985779988172948104859 -11886726869362588406116498294665776430125443004545795604874908974966213175657 0 17788417310393617970565461101673058730 26579389295765997098038219224914752502 -39837909584117468994515082648996640296647698127144907419331582922284576857746 0 139948228632881329197790879083965868589 198962128283993906552682677112419158641 -110406229312233517187558312453560306813003539812252125026529742496668577355427 0 316013578051280022028732268621317113588 184669349506200843340058030671544135216 -92833014802295844690550408463260646371050330408510384694494977281886249323717 0 383443348457631681090609066087458586661 231593935974356515782021543144093196749 -90053170674203264199572594146349966928165257823169947665692707560356155250384 0 319707212644984513470107741136000455858 103144815245296808748457957960340718505 -37544302656645254650508135723079209596088112041484922814444267507096360721013 0 221664348967721174775960077274864234604 164553322674704800124137421169946981356 -40642688734474511208782378308471518520368606251005956884161330242110707170826 0 94082818187993022774058748075208786072 118256748155556232861506157493067214629 -15452994110037531339315787070441234012989023168538166002159233948710666912540 0 82682887634209238749059184281663960689 138446352759502137269675980257708548683 -61856678683918642262025578909738931707831021412334987580784609062080608920581 0 164772436346250304848832239977427438449 242313549875024224762139976621387061061 -17216366248238458454263606986259240890138499781670747664893415843870501957545 0 358361750634471079057784199341728445093 76529154342801107919495961369326405016 -970317079322200308104074699165705762777859179902050358532802551479715798945 1 265913809638255052869781448042759865596 14742383331434773952437548135600351695 -50615648027079633481817887811628975486578364133810763817478265696233331729875 0 239354881642705224265797915556884784540 195522123870549595746606384599767116001 -110696864900836160600533298128663064914650020130146848376102216132963976706794 0 222447030879414082177876650264071077323 190360186601393209517794342171228913918 -22341068297026024733249858687511911788450364332930721487846116185162309671401 0 178106611997434343078059156461514865172 151983502986376854980382950891427927254 -71045901644051380085784705507546409244661099108301208480217835532784768538049 0 410263837956719177357349744303919834852 163247684960647842711104129020296466344 -48683529946796413492959528919396333137505315339197874640269451885809210688556 0 184361604728401684031902959749218267003 304973206003594242459543357158117175210 -80888476553098450830522306745466805565784722357713834303223141197387723579881 0 78239857790838434441155131552686533268 128959132599881975238415151866358260384 -51851379734185017708754726992782810046464532024605454823628730113239772107200 0 76096521444951134494721982873837785911 171597182063103180344505146613904228390 -85681048438320998870625922658543384771606360832350484447431119365383659958383 0 220904828369012993466642414644432897152 19918107937484570282382287984892615142 -104425422497966673919060052484984244517344118900365389243765745283143182376190 0 388624472848898764724196715816565420240 146270020407810333121916409757229888657 -102616663267874457510631597630552755801055557302492990123067061311536466518381 0 243011260509222169934549268637126151043 171255529548195422707562024191945954163 -58570354407008440266823022781483917089010296771524936422348583476682869362393 0 148275060338393325646678855605158726735 279881770779749404488646783495054748522 -110188627086589139227024152804268341017916359760088325873092453486128121889657 0 417213708392565822126297532762876418678 137714842836520489740396208977213698332 -29701466179580565761862787588596357995974028996536663213148023528345816634832 0 369799770229771358858241804911386340037 88377182243265131732503796921604471108 -85829746163406514813602125757983123658749418935998739825889203157855968750286 0 213054444313211401989251561926703035458 136553374597693332037939923500610101245 -19116592590274040629048276053176779333345837105436986782889360144583539944829 0 177939323297073798350771206627022600503 104258270569147093832605971003612831080 -16088592043410392527398596241470511260866513716225160774691697301654582177689 0 62773438551394797186381509244296160302 136242309192578689055653255563127968838 -81087488329367567297149304907061109495632159497324613244315165192312985471300 0 261585593678970802095048069504958206150 18344781372753877599678129983348053125 -44370875350115935921709053008503374975727997269061799563237732569661276189966 0 87696417151694094103647630912019352561 87610379788192352056370559038116729703 -30545518247445277676175409409466459640486529813960467683153183465697451146324 0 136550045878787259175929273761207365300 26489438258397791726350254738149844003 -84692873782749862909843896057551917651791150937670311782287253806695267577867 0 201303658799104594455447348409071538149 114553852795752087477155206580858944562 -78160438773034816104485757517341876464053480423699420551043545158946125647330 0 212762386090142532991222549998244618231 187402156741302036129653199627411614152 -92237013736085486534630388279779220448414268075851652655646601178457372048645 0 138761954534192288130959026880638025383 171696113422438085980887336412414789822 -32543567835183441375187578988155628114602050011293910320195077358776847127267 0 431451549531244926796923188849532859327 255368553379199224326871679382941377135 -7870110480014336914827330123310162007173412779618252060238824664020396808395 0 291103478523168637636340156857349121623 211496450359683802865504135751410545567 -19879146747956185044853462900505427565887698594524838280949876276345237062325 1 212423488542144528396280658312022064999 1913517102279168215888102254788136153 -35072791174821106010960597663258919310680424763514823423893332156095092153430 0 140733176965746301645316634783103035048 276435374692207652816789316657969201900 -3636799662228051987357709819964885719876577205569415424767607443658441937520 0 45372634913228575794149989981778785304 21349480024399857003194671048173675127 -83617231155762008101002683358750292586392071144691787225901404111162623834235 0 170605285239278013377291962482936592342 51989206135489279821622090854218266222 -51352634709079615615519041938315860124487239443978690429072499647367819662378 0 281177528222539675176479822700137977301 178482411775103591423708940682757919147 -27188155743178101341475108977885509460827178452176989536393808829064720341786 1 377239208772149471013532978594169891107 22245027447522982665916094325882727434 -67374275679475821161224159185882922754218647224422117292048603699639279639881 0 293340639144579123838308385781473375013 138953890250240415163113679675065453886 -68610398976535205506660486339389582954363887044949067061741147260059983082774 0 412590969977799154655769019135015900396 225486476084191112002772813301303329344 -112102899606794978086928911534171013658628475474517685480241595632177744452281 0 251516712374755576836748153513995484312 205185246530018618506317286572432875796 -32343399207302182072247801289507640581166381323478563301133722745447271498091 0 168934919088611730410053958049624850408 189803626426678839401715906303317380612 -19619107322433222037003861376151409274788861223610661572418013570637755161464 0 363856613654290124422436537684887928591 78371701067939654748498346270974058971 -86210053712008681551840771974850181959283054749982916851811999731401463945066 0 287916280158321046501430427643403221365 126103119625227149700674121467174269933 -46082272645640685591794229729747308008510051678100121060098144805546977816195 0 326153801202885954978173961040935414527 195472358632339191553744511472278095781 -81628534913168814015222166933710740305886103857541053559673494140318102103128 0 376191036117642974761668478388969093170 119536095802056888340390954484525407565 -27648369390782481158660209612073732361572108340499175703283702250558399529449 0 293268741703162359828863701617715216830 50931819782712106764488245401373041981 -93657477748199222906213478703691728882893558859067753481897128184938925624844 0 402705512633992001720041320634727245691 165956193131012618838026778562539429279 -6525929643034117291034989337319482155151543350596334371075318091431458859362 0 277883373797686485338383048422818897708 126926459121607879599359383315954144833 -49990272916766332351621491194787485284891401467190167288081471929308847831174 0 374946734269150587480584221130149924936 165903213025793986170041929377420761698 -42159303116311726795345082901677733191678536062270140159445797701057274789282 0 428855176222482198384658915061039996179 170891238149990582885875883264324508421 -65618552636831661785872516242835617184370774177084104095809031062249527355675 0 171277069033299808037840321887702728005 280907617202719344632982502271229026549 -65291543430153768133864589916740008581237512001522762705135573907675918499617 0 356990419053478704414167599946630127491 237344686643890724212453374779826380675 -10010112367304986085473813054361650414090959558171834464030003164002515562104 0 151016554771586988481156132370910101543 256434714753692528097590844241598835520 -87402447737038005617716073919006505225367860323340151901337980272527485338657 0 374388464457083639345511210447081202268 37163617808277663784429364509541037462 -61254818088142240050558028088177611620669505214424764791924749741408787493841 0 245267320057483780180585351468167271324 215946789431143233312534243420847102176 -46608346812889398678537840647776506147949577420901277002253883589153480092386 0 409391177257865745337129140139395808842 129978308137680797552297552236963233150 -40529947393410893117225192819147583904449369477975450328632789009267563629341 0 143446476255270625569913647655315049047 211224915135649297760941317919552216723 -65577693561217952404953086161646651928460203697645556541840666215133855382198 0 293935903776005798883892679042272269834 203194341818629539980973091822224544007 -39816499629620479981308743349435818196736057294584616727492790110768973675752 0 352868208599887261855071677770806759176 88007412258199062242068727712841756279 -52497759989726918235828589914315545739381017440070757523339519532490775026470 0 120340354670950578273928442872213264958 42912402037538348895541031135951825942 -80160415097579566941346293825004970498416004153106840457711030545400727284035 0 232897147197410743530259491499312007548 159258091495630515806492505610705287896 -74591724894183426040713911199302011081088242858299464795687402403418812985013 0 187607606238303807859391651886224536820 22759524811240025832210647274343167783 -60634046001346032486285104458410155199148893551027455047155385995751198188833 0 346634028981719696464471762055213638178 84688380410581614070547910835194261736 -64416141553608911215182367027237180989871307823616842722183731497583921669386 0 128770923846100167923750095099503751179 258096627147624253600078140951681137203 -53701408549982703334348453822524579497586282870022009155882208170354518051429 0 94026096934499371479624973831012281239 318972871837885246242771493857138350779 -62266760348614871928053785552665560978798630189695377842457806462568904579388 0 108171909667698530756495447109516442055 70068600468697070234238502625161461127 -75570489346674803744824490198814758730155214257328179272174544411718074754486 0 194125421664951853292706760398041852070 160531198899739490332457604714501392277 -73539256582497473279864919859300082086959455173903079688797062452710488701045 0 197619681309554385960898777091350944826 113437966557044699849711500573507990584 -54835617485483570156824425407357656987319850397750755313930841650535183423376 0 193411487353150122851222366750490050473 146288370511850951237586856461292376486 -50316396146902912806455391608804944829356468492162643499444559825450469703422 0 441705537532349615470412593889353986498 201668713479140546876571608393552731667 -32508867278808409984319331457578725109182605765119274840200687284774578177381 0 215518228331853763115869048072899342362 239188113165339563950926165346561213294 -43816750570703907399988912062428196845932304412145214822714547618359529288411 0 442695212073531973821612497619395677933 209313606208772748141158749901549148121 -16107041463552930527083848118957090323931663149850881963849525010097745183091 0 104509116825839514008676761728554226998 37881262892700920059620528162639922941 -99883899501559850098685012455455033473213413114892111555219378485842071963767 0 420097551060185795912774052184514796024 248073526751475526887029386817118370050 -47866013519694521824950737982738200827332907343329386708153744895621299699112 0 91562682939504840438832432994816163064 198308108586594292769146484494509799697 -67252020601087137502700374223562250533943411727966346615591114686471270864872 0 345812246595120190169267251526649345235 19874870098287992771281523486071036762 -106102439876993475446033113865665086757661326454375115939052798009109259239705 0 302647554152950449637036505998389166349 146937675842288100045473672769479987523 -82604900490103051825176809378288626785350209370261084519886656021651310139567 0 119538227321891366386640139183339130613 161164175747157404069223094127035591066 -35059513782705869436487299084142452308098577670317862281528109843125588272697 0 344789700412841914224609735076293612297 34039391971731815963589939221884923297 -1532162964761207298493232097019726688300545292157004952147128643075092344277 0 102403204329319711965570717991225841274 115449871703149483724995841405367720520 -112901583233770290354935763246364939238659434357381238688197967722299425687599 0 292600433889017057931577545225260446897 200117943154986541640536981980394286431 -60074307812406739264973895109610475833338858366081189846659987007850489828141 0 104131523166357428660562848765671859976 43540308453253937000928265743732281912 -89051900200463115261567779245944070691196061184705599468214701432813016409539 0 417227661650759091450248142203777430708 108082704480082673342232333418809853380 -20547109510500121319098763136248883092267057909889855091215904233971812996603 0 232267672946499079279188377447371807311 23765277551502455299324384936539514213 -14594440503033367518707342646662924990035372611611749321026468188310998585547 0 249804227655770791931708451399696795761 184196661892054029987566184282322338833 -52466982668641938958207299098652403490029429633110026639024675295772445398002 0 407253389764968377574121281617540477125 250315620450113045056813156730841669940 -41668109937418530633760439529536653031965686412374614718908102909468490652050 0 176994268352802357655180912771844200834 234717576122359361449081941634127371943 -97948290061134181054960977828286444308120149591678834601293270046911898368065 0 194572859056595966820856104541103236839 98884203102255456244012958048416472896 -988686837846819224261620281339359468606887235561022312693486658190920245999 0 315743806425751456324439118453490560151 141259661648775445596937935769240490626 -12441503558170542276688535999723120435885658333397361267253942073706630839113 1 382336387513322030095707529611028589580 12281898100654059670465284537305963860 -94478470948344197365436080629460578316589964553070240180227735296233895011595 0 391130729792048802827870618736666617935 212840505023369592315299456515647710515 -49905912202605306998104647335914997900695713471578912544599327035652113342110 0 359972162351284088317931313665030720151 87965839670828797614380295841849108268 -61115267680117314297621770979624980775693264599292240148745734786798164280734 0 60030230495184011496205680007539223538 110960206930007615014187826950666426042 -79689488211389205551110887886314868356698614515118487664845716160111266114674 0 64721976899157021039149110158707008964 49643426831553217548556228059344586342 -110061893058307727424811964244705915331673130626496360738537133083759191718776 0 447593647063360822921670549433347172362 194989323677707644412491767398447636989 -109105273288760976286017284896529742376192825069882027251163541609878365140550 0 237077488457283916401115390882926322749 49437212660409697625787570690762317829 -106555527050667451037014509760960379272674969770752257057730810189248263887241 0 176625882997152335561882563727741143095 272187955283769467159655739569397073090 -29777473512160770468439590378242408545763159980426600785800541320032876564475 0 129662269173894839471373906593073825155 170710890659541699558198376172411671282 -7765376197227882802520905391694187862814094611394079210209589423192575311987 1 177402154934067546431620736692584833663 20185375803432370647665594714952789745 -44773069266242470981015692241236094202947894822527569449772604329682689336376 0 85391370035719927758312958769343750533 75673248097541893709593318875502349885 -72581543171469187650461984698033121499244572145578332080300231705957160849225 0 246179923045001011906566975901586246435 96626268377826505356999747031272398966 -94243076669252379163237103476094437627782009326877734019682139654128196327634 0 253401522599758410559467593334355409083 127299597946823896676028364491932503648 -16668294379240429959032294798977200647806249846074219403846034693805295714154 0 62456958940095654626581212328245602472 28596899590868678321091612471432264970 -103126752106242195199020000559165856424561733672057204865923127962069007486172 0 260981515574682867643391505969164763635 243006967824179347252632830780138869679 -106490124936750463164819685139671838440325560344030953131039263857021488217782 0 283761788871889434155286186983579003876 190698160486897034616728600049426595772 -10498135352132582786611801883273998067865274112115023368818813091832019986270 0 348524947962863364981370024319447642945 73288730709116263788571369485283332707 -39099319033777932088076131662757777746017133938635905245824520712637043616476 0 374593955892191706158881479505168106011 178582083282329638071070459583590605247 -80681029975796264065049743467903933633596328136790459796921220008680265949239 0 187486862440379639562901190065244217726 193327244359023990154006514788761269078 -114853851157818242093405428079587790951982761259070221300707133110628609162213 0 277457561861173715876968136051567352896 73104208098886130002508136533591177927 -11822184929442915408866255675998102986785069041487181471013501454863660776156 0 355049673587080330272116392915164008273 134377115638177903502066592541008882109 -39350140911073416337161583799045280357220426933288949579462122345991953456564 0 67100400439514393651792348888210926398 225931692712456111409360088888791890418 -94909673324756914361122416786697317158104553465119957913988115314160855671104 0 175458549739071978836498768372483234124 219900386149227454356047629792132150126 -81971061996093304865842378893625235212733678672851012650304665548469653355548 0 137081861416212358196953335985664199640 86263670574762664194122088966404909490 -3126572534395041627723576569825324273045794491923896695001497824936230437081 1 353910429294342922323394791393916044058 51454963170751671132097515840610630711 -72081207952783977077221023602806918406040186481121226596239547904710113416599 0 378903725944370134270550881374919286778 262445336640169671183919534551913154785 -93440292291178608179493262251559787571748719669179293506316391511109757736021 0 313561311967250896411354135457116551983 215033438903580834325163312749551876279 -111318362681674673153593481653134769531394082726090136355455525289146362552527 0 239698118127048476630297078125304117146 290075225998841813123425883210204158049 -16101888204943696638584672263586138834668198024549028230186841297885510985266 0 300668748362363178322606756706072791290 222892946606737902794349829725297423025 -80832596609752631521065593139374817100068380247408429404638791708528074700000 0 133506040778754135664736148383531994386 138242257205167372709323875041821213854 -98679094494153869811309794868401782182972931624747393101416830377625811507845 0 289088060552003132310563343220419883135 133012234916457582305340261211007513675 -50169262301390073953478011712142996128144453611896800768965242858432265625574 0 312371011149408777010231948994387542305 189162361493724151470119304062233849265 -94514454519325969469564705441293574760697113883216131791847352094020209640390 0 100519364146184107351779567909955124446 66357615459118287576006691091004172330 -67531951008340822553702463552424012381837846693916870606799078562697707788342 0 188572998261223564794949073375980422548 191121784770908979674537277774936620626 -13575902607946535970544257563204027075197215458782827754451934106044151580429 0 161457055015425775790307722099862778898 173981097243323772801076881865920205962 -29876094332527750519765177741580421602246098955819943600143019311581728880558 0 279589863013479225481481082016037256641 84905300259414745399852597324901796692 -84277802069989432692030613003515748983914073244645030694906803918065792777667 0 109180722392579432341766236761439575925 40366363780847943086742894191531238948 -39866361440412998359187589600229227640136087692519447439859013360860914964540 0 131919559772432446693586894345995207238 277829303199534515573150722173492823859 -64292620359806772458115923014780068902630750027207137974266666023757008050729 0 349496868167737461342961655656965407480 202859024444354712610923970561240646902 -30187838780028003157091695656870021824722380734740512761671923888483131473920 0 207301943557104800130695406699357800561 99136993766428598251252972280263030227 -2070595462734792723160604318127633817383993793929011395313372672504638879844 0 356596299588891828893554517103238438200 19265730252754533054786922587025143045 -49407227241188195171302179156824725361365001129610779099593108768995260333396 0 449709501359412277075892548259802066402 220655333901309183113715412512615639388 -11166525943377157506679219795292333162626473965133941849553231030023266786671 0 82103486178236231870502803952854557361 106016334807599699379085588312463128805 -85298305074545468727241233978926932453255528071274145193023442649433627675692 0 170251098930900220364950139061595431321 263039774453348105560647259251009340359 -75901225957608420489523679498906123416939874941722927932584140148568754853359 0 367775846664407067708409350808304983963 228250975488890622436769909679966897148 -19701411891869799528408919978261798635440380425317134317618775431253599221526 1 312606752680597586275388611948674601182 39034286976800766761195482309044592589 -54375510044233786964970524494381276583023144915774702848872137160199977916188 0 127193521201046990742860224113611374899 18306377472368142874631660065400841101 -58000612156893808155655089975929426146946496418925077926195850497504311723155 0 314825157710548743020563704844014702569 55300762332076609571236856030428700240 -19999324404036989468923312117690860525922773709242396535894614417483125743932 1 238080734463373089553738240239740555412 28461305981634003461640932991669784831 -51516159853838479277067167427588382532186087722784325313078513146797570167553 0 314801344839641919185533592980199020890 212256328608465067850361423357984506102 -57183546068135758522012103251726895336173043505336182078335754000574575467231 0 162338299609484508696078948647900992181 206425845635638305154233093391010893385 -91443461352497755829798526852930736786191070176623519271687784916250805908159 0 230687591401916901223664551300264299180 60701841790460552668876096333436364428 -98120812083107266861581780940611277011249508069711243562061659660398530190903 0 449591422077285883171898483927613790767 248822671127277985581056053156997756793 -46012966042078752026391723620185637105419040632809142369847686600235823397654 0 99096720962092064622788412010547144382 240696141361157163312134006658045356916 -4222562747910054060891600910701700304169888983205830397053718026879320387640 0 144205960184849623630726732768689376787 26377786274362308865113169266944794765 -52720494189195359225604397967278093197221165273317911810037857788532899999459 0 210291573378819564039776501532254596118 174702705543935354635553864804652879984 -24021465484619219469678455112207606958109855212752630071888061124387477204515 0 98460158805609870122050694819667353934 13574921525761739694695810678378447418 -34307676923414811002200539438128678525379935384479512610245626584334679463975 1 185308023325696852730728554153031309694 1097056076947150902881711890635323475 -21175871374237291017842274799621644603797203428126330560083532496389533173941 0 207286853357863593014167266982684077047 40813869432361353376869785000884491702 -13459999585086340722657553918862545350225088269519454710585749849446548112889 0 149481442423534024789167589213312537108 66681978331625266648147229429592498750 -98006113938452729535359575092554869922787397602358529268929184262546183624873 0 428728444736865355769913943318622712748 148869492186806888273857387326783739433 -47528856072620219702069253811123800357801560632194545884263935004681251059631 0 119059597212521599625270418332028284211 163490004413422121013315496207426856856 -108714832896683290701609486530127858117520560276032414412282906129601499260332 0 451202222790195604680462837648570550516 270019053322804025439233973189684469973 -79462372809786893134273677916525688295792480453177997771808619854363171627253 0 192825310727752590823678760216586954563 63350538063051437603473680107335954453 -28192999054305076467060702457324030093010792036314592040866192932190459196216 0 174228590609283584775657670521400143261 138000768655897741221773630208065568151 -91148189861059806132514650560332522791401895244020435536928627840903656937255 0 155752212091079617148563724251384126852 85348766128541144920846420369007111139 -12952106583287842368088550034659761329359593779840943855816132539265305886623 0 52536536308798930313091704379814117051 121598312728571729946784972555390974669 -99599125650825923596058736747218920211058505183409773137233044023071897859270 0 190313580021192799260391189679424971907 137034301677381599341398154549693060119 -54213229483704198151221891885659693044685337658120053270444531237650234430432 0 191318554188893564781121079315763145114 181165081772371883135514505208599382288 -85675065415901899768267092934296204355876954120595491766981138243471370348810 0 196781905567813754770820417205785797897 187098099964525599902673675272821246683 -76935086666426761610199020369573278156412200077183669197796529926717549110209 0 174246838661143436064831585954260685255 216025882377919386571502567055820910298 -79531192454898790432991137468573372293099138037367951337084588400061869105492 0 188782984432849478324835924003638873162 78588919249834990924984026356461909286 -50165926718913292752528289062030273738603589968406797862496165868833566166557 0 352276556899844231488934431422026112096 44838358788766100404412448305910336569 -44882526218729411513131816976423490169506273360215353662623940745174242840594 0 231842397262664816309278502166248904948 270523336660463114343833348639404077850 -100035679597028236835707127478011314041864663837121686780753683420775109272388 0 393143455892708549401065206656491483858 276696655690566981551543738710185621777 -73959966612699029235394391078193100198062525620332109277514833768045045403486 0 273803617198046646958274699952404518165 210795411962966529556902221887629735639 -5238422081436175454438877826765359851430630374181928241682138451417494184598 0 122007510245196893390537159914315585666 22169762500703093314030692642228642637 -56530256724553507843215897797247227752655268500451299030028972298199931818956 0 185623717303514651391293258893005592732 169095748555346172245088611687498952223 -71670697989874334693670094360099860029936995621036928038251317307253173319739 0 104342388520531920277874300055004145973 152360404138564318491549215758686490726 -111155921163392480431197276120439379314527815471416783992551184578062017104258 0 314503124305375588296930165064199968761 179387056406010468451139212587391650869 -20941777879531103392060212235088460061125893789140820773135800999238499575283 0 144716578139176287037699304717876197004 210637621097578522478180062399196354135 -113163045195117652826752847094645502621628399310186372304803466230439623583587 0 159017347359273297768280048280284134115 228826588028074272686972481059266022438 -27073207894178456242023452703942997494047716271596560348839169008235460338032 0 52211736874969123235775604143752822061 146421510972146283555572366217063160662 -20628184188586586924974657831112379702692250475394562228318756141314108736594 0 398342927139347972519056339728301869134 45024864261689159362396025373412172277 -4233850411253284637340378062795718277073970922159339283261453619912304012224 0 68487243553654198785781765602885102605 97901104338378330807848145700156942107 -54579321478108303102239379314743438295118356646494580865151945278726682736466 0 101359437549318377077251417908574134525 269041120051888261835920401402570213671 -42760672319971083360312308921249219099760780525278312880150241715918255569583 0 260131687385158692510775732612549929782 132280593386475439203869116786625944271 -30666221860142161716919402524724047716102151127690742502559963778309431674587 0 101739542058854388973465286505617531911 306446411723193461369695946212657335804 -2306250407875774185496590601413689851973021057384672688225239929829946030139 0 225598275572220839394555901254149777482 109088788933544115160054694054838214497 -67959249690605520921762484362717153209628568738287602088050070912944188281262 0 268376933758506314673037169295618724006 253681280627923142613472739033629427231 -50685433406780069268509238523925898092578703377310130493665095445219214935331 0 412425996186734700536065050137165278795 259909153275143442213010998482606665916 -57628064551068461196126043803225931714167369589748561338468425789484678170169 0 237403719360418672241294965986528146811 189451304114328954021292681437217612278 -97628785638714191736841792687673441096436035905124032228399202245170925583531 0 123244278848877628321313568863032993880 169315482298053447347078754904929763561 -108739969970833462514225898908657858031190116347433816706073475570201270198749 0 313962871644069216794780145794689373932 111541183731885563273695772009039161570 -90124302614594258183904851529059472999720774189085430509426488764372063937433 0 345532195400054055848060639544900607923 174348624289410666060615618816125997485 -62548566734740955657426827017094425891918419763478857243303770743060017976872 0 51531779281034126650468629897880780259 46520395855116137868110371618743279103 -56804102339298976947225605198162021041914664320931477721735791062926383819727 0 53769135077508761110025027722988928768 114899103846736908085945156872710400047 -36344677466237804180332731813427081591548461874377325715629636029542643635600 0 78675129315223385559982447423586728659 149996002188809691333900403511611808669 -74192427450011839922065136869810514995455297294473009922802398357772484357116 0 231075558226846948790838356728789803218 247788366733093007191633218920156128676 -10814607672194567708476955895911343647106655511963324128736517074523432296839 0 339804128683543310990293936155072305939 84560082006366279303397770296163374502 -14670247243417959748430522331710394486060945019663229292652381944686873932949 0 216816132467768706293094573634318230766 104398878419944410625233659620082612605 -56107121155374357535945152164294125642042757527061510117228535407976438121497 0 386892987353912128991329976336503087663 177056120338547206064090741914307427623 -77188073545141919153253032079852740640070319670556722220393219363109374723169 0 160023758393181220743981990294458592550 176101366278784987644445468565960054734 -80438386114623064927081270874114948353006511217400804502969162121456798770972 0 237644167675163130732252669886630134053 165845044977546381122944494488511437219 -21750645098900908912678883667612133796043775549396301332468588133882973948423 1 352818499509769438196965569266618433477 46097658249573195873115401561342364971 -26862423298115945424714035040705559189101666757714539997310305287247826384072 0 399410086271561552098443586429538266727 133833127179670490138292782897063540502 -84080526562328653229664227914484504486674483582449941747220480411763289195625 0 401664657227536740437249455683812959613 100032113100646377756626749845861775733 -62111157053950200884658018451481945090142434132677284556025153670367313526083 0 370411415301340194750740515404640345867 228428336354806362252125128311087217418 -20424636474284922549582098406694102113352361920085060259599092888980976860405 0 59901448108733896277327576901204637639 168974316108173167722431119890394086304 -4160741530737146563091753966954883675428769918419651542270453218465396411039 0 269768321090245135860744703491135204176 161918775753946834640965015495806500243 -104425436525312353906936282248490165432844814232751969526593455875068190422401 0 116842637655429969687192063405922383016 282963147268125192126386127265341408880 -45930251344865671062516217930438777182729125375578126035939365948425751025341 0 107758920282811432284432421635820750119 123967199290914156627921605572173478047 -72166184723593643001804792566029224788650578804787833776316904348974427149973 0 440446365303761438160849560777344991059 183872861468422199367074858983042235115 -2135453739441151053057995441603527253842483912513195498931921015333762698074 1 192707615268490441859403928586904730230 27443246753789444977871683730713716939 -104721606071924032616289509731059945977525632443948667457224299259862780652331 0 99444080945278110891417445470834440351 234394336920344357446088626074774513777 -64649953962548670309290426361806178400660868603739559301989610775384906017495 0 119437588015372638358653065955929696067 207301307614384551817741015416335873135 -60417201892110932276808545275887130933760205660759426438971415293151990784104 0 357563908700485063630659780719065773984 87137760942547591151360966114147098276 -1664536496415348284764196001551027954475847974659102907819354668847923381573 0 182045570726329972739526684296461028116 241640667881073598698849741884010686015 -50334896246466881619652816643741010809861401542950841215066206542516528102462 0 76982247423725866446244451658781690462 256225685541611358347372834773391879412 -111671568565463631161878244794179262953758976156489233845234863174777065225302 0 209674305635206206295267498468839168211 334836052697409696720943411830443927749 -84068227844428618510752367273992331772053774742356243151842411999371420637390 0 213856510655212747028968004757416813425 222215284506449987049877285938495622312 -110461813543747915084659553707740550284295066838275703224501816139984718372811 0 182635661414763611436852512202959298454 246374114488987432989195223519784003714 -29122247838865942836137321790378969069427684636926264527790956825568474242589 1 299062050762546799893302911446117025242 21385481687783856753974327664184076975 -16139263766274456177120355717390955306945847926594640452192538079975737408402 0 71648532241236883109215415334010002606 297979341628169690399484262103437310377 -20874239397852157795669141416178151087606380113974852609761536881486493307392 0 150553430298391297479361605513233569661 192471917098583014371143253644106423847 -104756145063898825208314983368638941374063531139745857591331545086102872055886 0 285030268787929265222013005900329452927 36246807619398950222393088279865542261 -16755210969954310765772278703348360905394375120217980143154390499093667986706 0 182375653669222507590299539329223664205 212429785785345358252219258317618075269 -70783109175121756862845343456351761250951390552005811879507805412945108769003 0 173364062218023105248771803378923309564 254499306310855828324275461595044322206 -58222758908908801229188263699948193412155251602750877235727824399408958823180 1 260364144991476085540847676394715163006 6216379023146083833797664783851119428 -64353453107305707254506444564522097019246741144999070996419662360688616425200 0 149196767399047737387187523417183578929 218984233813941470956365754335022558948 -23354141892373264776056653866323711150159533531152826524271373486896062079789 0 105379255404110152858013130094867167983 95808563785963366788123337399978175961 -57611942899484060300220171354902447078566605980011822292979366602702697093733 0 262371395567136959236798414413756731062 79454808214675985290091166154330561735 -91808587882179410241698697432443233124181819236062853091400802770675587123453 0 95262282643029874522451691973020293809 73639088399079781493053381290465217598 -60407678448328100376700120908974227594315698177575142685012440141825902479686 0 86350058477511789825269604245611020595 201453575936887761731596689172812610649 -55607964307589679565627221312050555924965301035201614143863626108426976586943 0 149806402551016139381632929153498089910 322190303137941309147101690410399742149 -15413513245722317145573937263298588315338520550444589637013196058603861533929 0 311413133960420882134668940412547732657 41752856447193350215821364955138846387 -45288307403562577908073300826138633502447496019441638492651413492215307502001 0 334612629039861254519449932805080904299 249134695833083167552432498040264330773 -10114287750251240077003521495780052652710906794931970767516471307021478042372 0 171855821176733600196644704610006371031 224264889055473063135963301318521913722 -75265869302360323966037358405959354112912133407276386969305344351127211985499 0 187629032489822873024840163305080532404 67946726721917054161674881154812311851 -82152140935567462125942530669394987761484709680471652422176220016190772323683 0 341711612671273408796680780277092788852 28162315999875702735428022505126898785 -115493258573648939360332440464654352426418451414031017462467410557521316482296 0 473368058800169480420489020974833089245 203037730079224839345182038021209908632 -76479466949996444789078719436743917952923292996685467124258003672588325212268 0 444905791058695952979320814797488933794 273796041384197302803781177529748091699 -25125996619364855763011720349183645509988835722218814152536734845877645629459 0 278343053289420744465930265972001641765 98058311008454617563825515575074589524 -1248078784790969430960737943140846067104070478921585937660777704984684765315 0 38416534064979764071134997784606951428 127546077762334049779419306099136370631 -11686198105044859792342682897153468115390574663358027760130867916223539595820 0 287146318209289667583902269984784231016 10765504804724987353392419097343402007 -141137463265921021412870884736445100035811670446600694335396867672508171650 0 274538521361560663499576744085724627784 43759352819540090137575600740942368090 -144477822419594343023623868012350352326412608245328586229841950645922033380 0 385071211877541479197628698548269248570 216530764296105542633490555168418706765 -40992570010755300113787619080842635223227219430122838726605437431997137071983 0 381986273085074957208534020565292972885 97249896313959696898263511907805366012 -12371219560722909729801092779566636720666568841000197545482418738774620333404 0 49031433498638207394111313092777421885 98213948980661473321966633660879562330 -35364145411313833723155005654157370127366685927320245081060340566566358872091 0 48244574149369049428136591890000866807 130929816671018105885243625154836387471 -66526060365153106090097562728372879608145845946552240188072421392613749055871 0 408064451481820347594923372630675737973 158337362317029691406884575296530908875 -91593038880149586289478060392810945286291314167005798449092379142091243240221 0 107426298672020696194031065661831880111 98102523989228437563023482987037203113 -36953158611679062860779341792683933272314093244384643873893957899852659939766 0 268220495268502573153302303502516989447 36706988253247939955429300467301260877 -102806076389248676169723386456845517019583891903155373282638030766050822573442 0 369357301144600826179236296942881198398 140238574182124346900541398498633260517 -46044626959422514914355589659345347006403766372290743284392160192247861050437 0 276798709786093669651474831434363707422 176469346034907716557981322668565373378 -6643241370247813390213189856923622457243138508554526415012501652095862948578 0 362809350268703833655982210520577611333 248234889002626536539296445178050883467 -3003611162645085184724773306628921343238426566743403083806568135912317221711 0 49430335564908082919354003134664006408 54277786036874037199333792285725984168 -96876230011928012643319349144809249486236091044415402214656652123498404816465 0 371865972612933313125490018137335013361 181035470744186787316188165294300429057 -70539399787146524119537253782689181217782531842640890859557885068192318808675 0 219895234157785192115371062823093258139 40452077911054828451716952279309183079 -12792182983527004520975218513340517459942353335063150574519623735081074174423 0 63571412164010818098072521617700329316 85329396661156419390362628137874964107 -110152695794993170753923644931120234258349968997078249043716663735048422814374 0 248188446182126041552204184851733813445 93139156084824522664353241489935855613 -65840731854429843283458734433514477644484906558272863261249182288564870527907 0 99315315012308028197996798680515898864 302468868741384936781149965403691875226 -52853388003490222462322884440094929618336542635888093820348783014557592715136 0 111109581968930903523994230157574979504 250247047983837250463484617920066150742 -50734701374467073106172641985592145155755728377753483865748069366241851996301 0 113285192555933656337001594394721208316 114188194674249494201342828060443412702 -75220177207339571486214284671208360514752674018651974660672077642757335257964 0 208753672347302431403085463318802646825 38079291622561352853876760845752233823 -70850502259261963281077733183853116012527658146003489440651781484509736090747 0 383849891090772776349800233543906736636 115116677521309445920019447198820467607 -108384758494731974499059475488930734994270173848227614231105472374331385075039 0 305813145983335213224833465217614586002 304027241590675546375125414312879526460 -94373599114502355455545199133910091218317366506629156537406087577194291244011 0 351136016950756632286804075936343958770 95067682142608432546549827729743476368 -7521550967913586903228360363216764835777144780933977995170441904489123087211 0 253982028825150512281146329690969617083 252559256526854359480025933012230928390 -61666829540629680448849586522092115591540432827016616588771017710458696954399 0 425770473896701467405333075315188289751 154626125321862001610086983916421881406 -83264226567052362967889718567642041892799793060194341234382713980215885515458 0 426148169185249288043610821740620753492 279418344796949182873075834028478072383 -19019347415732266071991527865888663738788152541408584446039325290901778680968 0 297037177318530079737599553142051249429 258324427867533886709683197241010959165 -66805591623561814519371162167023575943173675845202764176053458626426415285279 0 431874248864119623385007564239662402383 113747308401473376404062565459480246442 -41097576912135798324449247483462337595237158960632722269680734379550641282360 0 310239710353665125964247442453361783538 36211255729138935810809388737560179792 -106531828752929097973099333323873511155015138577164953772095982974264754025720 0 160144335573662551680491632669654960400 286533908321602540916663884444348469192 -112562653182041598411223262346013009051779519559885256720772447121240284928278 0 347700247713861601575782430734816386205 300058978671715632622934387591547039372 -52427044257732773839332556348154585646167248203478162452755267634742677706970 0 390966194973676476419828646774635297917 216136611829696600693343021137419448733 -15212092589841742999753871635822286815018075280258225985733671459221655374161 0 327646356774615541816703175226381732647 218725297244237214830407422552722548155 -97553216855710973986861809091613125625844553296640212060217960231100388695310 0 313769086164482510335746925200056718599 221534517434445658237164082100581035083 -83355570070624458994065855852548410753932476200906527167345973187957887904211 0 363444147578926634179702666939073411987 145198050680939081864852219892822209712 -41343444822532763737703969955283041579029742717270388437745094693668395478522 0 159544845391552409583305047359448088977 306329369601186353748116846691267622034 -69418381162326895416670147347702351280013772780284338417099683798886749335000 0 325042069151948582473511009040309533189 156532359584716063601302777577287443307 -86364868946101942551622516498127240262704182804837681238926408244440326124422 0 336974675322679468203539269571439868209 56664323533183020834598474906965745140 -99641177107819237055940644169179810357708227101112995191006535542547881424189 0 176948700035946127572922837533619941208 116086607304093585021818521383072452187 -57786446020611608012812377796223714878123336362492829538615405329313910757275 0 384800491534406380059844565570981772222 52266986398002287433031888862791991912 -3997314366767485810536545892291469074475439063264066213159323908499425387885 0 163725879096128676036352682652791025333 226192662661660937487283376658070867993 -67615980243338399738933073436777111073055676323119013192447912286335308137561 1 374683121854576184123882418807855041884 16460998928311049120615301499425467046 -95056188134586701650731142730682390276180906522232386805776875227233931192257 0 182406152787653624920535597648223675130 321349256573358626023271328220600865885 -29303255512942236056559307424251563011898396583247137160713909543128925925427 0 394355598494679314906318546129282608566 21331184493823760431200200045312958317 -106095072816802880785446250517258359371356549964859346325751724793738001008422 0 227220012957687545864426229191667293600 246770254904721260287425808259102883326 -36663409390673276751483064413776972596349494023383074980924833849240715914985 0 230187743392050124167268335285107339099 113059593568162008643646418169617685921 -82771506453137383921805646090184316226373521296248698278472145852657812654973 0 448680297043662761128367615186030747453 262213197051601850186326211611243750630 -63961727889177597433433858730561143931667179944225767027103327736231065696099 0 383046246971869687678886265006291417773 176507028690258621063089573000178683886 -78958182741591209735566019555046167360622345655285025222267245540000693237686 0 307912291337105562139537119019669321441 205848874805968588248301686005915074358 -29992541544181020685567466715431424759364607099134658865493940770134575976913 0 313333525502439697557329124795940920692 119522579582707356541133016141177173429 -55996337402705502978149476294220833964476263135957787076213671287801218036407 0 248653658166754470467954009769673979237 276357143630952130131740319087086825142 -97388654595383098119323361968512505408880391182023251416914044507619796828952 0 129607295120933934361947354739649396253 348702844153420909874274280858560595705 -103684421200131084868193986299612363056150648084335897887918260497897369235090 0 137988002724012727477191872780034713743 281923115583615970905143589103674297341 -51903898411245971311002978993768156073831050322488428489494694776389241427257 0 262623709047670399028123774298754795224 31207003832941590720457219524494852428 -58425740812556224150592989975848513144000880020796014870836565112334653910969 0 68158486446232977754554838151530747723 103012829212283472090535513492564260332 -37796766811528874602749994484146268556661763818946409615191822134163010896195 0 156162579596819770844738799408305517568 216013367627807166468937156952120714774 -104673787754110794527069099437830027691927980594776028872356770838068203806260 0 180507395316923827970381624873078371023 178646113611377959474138110551958431743 -45049215919159377471044030910333276930460202953886997427951830354892876391750 0 289076163967625382925306621033964902295 275857644218806224885962811288944950047 -63380093078641019264279595299599687418919536736701706690586662050919186274038 0 399343751961570017203410924552463623568 214629550774181167492620640131657432952 -113574786881534700522953391206121360810474649833543795441469078984157833695331 0 337585385764445108631145233545574984716 111193763139435755487945169649411985164 -49790013599705917814124073220601751501094132229628439049642413761065435294261 0 133366839688672057327444255549371842134 154705106335188742425870712888796851785 -64788086314106700834083217375850254865242934978855596692209349780129318378744 0 208048087191243283921553562292224008573 167962365335602158241904574503681973259 -21491352853103006049432930470687105869438626598819647480078412200076780080071 0 332927058661660714688841373072172537860 250843627315496890002575084365967548370 -32545348251896241054810690816139751919948032476162876085055732154364864057131 0 316420089474027539413130854556009781339 270726459303085467721289162826279737461 -31996583464016056235483983406448600381643612958093441920343439838927133445573 0 29765666218398212724673810874306110546 53399747936093672030894874965364064994 -89615799918538749381458729851193528246397510676297075907981857435559071071168 0 295961493720216429081105109644726719164 166422862787022480873353396305163229140 -15099232009191703557558812339937048250095387722489276214956414252433682422949 0 41556871202295228747165988589465572779 18694763296781371373688519867172104072 -57782406756368925624499515233394410310678496338615394643584084829055171163362 0 323173423073911508486605108651109896446 275664965293871722614277067423096860210 -83687148390003254520718432275833361406493655534410485533465629909784485351645 0 141707333321176834248589538053969047926 105093052280280330880420790614797103764 -94092632829203668064644865473590683890347120881122980873059859591305224541350 0 173463607522614220455498884551679162201 110097298199074074423678389142644523679 -80090736820260360487524344579373063292049864365463485605145955787985831409649 0 331405388022832422419399109218815665369 216771901726439591270277310542827853977 -4535777321770763218606323711246921035408569985909271950592300354642976197724 0 186391731486184025423142954209585423742 284613554603792609269913086961998880737 -30349365459394789718698194480160894678149070522952896486375773030035845255292 0 374909122453556624374459507677331897405 180673515562524763308695824558468012619 -72161671505952573796585894492305379034527876152238180794487819996016895314734 0 237579250085222191217791969107501240781 270701579459201000027844273593745256308 -48235355097859320774568350607756012532791518095832519070257073273821652387881 0 118852566568104430033747992761543500660 252476177183388145123352883841457568048 -43458547309016996164900737966450444092259120892532867520800108971738481241089 1 229858288423301440725886092157390239384 9465475033813460502057314905576362790 -87707849061176806397783023083472930256635791245553973448536304191247865297995 0 420158629397090655290956352806019948850 206730737632167395841059566861837480242 -32484531001265918515785349406978341204499124194739421246274581141032317566767 0 195662044073880514719905180740973104082 253767289227219312208752180951529687412 diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs deleted file mode 100644 index 28d840f85a..0000000000 --- a/evm/src/cpu/kernel/tests/exp.rs +++ /dev/null @@ -1,43 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::{run_interpreter, Interpreter}; - -#[test] -fn test_exp() -> Result<()> { - // Make sure we can parse and assemble the entire kernel. - let exp = KERNEL.global_labels["exp"]; - let mut rng = thread_rng(); - let a = U256([0; 4].map(|_| rng.gen())); - let b = U256([0; 4].map(|_| rng.gen())); - - // Random input - let initial_stack = vec![0xDEADBEEFu32.into(), b, a]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack.clone()); - - let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); - - let expected_exp = a.overflowing_pow(b).0; - assert_eq!(stack_with_kernel, vec![expected_exp]); - - // 0 base - let initial_stack = vec![0xDEADBEEFu32.into(), b, U256::zero()]; - let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); - - let expected_exp = U256::zero().overflowing_pow(b).0; - assert_eq!(stack_with_kernel, vec![expected_exp]); - - // 0 exponent - let initial_stack = vec![0xDEADBEEFu32.into(), U256::zero(), a]; - interpreter.set_is_kernel(true); - interpreter.set_context(0); - let stack_with_kernel = run_interpreter::(exp, initial_stack)?.stack(); - - let expected_exp = 1.into(); - assert_eq!(stack_with_kernel, vec![expected_exp]); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/hash.rs b/evm/src/cpu/kernel/tests/hash.rs deleted file mode 100644 index 672aa5d1ad..0000000000 --- a/evm/src/cpu/kernel/tests/hash.rs +++ /dev/null @@ -1,137 +0,0 @@ -use anyhow::Result; -// use blake2::Blake2b512; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; -use ripemd::{Digest, Ripemd160}; -use sha2::Sha256; - -use crate::cpu::kernel::interpreter::{ - run_interpreter_with_memory, Interpreter, InterpreterMemoryInitialization, -}; -use crate::memory::segments::Segment::KernelGeneral; - -/// Standard RipeMD implementation. -fn ripemd(input: Vec) -> U256 { - let mut hasher = Ripemd160::new(); - hasher.update(input); - U256::from(&hasher.finalize()[..]) -} - -/// Standard Sha2 implementation. -fn sha2(input: Vec) -> U256 { - let mut hasher = Sha256::new(); - hasher.update(input); - U256::from(&hasher.finalize()[..]) -} - -fn make_random_input() -> Vec { - // Generate a random message, between 0 and 9999 bytes. - let mut rng = thread_rng(); - let num_bytes = rng.gen_range(0..10000); - (0..num_bytes).map(|_| rng.gen()).collect() -} - -fn make_interpreter_setup( - message: Vec, - hash_fn_label: &str, - hash_input_virt: (usize, usize), -) -> InterpreterMemoryInitialization { - InterpreterMemoryInitialization { - label: hash_fn_label.to_string(), - stack: vec![ - U256::from(hash_input_virt.0), - U256::from(message.len()), - U256::from(0xdeadbeefu32), - ], - segment: KernelGeneral, - memory: vec![( - hash_input_virt.1, - message.iter().map(|&x| U256::from(x as u32)).collect(), - )], - } -} - -fn prepare_test( - hash_fn_label: &str, - hash_input_virt: (usize, usize), - standard_implementation: &dyn Fn(Vec) -> T, -) -> Result<(T, Vec)> { - // Make the input. - let message = make_random_input(); - - // Hash the message using a standard implementation. - let expected = standard_implementation(message.clone()); - - // Load the message into the kernel. - let interpreter_setup = make_interpreter_setup(message, hash_fn_label, hash_input_virt); - - // Run the interpreter - let result: Interpreter = run_interpreter_with_memory(interpreter_setup).unwrap(); - - Ok((expected, result.stack().to_vec())) -} - -fn test_hash_256( - hash_fn_label: &str, - hash_input_virt: (usize, usize), - standard_implementation: &dyn Fn(Vec) -> U256, -) -> Result<()> { - let (expected, result_stack) = - prepare_test(hash_fn_label, hash_input_virt, standard_implementation).unwrap(); - - // Extract the final output. - let actual = result_stack[0]; - - // Check that the result is correct. - assert_eq!(expected, actual); - - Ok(()) -} - -#[test] -fn test_ripemd() -> Result<()> { - test_hash_256("ripemd", (200, 200), &ripemd) -} - -#[test] -fn test_sha2() -> Result<()> { - test_hash_256("sha2", (0, 1), &sha2) -} - -// Since the Blake precompile requires only the blake2_f compression function instead of the full blake2b hash, -// the full hash function is not included in the kernel. To include it, blake2/compression.asm and blake2/main.asm -// must be added to the kernel. - -// /// Standard Blake2b implementation. -// fn blake2b(input: Vec) -> U512 { -// let mut hasher = Blake2b512::new(); -// hasher.update(input); -// U512::from(&hasher.finalize()[..]) -// } - -// fn combine_u256s(hi: U256, lo: U256) -> U512 { -// U512::from(lo) + (U512::from(hi) << 256) -// } - -// fn test_hash_512( -// hash_fn_label: &str, -// hash_input_virt: (usize, usize), -// standard_implementation: &dyn Fn(Vec) -> U512, -// ) -> Result<()> { -// let (expected, result_stack) = -// prepare_test(hash_fn_label, hash_input_virt, standard_implementation).unwrap(); - -// // Extract the final output. -// let actual = combine_u256s(result_stack[0], result_stack[1]); - -// // Check that the result is correct. -// assert_eq!(expected, actual); - -// Ok(()) -// } - -// #[test] -// fn test_blake2b() -> Result<()> { -// test_hash_512("blake2b", (0, 2), &blake2b) -// } diff --git a/evm/src/cpu/kernel/tests/kernel_consistency.rs b/evm/src/cpu/kernel/tests/kernel_consistency.rs deleted file mode 100644 index b02c11a234..0000000000 --- a/evm/src/cpu/kernel/tests/kernel_consistency.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anyhow::Result; - -use crate::cpu::kernel::aggregator::{combined_kernel, KERNEL}; - -#[test] -fn test_kernel_code_hash_consistency() -> Result<()> { - for _ in 0..10 { - let kernel2 = combined_kernel(); - assert_eq!(kernel2.code_hash, KERNEL.code_hash); - } - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/log.rs b/evm/src/cpu/kernel/tests/log.rs deleted file mode 100644 index 9c80b42614..0000000000 --- a/evm/src/cpu/kernel/tests/log.rs +++ /dev/null @@ -1,199 +0,0 @@ -use anyhow::Result; -use ethereum_types::{Address, U256}; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn test_log_0() -> Result<()> { - let logs_entry = KERNEL.global_labels["log_n_entry"]; - let address: Address = thread_rng().gen(); - let num_topics = U256::from(0); - let data_len = U256::from(0); - let data_offset = U256::from(0); - - let retdest = 0xDEADBEEFu32.into(); - - let initial_stack = vec![ - retdest, - data_offset, - data_len, - num_topics, - U256::from_big_endian(&address.to_fixed_bytes()), - ]; - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 0.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 0.into()); - - interpreter.run()?; - - // The address is encoded in 1+20 bytes. There are no topics or data, so each is encoded in 1 byte. This leads to a payload of 23. - let payload_len = 23; - assert_eq!( - interpreter.get_memory_segment(Segment::LogsData), - [ - payload_len.into(), - U256::from_big_endian(&address.to_fixed_bytes()), - 0.into(), - 0.into(), - ] - ); - Ok(()) -} - -#[test] -fn test_log_2() -> Result<()> { - let logs_entry = KERNEL.global_labels["log_n_entry"]; - let address: Address = thread_rng().gen(); - let num_topics = U256::from(2); - let topics = [4.into(), 5.into()]; - let data_len = U256::from(3); - let data_offset = U256::from(0); - - let memory = vec![10.into(), 20.into(), 30.into()]; - - let retdest = 0xDEADBEEFu32.into(); - - let initial_stack = vec![ - retdest, - data_offset, - data_len, - topics[1], - topics[0], - num_topics, - U256::from_big_endian(&address.to_fixed_bytes()), - ]; - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 2.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 5.into()); - - interpreter.set_memory_segment(Segment::MainMemory, memory); - - interpreter.run()?; - assert_eq!( - interpreter.get_memory_segment(Segment::Logs), - [0.into(), 0.into(), 5.into(),] - ); - - // The data has length 3 bytes, and is encoded in 4 bytes. Each of the two topics is encoded in 1+32 bytes. The prefix for the topics list requires 2 bytes. The address is encoded in 1+20 bytes. Overall, we have a logs payload length of 93 bytes. - let payload_len = 93; - assert_eq!( - interpreter.get_memory_segment(Segment::LogsData), - [ - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - payload_len.into(), - U256::from_big_endian(&address.to_fixed_bytes()), - 2.into(), - 4.into(), - 5.into(), - 3.into(), - 10.into(), - 20.into(), - 30.into(), - ] - ); - Ok(()) -} - -#[test] -fn test_log_4() -> Result<()> { - let logs_entry = KERNEL.global_labels["log_n_entry"]; - let address: Address = thread_rng().gen(); - let num_topics = U256::from(4); - let topics = [45.into(), 46.into(), 47.into(), 48.into()]; - let data_len = U256::from(1); - let data_offset = U256::from(2); - - let memory = vec![0.into(), 0.into(), 123.into()]; - - let retdest = 0xDEADBEEFu32.into(); - - let initial_stack = vec![ - retdest, - data_offset, - data_len, - topics[3], - topics[2], - topics[1], - topics[0], - num_topics, - U256::from_big_endian(&address.to_fixed_bytes()), - ]; - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 2.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 5.into()); - - interpreter.set_memory_segment(Segment::MainMemory, memory); - - interpreter.run()?; - assert_eq!( - interpreter.get_memory_segment(Segment::Logs), - [0.into(), 0.into(), 5.into(),] - ); - - // The data is of length 1 byte, and is encoded in 1 byte. Each of the four topics is encoded in 1+32 bytes. The topics list is prefixed by 2 bytes. The address is encoded in 1+20 bytes. Overall, this leads to a log payload length of 156. - let payload_len = 156; - assert_eq!( - interpreter.get_memory_segment(Segment::LogsData), - [ - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - payload_len.into(), - U256::from_big_endian(&address.to_fixed_bytes()), - 4.into(), - 45.into(), - 46.into(), - 47.into(), - 48.into(), - 1.into(), - 123.into(), - ] - ); - Ok(()) -} - -#[test] -fn test_log_5() -> Result<()> { - let logs_entry = KERNEL.global_labels["log_n_entry"]; - let address: Address = thread_rng().gen(); - let num_topics = U256::from(5); - let topics = [1.into(), 2.into(), 3.into(), 4.into(), 5.into()]; - let data_len = U256::from(0); - let data_offset = U256::from(0); - - let retdest = 0xDEADBEEFu32.into(); - - let initial_stack = vec![ - retdest, - data_offset, - data_len, - topics[4], - topics[3], - topics[2], - topics[1], - topics[0], - num_topics, - U256::from_big_endian(&address.to_fixed_bytes()), - ]; - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_entry, initial_stack); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 0.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsDataLen, 0.into()); - - assert!(interpreter.run().is_err()); - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs deleted file mode 100644 index 7581eefe75..0000000000 --- a/evm/src/cpu/kernel/tests/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -mod account_code; -mod add11; -mod balance; -mod bignum; -mod blake2_f; -mod block_hash; -mod bls381; -mod bn254; -mod core; -mod ecc; -mod exp; -mod hash; -mod kernel_consistency; -mod log; -mod mpt; -mod packing; -mod receipt; -mod rlp; -mod signed_syscalls; -mod transaction_parsing; - -use std::str::FromStr; - -use anyhow::Result; -use ethereum_types::U256; - -pub(crate) fn u256ify<'a>(hexes: impl IntoIterator) -> Result> { - Ok(hexes - .into_iter() - .map(U256::from_str) - .collect::, _>>()?) -} diff --git a/evm/src/cpu/kernel/tests/mpt/delete.rs b/evm/src/cpu/kernel/tests/mpt/delete.rs deleted file mode 100644 index 34bc0d66ba..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/delete.rs +++ /dev/null @@ -1,177 +0,0 @@ -use anyhow::Result; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{BigEndianHash, H256, U512}; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::random; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{nibbles_64, test_account_1_rlp, test_account_2}; -use crate::generation::mpt::AccountRlp; -use crate::generation::TrieInputs; -use crate::Node; - -#[test] -fn mpt_delete_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) -} - -#[test] -fn mpt_delete_leaf_nonoverlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0x123), test_account_2()) -} - -#[test] -fn mpt_delete_leaf_overlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -fn mpt_delete_branch_into_hash() -> Result<()> { - let hash = Node::Hash(H256::random()); - let state_trie = Node::Extension { - nibbles: nibbles_64(0xADF), - child: hash.into(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -fn test_after_mpt_delete_extension_branch() -> Result<()> { - let hash = Node::Hash(H256::random()); - let branch = Node::Branch { - children: std::array::from_fn(|i| { - if i == 0 { - Node::Empty.into() - } else { - hash.clone().into() - } - }), - value: vec![], - }; - let nibbles = Nibbles::from_bytes_be(&random::<[u8; 5]>()).unwrap(); - let state_trie = Node::Extension { - nibbles, - child: branch.into(), - } - .into(); - let key = nibbles.merge_nibbles(&Nibbles { - packed: U512::zero(), - count: 64 - nibbles.count, - }); - test_state_trie(state_trie, key, test_account_2()) -} - -/// Note: The account's storage_root is ignored, as we can't insert a new storage_root without the -/// accompanying trie data. An empty trie's storage_root is used instead. -fn test_state_trie( - state_trie: HashedPartialTrie, - k: Nibbles, - mut account: AccountRlp, -) -> Result<()> { - assert_eq!(k.count, 64); - - // Ignore any storage_root; see documentation note. - account.storage_root = HashedPartialTrie::from(Node::Empty).hash(); - - let trie_inputs = TrieInputs { - state_trie: state_trie.clone(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_delete = KERNEL.global_labels["mpt_delete"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; - let trie_data = interpreter.get_trie_data_mut(); - if trie_data.is_empty() { - // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. - // Since we don't explicitly set it to 0, we need to do so here. - trie_data.push(0.into()); - } - let value_ptr = trie_data.len(); - trie_data.push(account.nonce); - trie_data.push(account.balance); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(H256::zero().into_uint()); - trie_data.push(account.code_hash.into_uint()); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into_u256().unwrap()) - .expect("The stack should not overflow"); // key - interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 0, - "Expected empty stack after insert, found {:?}", - interpreter.stack() - ); - - // Next, execute mpt_delete, deleting the account we just inserted. - let state_trie_ptr = interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot); - interpreter.generation_state.registers.program_counter = mpt_delete; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(k.try_into_u256().unwrap()) - .expect("The stack should not overflow"); - interpreter - .push(64.into()) - .expect("The stack should not overflow"); - interpreter - .push(state_trie_ptr) - .expect("The stack should not overflow"); - interpreter.run()?; - let state_trie_ptr = interpreter.pop().expect("The stack should not be empty"); - interpreter.set_global_metadata_field(GlobalMetadata::StateTrieRoot, state_trie_ptr); - - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - let state_trie_hash = - H256::from_uint(&interpreter.pop().expect("The stack should not be empty")); - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(state_trie_hash, expected_state_trie_hash); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mpt/hash.rs b/evm/src/cpu/kernel/tests/mpt/hash.rs deleted file mode 100644 index e9a7ebde86..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/hash.rs +++ /dev/null @@ -1,141 +0,0 @@ -use anyhow::Result; -use eth_trie_utils::partial_trie::PartialTrie; -use ethereum_types::{BigEndianHash, H256}; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1_rlp, test_account_2_rlp}; -use crate::generation::TrieInputs; -use crate::Node; - -// TODO: Test with short leaf. Might need to be a storage trie. - -#[test] -fn mpt_hash_empty() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: Default::default(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_empty_branch() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_hash() -> Result<()> { - let hash = H256::random(); - let trie_inputs = TrieInputs { - state_trie: Node::Hash(hash).into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_leaf() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_extension_to_leaf() -> Result<()> { - let state_trie = extension_to_leaf(test_account_1_rlp()); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_branch_to_leaf() -> Result<()> { - let leaf = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_2_rlp(), - } - .into(); - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[3] = leaf; - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - test_state_trie(trie_inputs) -} - -fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack, found {:?}", - interpreter.stack() - ); - let hash = H256::from_uint(&interpreter.stack()[1]); - let expected_state_trie_hash = trie_inputs.state_trie.hash(); - assert_eq!(hash, expected_state_trie_hash); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs deleted file mode 100644 index 37077e4022..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs +++ /dev/null @@ -1,93 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn hex_prefix_even_nonterminated() -> Result<()> { - let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; - - let retdest = 0xDEADBEEFu32.into(); - let terminated = 0.into(); - let packed_nibbles = 0xABCDEF.into(); - let num_nibbles = 6.into(); - let rlp_pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(5)]); - - assert_eq!( - interpreter.get_rlp_memory(), - vec![ - 0x80 + 4, // prefix - 0, // neither flag is set - 0xAB, - 0xCD, - 0xEF - ] - ); - - Ok(()) -} - -#[test] -fn hex_prefix_odd_terminated() -> Result<()> { - let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; - - let retdest = 0xDEADBEEFu32.into(); - let terminated = 1.into(); - let packed_nibbles = 0xABCDE.into(); - let num_nibbles = 5.into(); - let rlp_pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(4)]); - - assert_eq!( - interpreter.get_rlp_memory(), - vec![ - 0x80 + 3, // prefix - (2 + 1) * 16 + 0xA, - 0xBC, - 0xDE, - ] - ); - - Ok(()) -} - -#[test] -fn hex_prefix_odd_terminated_tiny() -> Result<()> { - let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; - - let retdest = 0xDEADBEEFu32.into(); - let terminated = 1.into(); - let packed_nibbles = 0xA.into(); - let num_nibbles = 1.into(); - let rlp_pos = U256::from(Segment::RlpRaw as usize + 2); - let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); - interpreter.run()?; - assert_eq!( - interpreter.stack(), - vec![U256::from(Segment::RlpRaw as usize + 3)] - ); - - assert_eq!( - interpreter.get_rlp_memory(), - vec![ - // Since rlp_pos = 2, we skipped over the first two bytes. - 0, - 0, - // No length prefix; this tiny string is its own RLP encoding. - (2 + 1) * 16 + 0xA, - ] - ); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs deleted file mode 100644 index cbb13b9b40..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ /dev/null @@ -1,241 +0,0 @@ -use anyhow::Result; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{BigEndianHash, H256}; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{ - nibbles_64, nibbles_count, test_account_1_rlp, test_account_2, -}; -use crate::generation::mpt::AccountRlp; -use crate::generation::TrieInputs; -use crate::Node; - -#[test] -fn mpt_insert_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) -} - -#[test] -fn mpt_insert_leaf_identical_keys() -> Result<()> { - let key = nibbles_64(0xABC); - let state_trie = Node::Leaf { - nibbles: key, - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, key, test_account_2()) -} - -#[test] -fn mpt_insert_leaf_nonoverlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0x123), test_account_2()) -} - -#[test] -fn mpt_insert_leaf_overlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -#[ignore] // TODO: Not valid for state trie, all keys have same len. -fn mpt_insert_leaf_insert_key_extends_leaf_key() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABCDE), test_account_2()) -} - -#[test] -#[ignore] // TODO: Not valid for state trie, all keys have same len. -fn mpt_insert_leaf_leaf_key_extends_insert_key() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABCDE_u64.into(), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABC), test_account_2()) -} - -#[test] -fn mpt_insert_branch_replacing_empty_child() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - test_state_trie(state_trie, nibbles_64(0xABC), test_account_2()) -} - -#[test] -// TODO: Not a valid test because branches state trie cannot have branch values. -// We should change it to use a different trie. -#[ignore] -fn mpt_insert_extension_nonoverlapping_keys() -> Result<()> { - // Existing keys are 0xABC, 0xABCDEF; inserted key is 0x12345. - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0xD] = Node::Leaf { - nibbles: 0xEF_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let state_trie = Node::Extension { - nibbles: 0xABC_u64.into(), - child: Node::Branch { - children, - value: test_account_1_rlp(), - } - .into(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0x12345), test_account_2()) -} - -#[test] -// TODO: Not a valid test because branches state trie cannot have branch values. -// We should change it to use a different trie. -#[ignore] -fn mpt_insert_extension_insert_key_extends_node_key() -> Result<()> { - // Existing keys are 0xA, 0xABCD; inserted key is 0xABCDEF. - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0xB] = Node::Leaf { - nibbles: 0xCD_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let state_trie = Node::Extension { - nibbles: 0xA_u64.into(), - child: Node::Branch { - children, - value: test_account_1_rlp(), - } - .into(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABCDEF), test_account_2()) -} - -#[test] -fn mpt_insert_branch_to_leaf_same_key() -> Result<()> { - let leaf = Node::Leaf { - nibbles: nibbles_count(0xBCD, 63), - value: test_account_1_rlp(), - } - .into(); - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0] = leaf; - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - test_state_trie(state_trie, nibbles_64(0xABCD), test_account_2()) -} - -/// Note: The account's storage_root is ignored, as we can't insert a new storage_root without the -/// accompanying trie data. An empty trie's storage_root is used instead. -fn test_state_trie( - mut state_trie: HashedPartialTrie, - k: Nibbles, - mut account: AccountRlp, -) -> Result<()> { - assert_eq!(k.count, 64); - - // Ignore any storage_root; see documentation note. - account.storage_root = HashedPartialTrie::from(Node::Empty).hash(); - - let trie_inputs = TrieInputs { - state_trie: state_trie.clone(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; - let trie_data = interpreter.get_trie_data_mut(); - if trie_data.is_empty() { - // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. - // Since we don't explicitly set it to 0, we need to do so here. - trie_data.push(0.into()); - } - let value_ptr = trie_data.len(); - trie_data.push(account.nonce); - trie_data.push(account.balance); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(H256::zero().into_uint()); - trie_data.push(account.code_hash.into_uint()); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into_u256().unwrap()) - .expect("The stack should not overflow"); // key - - interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 0, - "Expected empty stack after insert, found {:?}", - interpreter.stack() - ); - - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - let hash = H256::from_uint(&interpreter.stack()[1]); - - state_trie.insert(k, rlp::encode(&account).to_vec()); - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(hash, expected_state_trie_hash); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs deleted file mode 100644 index 85c023f090..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::HashedPartialTrie; -use ethereum_types::{BigEndianHash, H256, U256}; -use hex_literal::hex; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::constants::trie_type::PartialTrieType; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; -use crate::generation::TrieInputs; -use crate::Node; - -#[test] -fn load_all_mpts_empty() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: Default::default(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // We need to have the first element in `TrieData` be 0. - assert_eq!(interpreter.get_trie_data(), vec![0.into()]); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_leaf() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_leaf = U256::from(PartialTrieType::Leaf as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![ - 0.into(), - type_leaf, - 3.into(), - 0xABC.into(), - 5.into(), // value ptr - test_account_1().nonce, - test_account_1().balance, - 9.into(), // pointer to storage trie root - test_account_1().code_hash.into_uint(), - // These last two elements encode the storage trie, which is a hash node. - (PartialTrieType::Hash as u32).into(), - test_account_1().storage_root.into_uint(), - ] - ); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_hash() -> Result<()> { - let hash = H256::random(); - let trie_inputs = TrieInputs { - state_trie: Node::Hash(hash).into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_hash = U256::from(PartialTrieType::Hash as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![0.into(), type_hash, hash.into_uint(),] - ); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_empty_branch() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_branch = U256::from(PartialTrieType::Branch as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![ - 0.into(), // First address is unused, so that 0 can be treated as a null pointer. - type_branch, - 0.into(), // child 0 - 0.into(), // ... - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), // child 16 - 0.into(), // value_ptr - ] - ); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_ext_to_leaf() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: extension_to_leaf(test_account_1_rlp()), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_extension = U256::from(PartialTrieType::Extension as u32); - let type_leaf = U256::from(PartialTrieType::Leaf as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![ - 0.into(), // First address is unused, so that 0 can be treated as a null pointer. - type_extension, - 3.into(), // 3 nibbles - 0xABC.into(), // key part - 5.into(), // Pointer to the leaf node immediately below. - type_leaf, - 3.into(), // 3 nibbles - 0xDEF.into(), // key part - 9.into(), // value pointer - test_account_1().nonce, - test_account_1().balance, - 13.into(), // pointer to storage trie root - test_account_1().code_hash.into_uint(), - // These last two elements encode the storage trie, which is a hash node. - (PartialTrieType::Hash as u32).into(), - test_account_1().storage_root.into_uint(), - ] - ); - - Ok(()) -} - -#[test] -fn load_mpt_txn_trie() -> Result<()> { - let txn = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b").to_vec(); - - let trie_inputs = TrieInputs { - state_trie: Default::default(), - transactions_trie: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.clone(), - }), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let mut expected_trie_data = vec![ - 0.into(), - U256::from(PartialTrieType::Leaf as u32), - 2.into(), - 128.into(), // Nibble - 5.into(), // value_ptr - txn.len().into(), - ]; - expected_trie_data.extend(txn.into_iter().map(U256::from)); - let trie_data = interpreter.get_trie_data(); - - assert_eq!(trie_data, expected_trie_data); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/mpt/mod.rs b/evm/src/cpu/kernel/tests/mpt/mod.rs deleted file mode 100644 index 292d064af1..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::HashedPartialTrie; -use ethereum_types::{BigEndianHash, H256, U256}; - -use crate::generation::mpt::AccountRlp; -use crate::Node; - -mod delete; -mod hash; -mod hex_prefix; -mod insert; -mod load; -mod read; - -pub(crate) fn nibbles_64>(v: T) -> Nibbles { - let packed: U256 = v.into(); - Nibbles { - count: 64, - packed: packed.into(), - } -} - -pub(crate) fn nibbles_count>(v: T, count: usize) -> Nibbles { - let packed: U256 = v.into(); - Nibbles { - count, - packed: packed.into(), - } -} - -pub(crate) fn test_account_1() -> AccountRlp { - AccountRlp { - nonce: U256::from(1111), - balance: U256::from(2222), - storage_root: H256::from_uint(&U256::from(3333)), - code_hash: H256::from_uint(&U256::from(4444)), - } -} - -pub(crate) fn test_account_1_rlp() -> Vec { - rlp::encode(&test_account_1()).to_vec() -} - -pub(crate) fn test_account_2() -> AccountRlp { - AccountRlp { - nonce: U256::from(5555), - balance: U256::from(6666), - storage_root: H256::from_uint(&U256::from(7777)), - code_hash: H256::from_uint(&U256::from(8888)), - } -} - -pub(crate) fn test_account_2_rlp() -> Vec { - rlp::encode(&test_account_2()).to_vec() -} - -/// A `PartialTrie` where an extension node leads to a leaf node containing an account. -pub(crate) fn extension_to_leaf(value: Vec) -> HashedPartialTrie { - Node::Extension { - nibbles: 0xABC_u64.into(), - child: Node::Leaf { - nibbles: Nibbles { - count: 3, - packed: 0xDEF.into(), - }, - value, - } - .into(), - } - .into() -} diff --git a/evm/src/cpu/kernel/tests/mpt/read.rs b/evm/src/cpu/kernel/tests/mpt/read.rs deleted file mode 100644 index a86bab85bf..0000000000 --- a/evm/src/cpu/kernel/tests/mpt/read.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::Result; -use ethereum_types::BigEndianHash; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; -use crate::generation::TrieInputs; - -#[test] -fn mpt_read() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: extension_to_leaf(test_account_1_rlp()), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let mpt_read = KERNEL.global_labels["mpt_read"]; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - // Now, execute mpt_read on the state trie. - interpreter.generation_state.registers.program_counter = mpt_read; - interpreter - .push(0xdeadbeefu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(0xABCDEFu64.into()) - .expect("The stack should not overflow"); - interpreter - .push(6.into()) - .expect("The stack should not overflow"); - interpreter - .push(interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot)) - .expect("The stack should not overflow"); - interpreter.run()?; - - assert_eq!(interpreter.stack().len(), 1); - let result_ptr = interpreter.stack()[0].as_usize(); - let result = &interpreter.get_trie_data()[result_ptr..][..4]; - assert_eq!(result[0], test_account_1().nonce); - assert_eq!(result[1], test_account_1().balance); - // result[2] is the storage root pointer. We won't check that it matches a - // particular address, since that seems like over-specifying. - assert_eq!(result[3], test_account_1().code_hash.into_uint()); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs deleted file mode 100644 index ba72f658a2..0000000000 --- a/evm/src/cpu/kernel/tests/packing.rs +++ /dev/null @@ -1,30 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn test_mstore_unpacking() -> Result<()> { - let mstore_unpacking = KERNEL.global_labels["mstore_unpacking"]; - - let retdest = 0xDEADBEEFu32.into(); - let len = 4.into(); - let value = 0xABCD1234u32.into(); - let addr = (Segment::TxnData as u64).into(); - let initial_stack = vec![retdest, len, value, addr]; - - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(mstore_unpacking, initial_stack); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![addr + U256::from(4)]); - assert_eq!( - &interpreter.get_txn_data(), - &[0xAB.into(), 0xCD.into(), 0x12.into(), 0x34.into()] - ); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/receipt.rs b/evm/src/cpu/kernel/tests/receipt.rs deleted file mode 100644 index cf9f63896e..0000000000 --- a/evm/src/cpu/kernel/tests/receipt.rs +++ /dev/null @@ -1,613 +0,0 @@ -use anyhow::Result; -use ethereum_types::{Address, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::{thread_rng, Rng}; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::generation::mpt::{LegacyReceiptRlp, LogRlp}; -use crate::memory::segments::Segment; - -#[test] -fn test_process_receipt() -> Result<()> { - /* Tests process_receipt, which: - - computes the cumulative gas - - computes the bloom filter - - inserts the receipt data in MPT_TRIE_DATA - - inserts a node in receipt_trie - - resets the bloom filter to 0 for the next transaction. */ - let process_receipt = KERNEL.global_labels["process_receipt"]; - let success = U256::from(1); - let leftover_gas = U256::from(4000); - let prev_cum_gas = U256::from(1000); - let retdest = 0xDEADBEEFu32.into(); - - // Log. - let address: Address = thread_rng().gen(); - let num_topics = 1; - - let mut topic = vec![0_u8; 32]; - topic[31] = 4; - - // Compute the expected Bloom filter. - let test_logs_list = vec![(address.to_fixed_bytes().to_vec(), vec![topic])]; - let expected_bloom = logs_bloom_bytes_fn(test_logs_list).to_vec(); - - // Set memory. - let num_nibbles = 2.into(); - let initial_stack: Vec = vec![ - retdest, - num_nibbles, - 0.into(), - prev_cum_gas, - leftover_gas, - success, - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(process_receipt, initial_stack); - interpreter.set_memory_segment( - Segment::LogsData, - vec![ - 56.into(), // payload len - U256::from_big_endian(&address.to_fixed_bytes()), // address - num_topics.into(), // num_topics - 4.into(), // topic - 0.into(), // data_len - ], - ); - interpreter.set_txn_field(NormalizedTxnField::GasLimit, U256::from(5000)); - interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); - interpreter.set_memory_segment(Segment::Logs, vec![0.into()]); - interpreter.set_global_metadata_field(GlobalMetadata::LogsPayloadLen, 58.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, U256::from(1)); - interpreter.set_global_metadata_field(GlobalMetadata::ReceiptTrieRoot, 500.into()); - interpreter.run()?; - - let segment_read = interpreter.get_memory_segment(Segment::TrieData); - - // The expected TrieData has the form [payload_len, status, cum_gas_used, bloom_filter, logs_payload_len, num_logs, [logs]] - let mut expected_trie_data: Vec = vec![323.into(), success, 2000.into()]; - expected_trie_data.extend( - expected_bloom - .into_iter() - .map(|elt| elt.into()) - .collect::>(), - ); - expected_trie_data.push(58.into()); // logs_payload_len - expected_trie_data.push(1.into()); // num_logs - expected_trie_data.extend(vec![ - 56.into(), // payload len - U256::from_big_endian(&address.to_fixed_bytes()), // address - num_topics.into(), // num_topics - 4.into(), // topic - 0.into(), // data_len - ]); - - assert_eq!( - expected_trie_data, - segment_read[0..expected_trie_data.len()] - ); - - Ok(()) -} - -/// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000 -#[test] -fn test_receipt_encoding() -> Result<()> { - // Initialize interpreter. - let success = U256::from(1); - - let retdest = 0xDEADBEEFu32.into(); - let num_topics = 3; - - let encode_receipt = KERNEL.global_labels["encode_receipt"]; - - // Logs and receipt in encodable form. - let log_1 = LogRlp { - address: hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into(), - topics: vec![ - hex!("8a22ee899102a366ac8ad0495127319cb1ff2403cfae855f83a89cda1266674d").into(), - hex!("0000000000000000000000000000000000000000000000000000000000000004").into(), - hex!("00000000000000000000000000000000000000000000000000000000004920ea").into(), - ], - data: hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .to_vec() - .into(), - }; - - let receipt_1 = LegacyReceiptRlp { - status: true, - cum_gas_used: 0x02dcb6u64.into(), - bloom: hex!("00000000000000000000000000000000000000000000000000800000000000000040000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000008000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000400000000000000000000000000000002000040000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000008000000000000000000000000").to_vec().into(), - logs: vec![log_1], - }; - // Get the expected RLP encoding. - let expected_rlp = rlp::encode(&rlp::encode(&receipt_1)); - - let initial_stack: Vec = vec![retdest, 0.into(), 0.into(), 0.into()]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(encode_receipt, initial_stack); - - // Write data to memory. - let expected_bloom_bytes = vec![ - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x40, 00, 00, 00, 00, 0x10, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x02, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 0x40, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x20, 00, 0x04, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x08, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - ]; - let expected_bloom: Vec = expected_bloom_bytes - .into_iter() - .map(|elt| elt.into()) - .collect(); - - let addr = U256::from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7e, 0xf6, 0x6b, 0x77, 0x75, 0x9e, 0x12, 0xca, 0xf3, - 0xdd, 0xb3, 0xe4, 0xaf, 0xf5, 0x24, 0xe5, 0x77, 0xc5, 0x9d, 0x8d, - ]); - - let topic1 = U256::from([ - 0x8a, 0x22, 0xee, 0x89, 0x91, 0x02, 0xa3, 0x66, 0xac, 0x8a, 0xd0, 0x49, 0x51, 0x27, 0x31, - 0x9c, 0xb1, 0xff, 0x24, 0x03, 0xcf, 0xae, 0x85, 0x5f, 0x83, 0xa8, 0x9c, 0xda, 0x12, 0x66, - 0x67, 0x4d, - ]); - - let topic2 = 4.into(); - let topic3 = 0x4920ea.into(); - - let mut logs = vec![ - 155.into(), // unused - addr, - num_topics.into(), // num_topics - topic1, // topic1 - topic2, // topic2 - topic3, // topic3 - 32.into(), // data length - ]; - let cur_data = hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .iter() - .copied() - .map(U256::from); - logs.extend(cur_data); - - let mut receipt = vec![423.into(), success, receipt_1.cum_gas_used]; - receipt.extend(expected_bloom.clone()); - receipt.push(157.into()); // logs_payload_len - receipt.push(1.into()); // num_logs - receipt.extend(logs.clone()); - interpreter.set_memory_segment(Segment::LogsData, logs); - - interpreter.set_memory_segment(Segment::TxnBloom, expected_bloom); - - interpreter.set_memory_segment(Segment::Logs, vec![0.into()]); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, 1.into()); - interpreter.set_global_metadata_field(GlobalMetadata::LogsPayloadLen, 157.into()); - interpreter.set_memory_segment(Segment::TrieData, receipt); - - interpreter.run()?; - let rlp_pos = interpreter.pop().expect("The stack should not be empty"); - - let rlp_read: Vec = interpreter.get_rlp_memory(); - - assert_eq!(rlp_pos.as_usize(), expected_rlp.len()); - for i in 0..rlp_read.len() { - assert_eq!(rlp_read[i], expected_rlp[i]); - } - - Ok(()) -} - -/// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000 -#[test] -fn test_receipt_bloom_filter() -> Result<()> { - let logs_bloom = KERNEL.global_labels["logs_bloom"]; - - let num_topics = 3; - - // Expected bloom - let first_bloom_bytes = vec![ - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x40, 00, 00, 00, 00, 0x50, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x02, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x08, 00, 0x08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x50, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x10, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x20, 00, 00, 00, 00, 00, 0x08, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - ]; - - let retdest = 0xDEADBEEFu32.into(); - - let addr = U256::from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7e, 0xf6, 0x6b, 0x77, 0x75, 0x9e, 0x12, 0xca, 0xf3, - 0xdd, 0xb3, 0xe4, 0xaf, 0xf5, 0x24, 0xe5, 0x77, 0xc5, 0x9d, 0x8d, - ]); - - let topic1 = U256::from([ - 0x8a, 0x22, 0xee, 0x89, 0x91, 0x02, 0xa3, 0x66, 0xac, 0x8a, 0xd0, 0x49, 0x51, 0x27, 0x31, - 0x9c, 0xb1, 0xff, 0x24, 0x03, 0xcf, 0xae, 0x85, 0x5f, 0x83, 0xa8, 0x9c, 0xda, 0x12, 0x66, - 0x67, 0x4d, - ]); - - let topic02 = 0x2a.into(); - let topic03 = 0xbd9fe6.into(); - - // Set logs memory and initialize TxnBloom and BlockBloom segments. - let initial_stack: Vec = vec![retdest]; - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); - let mut logs = vec![ - 0.into(), // unused - addr, - num_topics.into(), // num_topics - topic1, // topic1 - topic02, // topic2 - topic03, // topic3 - 32.into(), // data_len - ]; - let cur_data = hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .iter() - .copied() - .map(U256::from); - logs.extend(cur_data); - // The Bloom filter initialization is required for this test to ensure we have the correct length for the filters. Otherwise, some trailing zeroes could be missing. - interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); // Initialize transaction Bloom filter. - interpreter.set_memory_segment(Segment::LogsData, logs); - interpreter.set_memory_segment(Segment::Logs, vec![0.into()]); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, U256::from(1)); - interpreter.run()?; - - // Second transaction. - let loaded_bloom_u256 = interpreter.get_memory_segment(Segment::TxnBloom); - let loaded_bloom: Vec = loaded_bloom_u256 - .into_iter() - .map(|elt| elt.0[0] as u8) - .collect(); - - assert_eq!(first_bloom_bytes, loaded_bloom); - let topic12 = 0x4.into(); - let topic13 = 0x4920ea.into(); - let mut logs2 = vec![ - 0.into(), // unused - addr, - num_topics.into(), // num_topics - topic1, // topic1 - topic12, // topic2 - topic13, // topic3 - 32.into(), // data_len - ]; - let cur_data = hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .iter() - .copied() - .map(U256::from); - logs2.extend(cur_data); - - interpreter - .push(retdest) - .expect("The stack should not overflow"); - interpreter.generation_state.registers.program_counter = logs_bloom; - interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); // Initialize transaction Bloom filter. - interpreter.set_memory_segment(Segment::LogsData, logs2); - interpreter.set_memory_segment(Segment::Logs, vec![0.into()]); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, U256::from(1)); - interpreter.run()?; - - let second_bloom_bytes = vec![ - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x40, 00, 00, 00, 00, 0x10, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x02, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 0x40, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x20, 00, 0x04, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x08, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - ]; - - let second_loaded_bloom_u256 = interpreter.get_memory_segment(Segment::TxnBloom); - let second_loaded_bloom: Vec = second_loaded_bloom_u256 - .into_iter() - .map(|elt| elt.0[0] as u8) - .collect(); - - assert_eq!(second_bloom_bytes, second_loaded_bloom); - - Ok(()) -} - -#[test] -fn test_mpt_insert_receipt() -> Result<()> { - // This test simulates a receipt processing to test `mpt_insert_receipt_trie`. - // For this, we need to set the data correctly in memory. - // In TrieData, we need to insert a receipt of the form: - // `[payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]]`. - // We also need to set TrieDataSize correctly. - - let retdest = 0xDEADBEEFu32.into(); - let trie_inputs = Default::default(); - let mpt_insert = KERNEL.global_labels["mpt_insert_receipt_trie"]; - let num_topics = 3; // Both transactions have the same number of topics. - let payload_len = 423; // Total payload length for each receipt. - let logs_payload_len = 157; // Payload length for all logs. - let log_payload_len = 155; // Payload length for one log. - let num_logs = 1; - - // Receipt_0: - let status_0 = 1; - let cum_gas_used_0 = 0x016e5b; - let logs_bloom_0_bytes = vec![ - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x40, 00, 00, 00, 00, 0x50, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x02, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x08, 00, 0x08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x50, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x10, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x20, 00, 00, 00, 00, 00, 0x08, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - ]; - - // Logs_0: - let logs_bloom_0: Vec = logs_bloom_0_bytes - .into_iter() - .map(|elt| elt.into()) - .collect(); - - let addr = U256::from([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7e, 0xf6, 0x6b, 0x77, 0x75, 0x9e, 0x12, 0xca, 0xf3, - 0xdd, 0xb3, 0xe4, 0xaf, 0xf5, 0x24, 0xe5, 0x77, 0xc5, 0x9d, 0x8d, - ]); - - // The first topic is shared by the two transactions. - let topic1 = U256::from([ - 0x8a, 0x22, 0xee, 0x89, 0x91, 0x02, 0xa3, 0x66, 0xac, 0x8a, 0xd0, 0x49, 0x51, 0x27, 0x31, - 0x9c, 0xb1, 0xff, 0x24, 0x03, 0xcf, 0xae, 0x85, 0x5f, 0x83, 0xa8, 0x9c, 0xda, 0x12, 0x66, - 0x67, 0x4d, - ]); - - let topic02 = 0x2a.into(); - let topic03 = 0xbd9fe6.into(); - - let mut logs_0 = vec![ - log_payload_len.into(), // payload_len - addr, - num_topics.into(), // num_topics - topic1, // topic1 - topic02, // topic2 - topic03, // topic3 - 32.into(), // data_len - ]; - let cur_data = hex!("f7af1cc94b1aef2e0fa15f1b4baefa86eb60e78fa4bd082372a0a446d197fb58") - .iter() - .copied() - .map(U256::from); - logs_0.extend(cur_data); - - let mut receipt: Vec = vec![423.into(), status_0.into(), cum_gas_used_0.into()]; - receipt.extend(logs_bloom_0); - receipt.push(logs_payload_len.into()); // logs_payload_len - receipt.push(num_logs.into()); // num_logs - receipt.extend(logs_0.clone()); - - let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, vec![]); - initialize_mpts(&mut interpreter, &trie_inputs); - - // If TrieData is empty, we need to push 0 because the first value is always 0. - let mut cur_trie_data = interpreter.get_memory_segment(Segment::TrieData); - if cur_trie_data.is_empty() { - cur_trie_data.push(0.into()); - } - - // stack: transaction_nb, value_ptr, retdest - let num_nibbles = 2; - let initial_stack: Vec = vec![ - retdest, - cur_trie_data.len().into(), - 0x80.into(), - num_nibbles.into(), - ]; - for i in 0..initial_stack.len() { - interpreter - .push(initial_stack[i]) - .expect("The stack should not overflow"); - } - - interpreter.generation_state.registers.program_counter = mpt_insert; - - // Set memory. - cur_trie_data.extend(receipt); - interpreter.set_memory_segment(Segment::TrieData, cur_trie_data.clone()); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, cur_trie_data.len().into()); - // First insertion. - interpreter.run()?; - - // receipt_1: - let status_1 = 1; - let cum_gas_used_1 = 0x02dcb6; - let logs_bloom_1_bytes = vec![ - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x40, 00, 00, 00, 00, 0x10, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x02, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x01, 00, 00, 00, 0x40, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 0x20, 00, 0x04, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 00, 00, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x08, - 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, - ]; - - // Logs_1: - let logs_bloom_1: Vec = logs_bloom_1_bytes - .into_iter() - .map(|elt| elt.into()) - .collect(); - - let topic12 = 4.into(); - let topic13 = 0x4920ea.into(); - - let mut logs_1 = vec![ - log_payload_len.into(), // payload length - addr, - num_topics.into(), // nb topics - topic1, // topic1 - topic12, // topic2 - topic13, // topic3 - 32.into(), // data length - ]; - let cur_data = hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .iter() - .copied() - .map(U256::from); - logs_1.extend(cur_data); - - let mut receipt_1: Vec = vec![payload_len.into(), status_1.into(), cum_gas_used_1.into()]; - receipt_1.extend(logs_bloom_1); - receipt_1.push(logs_payload_len.into()); // logs payload len - receipt_1.push(num_logs.into()); // nb logs - receipt_1.extend(logs_1.clone()); - - // Get updated TrieData segment. - cur_trie_data = interpreter.get_memory_segment(Segment::TrieData); - let num_nibbles = 2; - let initial_stack2: Vec = vec![ - retdest, - cur_trie_data.len().into(), - 0x01.into(), - num_nibbles.into(), - ]; - for i in 0..initial_stack2.len() { - interpreter - .push(initial_stack2[i]) - .expect("The stack should not overflow"); - } - cur_trie_data.extend(receipt_1); - - // Set memory. - interpreter.generation_state.registers.program_counter = mpt_insert; - interpreter.set_memory_segment(Segment::TrieData, cur_trie_data.clone()); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, cur_trie_data.len().into()); - interpreter.run()?; - - // Finally, check that the hashes correspond. - let mpt_hash_receipt = KERNEL.global_labels["mpt_hash_receipt_trie"]; - interpreter.generation_state.registers.program_counter = mpt_hash_receipt; - interpreter - .push(retdest) - .expect("The stack should not overflow"); - interpreter - .push(1.into()) // Initial length of the trie data segment, unused.; // Initial length of the trie data segment, unused. - .expect("The stack should not overflow"); - interpreter.run()?; - assert_eq!( - interpreter.stack()[1], - U256::from(hex!( - "da46cdd329bfedace32da95f2b344d314bc6f55f027d65f9f4ac04ee425e1f98" - )) - ); - Ok(()) -} - -#[test] -fn test_bloom_two_logs() -> Result<()> { - // Tests the Bloom filter computation with two logs in one transaction. - - // address - let to = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x09, 0x5e, 0x7b, 0xae, 0xa6, 0xa6, 0xc7, 0xc4, 0xc2, - 0xdf, 0xeb, 0x97, 0x7e, 0xfa, 0xc3, 0x26, 0xaf, 0x55, 0x2d, 0x87, - ]; - - let retdest = 0xDEADBEEFu32.into(); - let logs_bloom = KERNEL.global_labels["logs_bloom"]; - - let initial_stack: Vec = vec![retdest]; - - // Set memory. - let logs = vec![ - 0.into(), // unused - to.into(), // address - 0.into(), // num_topics - 0.into(), // data_len, - 0.into(), // unused: rlp - to.into(), - 2.into(), // num_topics - 0x62.into(), - 0x63.into(), - 5.into(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa1, - 0xb2, 0xc3, 0xd4, 0xe5, - ] - .into(), - ]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(logs_bloom, initial_stack); - interpreter.set_memory_segment(Segment::TxnBloom, vec![0.into(); 256]); // Initialize transaction Bloom filter. - interpreter.set_memory_segment(Segment::LogsData, logs); - interpreter.set_memory_segment(Segment::Logs, vec![0.into(), 4.into()]); - interpreter.set_global_metadata_field(GlobalMetadata::LogsLen, U256::from(2)); - interpreter.run()?; - - let loaded_bloom_bytes: Vec = interpreter - .get_memory_segment(Segment::TxnBloom) - .into_iter() - .map(|elt| elt.0[0] as u8) - .collect(); - - let expected = hex!("00000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000004000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000400000000000040000000000000000000000000002000000000000000000000000000").to_vec(); - - assert_eq!(expected, loaded_bloom_bytes); - Ok(()) -} - -fn logs_bloom_bytes_fn(logs_list: Vec<(Vec, Vec>)>) -> [u8; 256] { - // The first element of logs_list. - let mut bloom = [0_u8; 256]; - - for log in logs_list { - let cur_addr = log.0; - let topics = log.1; - - add_to_bloom(&mut bloom, &cur_addr); - for topic in topics { - add_to_bloom(&mut bloom, &topic); - } - } - bloom -} - -fn add_to_bloom(bloom: &mut [u8; 256], bloom_entry: &[u8]) { - let bloom_hash = keccak(bloom_entry).to_fixed_bytes(); - - for idx in 0..3 { - let bit_pair = u16::from_be_bytes(bloom_hash[2 * idx..2 * (idx + 1)].try_into().unwrap()); - let bit_to_set = 0x07FF - (bit_pair & 0x07FF); - let byte_index = bit_to_set / 8; - let bit_value = 1 << (7 - bit_to_set % 8); - bloom[byte_index as usize] |= bit_value; - } -} diff --git a/evm/src/cpu/kernel/tests/rlp/decode.rs b/evm/src/cpu/kernel/tests/rlp/decode.rs deleted file mode 100644 index 6a749f5cb8..0000000000 --- a/evm/src/cpu/kernel/tests/rlp/decode.rs +++ /dev/null @@ -1,132 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn test_decode_rlp_string_len_short() -> Result<()> { - let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - - let initial_stack = vec![ - 0xDEADBEEFu32.into(), - U256::from(Segment::RlpRaw as usize + 2), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); - - // A couple dummy bytes, followed by "0x70" which is its own encoding. - interpreter.set_rlp_memory(vec![123, 234, 0x70]); - - interpreter.run()?; - let expected_stack = vec![1.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} - -#[test] -fn test_decode_rlp_string_len_medium() -> Result<()> { - let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - - let initial_stack = vec![ - 0xDEADBEEFu32.into(), - U256::from(Segment::RlpRaw as usize + 2), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); - - // A couple dummy bytes, followed by the RLP encoding of "1 2 3 4 5". - interpreter.set_rlp_memory(vec![123, 234, 0x85, 1, 2, 3, 4, 5]); - - interpreter.run()?; - let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 3)]; // len, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} - -#[test] -fn test_decode_rlp_string_len_long() -> Result<()> { - let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - - let initial_stack = vec![ - 0xDEADBEEFu32.into(), - U256::from(Segment::RlpRaw as usize + 2), - ]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); - - // The RLP encoding of the string "1 2 3 ... 56". - interpreter.set_rlp_memory(vec![ - 123, 234, 0xb8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - ]); - - interpreter.run()?; - let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 4)]; // len, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} - -#[test] -fn test_decode_rlp_list_len_short() -> Result<()> { - let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - - let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); - - // The RLP encoding of [1, 2, [3, 4]]. - interpreter.set_rlp_memory(vec![0xc5, 1, 2, 0xc2, 3, 4]); - - interpreter.run()?; - let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 1)]; // len, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} - -#[test] -fn test_decode_rlp_list_len_long() -> Result<()> { - let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - - let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); - - // The RLP encoding of [1, ..., 56]. - interpreter.set_rlp_memory(vec![ - 0xf8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - ]); - - interpreter.run()?; - let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} - -#[test] -fn test_decode_rlp_scalar() -> Result<()> { - let decode_rlp_scalar = KERNEL.global_labels["decode_rlp_scalar"]; - - let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); - - // The RLP encoding of "12 34 56". - interpreter.set_rlp_memory(vec![0x83, 0x12, 0x34, 0x56]); - - interpreter.run()?; - let expected_stack = vec![0x123456.into(), U256::from(Segment::RlpRaw as usize + 4)]; // scalar, pos - assert_eq!(interpreter.stack(), expected_stack); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs deleted file mode 100644 index 75464235b7..0000000000 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ /dev/null @@ -1,166 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; - -#[test] -fn test_encode_rlp_scalar_small() -> Result<()> { - let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; - - let retdest = 0xDEADBEEFu32.into(); - let scalar = 42.into(); - let pos = U256::from(Segment::RlpRaw as usize + 2); - let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); - - interpreter.run()?; - let expected_stack = vec![pos + U256::from(1)]; // pos' = pos + rlp_len = 2 + 1 - let expected_rlp = vec![0, 0, 42]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_scalar_medium() -> Result<()> { - let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; - - let retdest = 0xDEADBEEFu32.into(); - let scalar = 0x12345.into(); - let pos = U256::from(Segment::RlpRaw as usize + 2); - let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); - - interpreter.run()?; - let expected_stack = vec![pos + U256::from(4)]; // pos' = pos + rlp_len = 2 + 4 - let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_160() -> Result<()> { - let encode_rlp_fixed = KERNEL.global_labels["encode_rlp_fixed"]; - - let retdest = 0xDEADBEEFu32.into(); - let string = 0x12345.into(); - let pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, string, pos, U256::from(20)]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); - - interpreter.run()?; - let expected_stack = vec![pos + U256::from(1 + 20)]; // pos' - #[rustfmt::skip] - let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_256() -> Result<()> { - let encode_rlp_fixed = KERNEL.global_labels["encode_rlp_fixed"]; - - let retdest = 0xDEADBEEFu32.into(); - let string = 0x12345.into(); - let pos = U256::from(Segment::RlpRaw as usize); - let initial_stack = vec![retdest, string, pos, U256::from(32)]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(encode_rlp_fixed, initial_stack); - - interpreter.run()?; - let expected_stack = vec![pos + U256::from(1 + 32)]; // pos' - #[rustfmt::skip] - let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_prepend_rlp_list_prefix_small() -> Result<()> { - let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; - - let retdest = 0xDEADBEEFu32.into(); - let start_pos = U256::from(Segment::RlpRaw as usize + 9); - let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 5); - let initial_stack = vec![retdest, start_pos, end_pos]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); - interpreter.set_rlp_memory(vec![ - // Nine 0s to leave room for the longest possible RLP list prefix. - 0, 0, 0, 0, 0, 0, 0, 0, 0, - // The actual RLP list payload, consisting of 5 tiny strings. - 1, 2, 3, 4, 5, - ]); - - interpreter.run()?; - - let expected_rlp_len = 6.into(); - let expected_start_pos = U256::from(Segment::RlpRaw as usize + 8); - let expected_stack = vec![expected_rlp_len, expected_start_pos]; - let expected_rlp = vec![0, 0, 0, 0, 0, 0, 0, 0, 0xc0 + 5, 1, 2, 3, 4, 5]; - - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_prepend_rlp_list_prefix_large() -> Result<()> { - let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; - - let retdest = 0xDEADBEEFu32.into(); - let start_pos = U256::from(Segment::RlpRaw as usize + 9); - let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 60); - let initial_stack = vec![retdest, start_pos, end_pos]; - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); - - #[rustfmt::skip] - interpreter.set_rlp_memory(vec![ - // Nine 0s to leave room for the longest possible RLP list prefix. - 0, 0, 0, 0, 0, 0, 0, 0, 0, - // The actual RLP list payload, consisting of 60 tiny strings. - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - ]); - - interpreter.run()?; - - let expected_rlp_len = 62.into(); - let expected_start_pos = U256::from(Segment::RlpRaw as usize + 7); - let expected_stack = vec![expected_rlp_len, expected_start_pos]; - - #[rustfmt::skip] - let expected_rlp = vec![ - 0, 0, 0, 0, 0, 0, 0, 0xf7 + 1, 60, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - ]; - - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/rlp/mod.rs b/evm/src/cpu/kernel/tests/rlp/mod.rs deleted file mode 100644 index 3629434f6e..0000000000 --- a/evm/src/cpu/kernel/tests/rlp/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod decode; -mod encode; -mod num_bytes; diff --git a/evm/src/cpu/kernel/tests/rlp/num_bytes.rs b/evm/src/cpu/kernel/tests/rlp/num_bytes.rs deleted file mode 100644 index b02175d055..0000000000 --- a/evm/src/cpu/kernel/tests/rlp/num_bytes.rs +++ /dev/null @@ -1,47 +0,0 @@ -use anyhow::Result; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; - -#[test] -fn test_num_bytes_0() -> Result<()> { - let num_bytes = KERNEL.global_labels["num_bytes"]; - - let retdest = 0xDEADBEEFu32.into(); - let x = 0.into(); - let initial_stack = vec![retdest, x]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![1.into()]); - Ok(()) -} - -#[test] -fn test_num_bytes_small() -> Result<()> { - let num_bytes = KERNEL.global_labels["num_bytes"]; - - let retdest = 0xDEADBEEFu32.into(); - let x = 42.into(); - let initial_stack = vec![retdest, x]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![1.into()]); - Ok(()) -} - -#[test] -fn test_num_bytes_medium() -> Result<()> { - let num_bytes = KERNEL.global_labels["num_bytes"]; - - let retdest = 0xDEADBEEFu32.into(); - let x = 0xAABBCCDDu32.into(); - let initial_stack = vec![retdest, x]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(num_bytes, initial_stack); - - interpreter.run()?; - assert_eq!(interpreter.stack(), vec![4.into()]); - Ok(()) -} diff --git a/evm/src/cpu/kernel/tests/signed_syscalls.rs b/evm/src/cpu/kernel/tests/signed_syscalls.rs deleted file mode 100644 index 993b8e03f2..0000000000 --- a/evm/src/cpu/kernel/tests/signed_syscalls.rs +++ /dev/null @@ -1,169 +0,0 @@ -use ethereum_types::U256; -use plonky2::field::goldilocks_field::GoldilocksField as F; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::interpreter::Interpreter; - -/// Generate a list of inputs suitable for testing the signed operations -/// -/// The result includes 0, ±1, ±2^(16i ± 1) for i = 0..15, and ±2^255 -/// and then each of those ±1. Little attempt has been made to avoid -/// duplicates. Total length is 279. -fn test_inputs() -> Vec { - let mut res = vec![U256::zero()]; - for i in 1..16 { - res.push(U256::one() << (16 * i)); - res.push(U256::one() << (16 * i + 1)); - res.push(U256::one() << (16 * i - 1)); - } - res.push(U256::one() << 255); - - let n = res.len(); - for i in 1..n { - // push -res[i] - res.push(res[i].overflowing_neg().0); - } - - let n = res.len(); - for i in 0..n { - res.push(res[i].overflowing_add(U256::one()).0); - res.push(res[i].overflowing_sub(U256::one()).0); - } - - res -} - -// U256_TOP_BIT == 2^255. -const U256_TOP_BIT: U256 = U256([0x0, 0x0, 0x0, 0x8000000000000000]); - -/// Given a U256 `value`, interpret as a signed 256-bit number and -/// return the arithmetic right shift of `value` by `shift` bit -/// positions, i.e. the right shift of `value` with sign extension. -fn u256_sar(shift: U256, value: U256) -> U256 { - // Reference: Hacker's Delight, 2013, 2nd edition, §2-7. - let shift = shift.min(U256::from(255)); - ((value ^ U256_TOP_BIT) >> shift) - .overflowing_sub(U256_TOP_BIT >> shift) - .0 -} - -/// Given a U256 x, interpret it as a signed 256-bit number and return -/// the pair abs(x) and sign(x), where sign(x) = 1 if x < 0, and 0 -/// otherwise. NB: abs(x) is interpreted as an unsigned value, so -/// u256_abs_sgn(-2^255) = (2^255, -1). -fn u256_abs_sgn(x: U256) -> (U256, bool) { - let is_neg = x.bit(255); - - // negate x if it's negative - let x = if is_neg { x.overflowing_neg().0 } else { x }; - (x, is_neg) -} - -fn u256_sdiv(x: U256, y: U256) -> U256 { - let (abs_x, x_is_neg) = u256_abs_sgn(x); - let (abs_y, y_is_neg) = u256_abs_sgn(y); - if y.is_zero() { - U256::zero() - } else { - let quot = abs_x / abs_y; - // negate the quotient if arguments had opposite signs - if x_is_neg != y_is_neg { - quot.overflowing_neg().0 - } else { - quot - } - } -} - -fn u256_smod(x: U256, y: U256) -> U256 { - let (abs_x, x_is_neg) = u256_abs_sgn(x); - let (abs_y, _) = u256_abs_sgn(y); - - if y.is_zero() { - U256::zero() - } else { - let rem = abs_x % abs_y; - // negate the remainder if dividend was negative - if x_is_neg { - rem.overflowing_neg().0 - } else { - rem - } - } -} - -// signextend is just a SHL followed by SAR. -fn u256_signextend(byte: U256, value: U256) -> U256 { - // byte = min(31, byte) - let byte: u32 = byte.min(U256::from(31)).try_into().unwrap(); - let bit_offset = 256 - 8 * (byte + 1); - u256_sar(U256::from(bit_offset), value << bit_offset) -} - -// Reference: Hacker's Delight, 2013, 2nd edition, §2-12. -fn u256_slt(x: U256, y: U256) -> U256 { - let top_bit: U256 = U256::one() << 255; - U256::from(((x ^ top_bit) < (y ^ top_bit)) as u32) -} - -fn u256_sgt(x: U256, y: U256) -> U256 { - u256_slt(y, x) -} - -fn run_test(fn_label: &str, expected_fn: fn(U256, U256) -> U256, opname: &str) { - let inputs = test_inputs(); - let fn_label = KERNEL.global_labels[fn_label]; - let retdest = U256::from(0xDEADBEEFu32); - - for &x in &inputs { - for &y in &inputs { - let stack = vec![retdest, y, x]; - let mut interpreter: Interpreter = Interpreter::new_with_kernel(fn_label, stack); - interpreter.run().unwrap(); - assert_eq!(interpreter.stack_len(), 1usize, "unexpected stack size"); - let output = interpreter - .stack_top() - .expect("The stack should not be empty."); - let expected_output = expected_fn(x, y); - assert_eq!( - output, expected_output, - "{opname}({x}, {y}): expected {expected_output} but got {output}" - ); - } - } -} - -#[test] -fn test_sdiv() { - // Double-check that the expected output calculation is correct in the special case. - let x = U256::one() << 255; // -2^255 - let y = U256::one().overflowing_neg().0; // -1 - assert_eq!(u256_sdiv(x, y), x); // SDIV(-2^255, -1) = -2^255. - - run_test("_sys_sdiv", u256_sdiv, "SDIV"); -} - -#[test] -fn test_smod() { - run_test("_sys_smod", u256_smod, "SMOD"); -} - -#[test] -fn test_signextend() { - run_test("_sys_signextend", u256_signextend, "SIGNEXTEND"); -} - -#[test] -fn test_sar() { - run_test("_sys_sar", u256_sar, "SAR"); -} - -#[test] -fn test_slt() { - run_test("_sys_slt", u256_slt, "SLT"); -} - -#[test] -fn test_sgt() { - run_test("_sys_sgt", u256_sgt, "SGT"); -} diff --git a/evm/src/cpu/kernel/tests/transaction_parsing/mod.rs b/evm/src/cpu/kernel/tests/transaction_parsing/mod.rs deleted file mode 100644 index fb50625f9e..0000000000 --- a/evm/src/cpu/kernel/tests/transaction_parsing/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod parse_type_0_txn; diff --git a/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs b/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs deleted file mode 100644 index 8415b47bd5..0000000000 --- a/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs +++ /dev/null @@ -1,68 +0,0 @@ -use anyhow::Result; -use ethereum_types::U256; -use hex_literal::hex; -use plonky2::field::goldilocks_field::GoldilocksField as F; -use NormalizedTxnField::*; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; -use crate::cpu::kernel::interpreter::Interpreter; - -#[test] -fn process_type_0_txn() -> Result<()> { - let process_type_0_txn = KERNEL.global_labels["process_type_0_txn"]; - let process_normalized_txn = KERNEL.global_labels["process_normalized_txn"]; - - let retaddr = 0xDEADBEEFu32.into(); - let mut interpreter: Interpreter = - Interpreter::new_with_kernel(process_type_0_txn, vec![retaddr]); - - // When we reach process_normalized_txn, we're done with parsing and normalizing. - // Processing normalized transactions is outside the scope of this test. - interpreter.halt_offsets.push(process_normalized_txn); - - // Generated with py-evm: - // import eth, eth_keys, eth_utils, rlp - // genesis_params = { 'difficulty': eth.constants.GENESIS_DIFFICULTY } - // chain = eth.chains.mainnet.MainnetChain.from_genesis(eth.db.atomic.AtomicDB(), genesis_params, {}) - // unsigned_txn = chain.create_unsigned_transaction( - // nonce=5, - // gas_price=10, - // gas=22_000, - // to=eth.constants.ZERO_ADDRESS, - // value=100, - // data=b'\x42\x42', - // ) - // sk = eth_keys.keys.PrivateKey(eth_utils.decode_hex('4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318')) - // signed_txn = unsigned_txn.as_signed_transaction(sk) - // rlp.encode(signed_txn).hex() - interpreter.set_rlp_memory(hex!("f861050a8255f0940000000000000000000000000000000000000000648242421ca07c5c61ed975ebd286f6b027b8c504842e50a47d318e1e801719dd744fe93e6c6a01e7b5119b57dd54e175ff2f055c91f3ab1b53eba0b2c184f347cdff0e745aca2").to_vec()); - - interpreter.run()?; - - assert_eq!(interpreter.get_txn_field(ChainIdPresent), 0.into()); - assert_eq!(interpreter.get_txn_field(ChainId), 0.into()); - assert_eq!(interpreter.get_txn_field(Nonce), 5.into()); - assert_eq!(interpreter.get_txn_field(MaxPriorityFeePerGas), 10.into()); - assert_eq!(interpreter.get_txn_field(MaxPriorityFeePerGas), 10.into()); - assert_eq!(interpreter.get_txn_field(MaxFeePerGas), 10.into()); - assert_eq!(interpreter.get_txn_field(To), 0.into()); - assert_eq!(interpreter.get_txn_field(Value), 100.into()); - assert_eq!(interpreter.get_txn_field(DataLen), 2.into()); - assert_eq!(interpreter.get_txn_data(), &[0x42.into(), 0x42.into()]); - assert_eq!(interpreter.get_txn_field(YParity), 1.into()); - assert_eq!( - interpreter.get_txn_field(R), - U256::from_big_endian(&hex!( - "7c5c61ed975ebd286f6b027b8c504842e50a47d318e1e801719dd744fe93e6c6" - )) - ); - assert_eq!( - interpreter.get_txn_field(S), - U256::from_big_endian(&hex!( - "1e7b5119b57dd54e175ff2f055c91f3ab1b53eba0b2c184f347cdff0e745aca2" - )) - ); - - Ok(()) -} diff --git a/evm/src/cpu/kernel/utils.rs b/evm/src/cpu/kernel/utils.rs deleted file mode 100644 index 18b5f54822..0000000000 --- a/evm/src/cpu/kernel/utils.rs +++ /dev/null @@ -1,73 +0,0 @@ -use core::fmt::Debug; - -use ethereum_types::U256; -use plonky2_util::ceil_div_usize; - -/// Enumerate the length `W` windows of `vec`, and run `maybe_replace` on each one. -/// -/// Whenever `maybe_replace` returns `Some(replacement)`, the given replacement will be applied. -pub(crate) fn replace_windows(vec: &mut Vec, maybe_replace: F) -where - T: Clone + Debug, - F: Fn([T; W]) -> Option>, -{ - let mut start = 0; - while start + W <= vec.len() { - let range = start..start + W; - let window = vec[range.clone()].to_vec().try_into().unwrap(); - if let Some(replacement) = maybe_replace(window) { - vec.splice(range, replacement); - // Go back to the earliest window that changed. - start = start.saturating_sub(W - 1); - } else { - start += 1; - } - } -} - -pub(crate) fn u256_to_trimmed_be_bytes(u256: &U256) -> Vec { - let num_bytes = ceil_div_usize(u256.bits(), 8); - // `byte` is little-endian, so we manually reverse it. - (0..num_bytes).rev().map(|i| u256.byte(i)).collect() -} - -pub(crate) const fn u256_from_bool(b: bool) -> U256 { - if b { - U256::one() - } else { - U256::zero() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_replace_windows() { - // This replacement function adds pairs of integers together. - let mut vec = vec![1, 2, 3, 4, 5]; - replace_windows(&mut vec, |[x, y]| Some(vec![x + y])); - assert_eq!(vec, vec![15u32]); - - // This replacement function splits each composite integer into two factors. - let mut vec = vec![9, 1, 6, 8, 15, 7, 9]; - replace_windows(&mut vec, |[n]| { - (2..n).find(|d| n % d == 0).map(|d| vec![d, n / d]) - }); - assert_eq!(vec, vec![3, 3, 1, 2, 3, 2, 2, 2, 3, 5, 7, 3, 3]); - } - - #[test] - fn literal_to_be_bytes() { - assert_eq!(u256_to_trimmed_be_bytes(&0.into()), Vec::::new()); - - assert_eq!(u256_to_trimmed_be_bytes(&1.into()), vec![0x01]); - - assert_eq!(u256_to_trimmed_be_bytes(&768.into()), vec![0x03, 0x00]); - - assert_eq!(u256_to_trimmed_be_bytes(&0xa1b2.into()), vec![0xa1, 0xb2]); - - assert_eq!(u256_to_trimmed_be_bytes(&0x1b2.into()), vec![0x1, 0xb2]); - } -} diff --git a/evm/src/cpu/membus.rs b/evm/src/cpu/membus.rs deleted file mode 100644 index b50ab5cce3..0000000000 --- a/evm/src/cpu/membus.rs +++ /dev/null @@ -1,84 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -/// General-purpose memory channels; they can read and write to all contexts/segments/addresses. -pub(crate) const NUM_GP_CHANNELS: usize = 3; - -/// Indices for code and general purpose memory channels. -pub mod channel_indices { - use core::ops::Range; - - pub(crate) const CODE: usize = 0; - pub(crate) const GP: Range = CODE + 1..(CODE + 1) + super::NUM_GP_CHANNELS; -} - -/// Total memory channels used by the CPU table. This includes all the `GP_MEM_CHANNELS` as well as -/// all special-purpose memory channels. -/// -/// Currently, there is one special-purpose memory channel, which reads the opcode from memory. Its -/// limitations are: -/// - it is enabled by `is_cpu_cycle`, -/// - it always reads and cannot write, -/// - the context is derived from the current context and the `is_kernel_mode` flag, -/// - the segment is hard-wired to the code segment, -/// - the address is `program_counter`, -/// - the value must fit in one byte (in the least-significant position) and its eight bits are -/// found in `opcode_bits`. -/// -/// There is also a partial channel, which shares its values with another general purpose channel. -/// -/// These limitations save us numerous columns in the CPU table. -pub(crate) const NUM_CHANNELS: usize = channel_indices::GP.end + 1; - -/// Evaluates constraints regarding the membus. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // Validate `lv.code_context`. - // It should be 0 if in kernel mode and `lv.context` if in user mode. - yield_constr.constraint(lv.code_context - (P::ONES - lv.is_kernel_mode) * lv.context); - - // Validate `channel.used`. It should be binary. - for channel in lv.mem_channels { - yield_constr.constraint(channel.used * (channel.used - P::ONES)); - } - - // Validate `partial_channel.used`. It should be binary. - yield_constr.constraint(lv.partial_channel.used * (lv.partial_channel.used - P::ONES)); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints regarding the membus. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // Validate `lv.code_context`. - // It should be 0 if in kernel mode and `lv.context` if in user mode. - let diff = builder.sub_extension(lv.context, lv.code_context); - let constr = builder.mul_sub_extension(lv.is_kernel_mode, lv.context, diff); - yield_constr.constraint(builder, constr); - - // Validate `channel.used`. It should be binary. - for channel in lv.mem_channels { - let constr = builder.mul_sub_extension(channel.used, channel.used, channel.used); - yield_constr.constraint(builder, constr); - } - - // Validate `partial_channel.used`. It should be binary. - { - let constr = builder.mul_sub_extension( - lv.partial_channel.used, - lv.partial_channel.used, - lv.partial_channel.used, - ); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs deleted file mode 100644 index ac32253da1..0000000000 --- a/evm/src/cpu/memio.rs +++ /dev/null @@ -1,367 +0,0 @@ -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use super::cpu_stark::get_addr; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::stack; -use crate::memory::segments::Segment; - -const fn get_addr_load(lv: &CpuColumnsView) -> (T, T, T) { - get_addr(lv, 0) -} -const fn get_addr_store(lv: &CpuColumnsView) -> (T, T, T) { - get_addr(lv, 1) -} - -/// Evaluates constraints for MLOAD_GENERAL. -fn eval_packed_load( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // The opcode for MLOAD_GENERAL is 0xfb. If the operation is MLOAD_GENERAL, lv.opcode_bits[0] = 1. - let filter = lv.op.m_op_general * lv.opcode_bits[0]; - - let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); - - // Check that we are loading the correct value from the correct address. - let load_channel = lv.mem_channels[1]; - yield_constr.constraint(filter * (load_channel.used - P::ONES)); - yield_constr.constraint(filter * (load_channel.is_read - P::ONES)); - yield_constr.constraint(filter * (load_channel.addr_context - addr_context)); - yield_constr.constraint(filter * (load_channel.addr_segment - addr_segment)); - yield_constr.constraint(filter * (load_channel.addr_virtual - addr_virtual)); - - // Constrain the new top of the stack. - for (&limb_loaded, &limb_new_top) in load_channel - .value - .iter() - .zip(nv.mem_channels[0].value.iter()) - { - yield_constr.constraint(filter * (limb_loaded - limb_new_top)); - } - - // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[2..] { - yield_constr.constraint(filter * channel.used); - } - yield_constr.constraint(filter * lv.partial_channel.used); - - // Stack constraints - stack::eval_packed_one( - lv, - nv, - filter, - stack::MLOAD_GENERAL_OP.unwrap(), - yield_constr, - ); -} - -/// Circuit version for `eval_packed_load`. -/// Evaluates constraints for MLOAD_GENERAL. -fn eval_ext_circuit_load, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // The opcode for MLOAD_GENERAL is 0xfb. If the operation is MLOAD_GENERAL, lv.opcode_bits[0] = 1. - let mut filter = lv.op.m_op_general; - filter = builder.mul_extension(filter, lv.opcode_bits[0]); - - let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); - - // Check that we are loading the correct value from the correct channel. - let load_channel = lv.mem_channels[1]; - { - let constr = builder.mul_sub_extension(filter, load_channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, load_channel.is_read, filter); - yield_constr.constraint(builder, constr); - } - for (channel_field, target) in izip!( - [ - load_channel.addr_context, - load_channel.addr_segment, - load_channel.addr_virtual, - ], - [addr_context, addr_segment, addr_virtual] - ) { - let diff = builder.sub_extension(channel_field, target); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - // Constrain the new top of the stack. - for (&limb_loaded, &limb_new_top) in load_channel - .value - .iter() - .zip(nv.mem_channels[0].value.iter()) - { - let diff = builder.sub_extension(limb_loaded, limb_new_top); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[2..] { - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(filter, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } - - // Stack constraints - stack::eval_ext_circuit_one( - builder, - lv, - nv, - filter, - stack::MLOAD_GENERAL_OP.unwrap(), - yield_constr, - ); -} - -/// Evaluates constraints for MSTORE_GENERAL. -fn eval_packed_store( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let filter = lv.op.m_op_general * (lv.opcode_bits[0] - P::ONES); - - let (addr_context, addr_segment, addr_virtual) = get_addr_store(lv); - - // The value will be checked with the CTL. - let store_channel = lv.partial_channel; - - yield_constr.constraint(filter * (store_channel.used - P::ONES)); - yield_constr.constraint(filter * store_channel.is_read); - yield_constr.constraint(filter * (store_channel.addr_context - addr_context)); - yield_constr.constraint(filter * (store_channel.addr_segment - addr_segment)); - yield_constr.constraint(filter * (store_channel.addr_virtual - addr_virtual)); - - // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[2..] { - yield_constr.constraint(filter * channel.used); - } - - // Stack constraints. - // Pops. - for i in 1..2 { - let channel = lv.mem_channels[i]; - - yield_constr.constraint(filter * (channel.used - P::ONES)); - yield_constr.constraint(filter * (channel.is_read - P::ONES)); - - yield_constr.constraint(filter * (channel.addr_context - lv.context)); - yield_constr.constraint( - filter - * (channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. - let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); - yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); - } - // Constrain `stack_inv_aux`. - let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(2); - yield_constr.constraint( - lv.op.m_op_general - * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. - let top_read_channel = nv.mem_channels[0]; - let is_top_read = lv.general.stack().stack_inv_aux * (P::ONES - lv.opcode_bits[0]); - // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * opcode_bits[0]`. - yield_constr - .constraint(lv.op.m_op_general * (lv.general.stack().stack_inv_aux_2 - is_top_read)); - let new_filter = lv.op.m_op_general * lv.general.stack().stack_inv_aux_2; - yield_constr.constraint_transition(new_filter * (top_read_channel.used - P::ONES)); - yield_constr.constraint_transition(new_filter * (top_read_channel.is_read - P::ONES)); - yield_constr.constraint_transition(new_filter * (top_read_channel.addr_context - nv.context)); - yield_constr.constraint_transition( - new_filter - * (top_read_channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - let addr_virtual = nv.stack_len - P::ONES; - yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); - // If stack_len == 2 or MLOAD, disable the channel. - yield_constr.constraint( - lv.op.m_op_general * (lv.general.stack().stack_inv_aux - P::ONES) * top_read_channel.used, - ); - yield_constr.constraint(lv.op.m_op_general * lv.opcode_bits[0] * top_read_channel.used); -} - -/// Circuit version of `eval_packed_store`. -/// Evaluates constraints for MSTORE_GENERAL. -fn eval_ext_circuit_store, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let filter = - builder.mul_sub_extension(lv.op.m_op_general, lv.opcode_bits[0], lv.op.m_op_general); - - let (addr_context, addr_segment, addr_virtual) = get_addr_store(lv); - - // The value will be checked with the CTL. - let store_channel = lv.partial_channel; - { - let constr = builder.mul_sub_extension(filter, store_channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(filter, store_channel.is_read); - yield_constr.constraint(builder, constr); - } - for (channel_field, target) in izip!( - [ - store_channel.addr_context, - store_channel.addr_segment, - store_channel.addr_virtual, - ], - [addr_context, addr_segment, addr_virtual] - ) { - let diff = builder.sub_extension(channel_field, target); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - - // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[2..] { - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - - // Stack constraints - // Pops. - for i in 1..2 { - let channel = lv.mem_channels[i]; - - { - let constr = builder.mul_sub_extension(filter, channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, channel.is_read, filter); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_context, lv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.add_const_extension( - channel.addr_segment, - -F::from_canonical_usize(Segment::Stack.unscale()), - ); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. - let addr_virtual = - builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(i + 1)); - let diff = builder.sub_extension(channel.addr_virtual, addr_virtual); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - // Constrain `stack_inv_aux`. - { - let len_diff = builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(2)); - let diff = builder.mul_sub_extension( - len_diff, - lv.general.stack().stack_inv, - lv.general.stack().stack_inv_aux, - ); - let constr = builder.mul_extension(lv.op.m_op_general, diff); - yield_constr.constraint(builder, constr); - } - // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. - let top_read_channel = nv.mem_channels[0]; - let is_top_read = builder.mul_extension(lv.general.stack().stack_inv_aux, lv.opcode_bits[0]); - let is_top_read = builder.sub_extension(lv.general.stack().stack_inv_aux, is_top_read); - // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * (1 - opcode_bits[0])`. - { - let diff = builder.sub_extension(lv.general.stack().stack_inv_aux_2, is_top_read); - let constr = builder.mul_extension(lv.op.m_op_general, diff); - yield_constr.constraint(builder, constr); - } - let new_filter = builder.mul_extension(lv.op.m_op_general, lv.general.stack().stack_inv_aux_2); - { - let constr = builder.mul_sub_extension(new_filter, top_read_channel.used, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.mul_sub_extension(new_filter, top_read_channel.is_read, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(top_read_channel.addr_context, nv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.add_const_extension( - top_read_channel.addr_segment, - -F::from_canonical_usize(Segment::Stack.unscale()), - ); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let addr_virtual = builder.add_const_extension(nv.stack_len, -F::ONE); - let diff = builder.sub_extension(top_read_channel.addr_virtual, addr_virtual); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - // If stack_len == 2 or MLOAD, disable the channel. - { - let diff = builder.mul_sub_extension( - lv.op.m_op_general, - lv.general.stack().stack_inv_aux, - lv.op.m_op_general, - ); - let constr = builder.mul_extension(diff, top_read_channel.used); - yield_constr.constraint(builder, constr); - } - { - let mul = builder.mul_extension(lv.op.m_op_general, lv.opcode_bits[0]); - let constr = builder.mul_extension(mul, top_read_channel.used); - yield_constr.constraint(builder, constr); - } -} - -/// Evaluates constraints for MLOAD_GENERAL and MSTORE_GENERAL. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - eval_packed_load(lv, nv, yield_constr); - eval_packed_store(lv, nv, yield_constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for MLOAD_GENERAL and MSTORE_GENERAL. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - eval_ext_circuit_load(builder, lv, nv, yield_constr); - eval_ext_circuit_store(builder, lv, nv, yield_constr); -} diff --git a/evm/src/cpu/mod.rs b/evm/src/cpu/mod.rs deleted file mode 100644 index 3d5124ba2b..0000000000 --- a/evm/src/cpu/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod byte_unpacking; -mod clock; -pub(crate) mod columns; -mod contextops; -pub(crate) mod control_flow; -pub mod cpu_stark; -pub(crate) mod decode; -mod dup_swap; -mod gas; -mod halt; -mod jumps; -pub mod kernel; -pub(crate) mod membus; -mod memio; -mod modfp254; -mod pc; -mod push0; -mod shift; -pub(crate) mod simple_logic; -pub(crate) mod stack; -mod syscalls_exceptions; diff --git a/evm/src/cpu/modfp254.rs b/evm/src/cpu/modfp254.rs deleted file mode 100644 index a3b40f5929..0000000000 --- a/evm/src/cpu/modfp254.rs +++ /dev/null @@ -1,53 +0,0 @@ -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -// Python: -// >>> P = 21888242871839275222246405745257275088696311157297823662689037894645226208583 -// >>> "[" + ", ".join(hex((P >> n) % 2**32) for n in range(0, 256, 32)) + "]" -const P_LIMBS: [u32; 8] = [ - 0xd87cfd47, 0x3c208c16, 0x6871ca8d, 0x97816a91, 0x8181585d, 0xb85045b6, 0xe131a029, 0x30644e72, -]; - -/// Evaluates constraints to check the modulus in mem_channel[2]. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let filter = lv.op.fp254_op; - - // We want to use all the same logic as the usual mod operations, but without needing to read - // the modulus from the stack. We simply constrain `mem_channels[1]` to be our prime (that's - // where the modulus goes in the generalized operations). - let channel_val = lv.mem_channels[2].value; - for (channel_limb, p_limb) in izip!(channel_val, P_LIMBS) { - let p_limb = P::Scalar::from_canonical_u32(p_limb); - yield_constr.constraint(filter * (channel_limb - p_limb)); - } -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints to check the modulus in mem_channel[2]. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let filter = lv.op.fp254_op; - - // We want to use all the same logic as the usual mod operations, but without needing to read - // the modulus from the stack. We simply constrain `mem_channels[1]` to be our prime (that's - // where the modulus goes in the generalized operations). - let channel_val = lv.mem_channels[2].value; - for (channel_limb, p_limb) in izip!(channel_val, P_LIMBS) { - let p_limb = F::from_canonical_u32(p_limb); - let constr = builder.arithmetic_extension(F::ONE, -p_limb, filter, channel_limb, filter); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/pc.rs b/evm/src/cpu/pc.rs deleted file mode 100644 index 4294dbaf61..0000000000 --- a/evm/src/cpu/pc.rs +++ /dev/null @@ -1,46 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -/// Evaluates constraints to check that we are storing the correct PC. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // `PUSH0`'s opcode is odd, while `PC`'s opcode is even. - let filter = lv.op.pc_push0 * (P::ONES - lv.opcode_bits[0]); - let new_stack_top = nv.mem_channels[0].value; - yield_constr.constraint(filter * (new_stack_top[0] - lv.program_counter)); - for &limb in &new_stack_top[1..] { - yield_constr.constraint(filter * limb); - } -} - -/// Circuit version if `eval_packed`. -/// Evaluates constraints to check that we are storing the correct PC. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // `PUSH0`'s opcode is odd, while `PC`'s opcode is even. - let one = builder.one_extension(); - let mut filter = builder.sub_extension(one, lv.opcode_bits[0]); - filter = builder.mul_extension(lv.op.pc_push0, filter); - let new_stack_top = nv.mem_channels[0].value; - { - let diff = builder.sub_extension(new_stack_top[0], lv.program_counter); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - for &limb in &new_stack_top[1..] { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/push0.rs b/evm/src/cpu/push0.rs deleted file mode 100644 index 4f37a55e0b..0000000000 --- a/evm/src/cpu/push0.rs +++ /dev/null @@ -1,36 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -/// Evaluates constraints to check that we are not pushing anything. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // `PUSH0`'s opcode is odd, while `PC`'s opcode is even. - let filter = lv.op.pc_push0 * lv.opcode_bits[0]; - for limb in nv.mem_channels[0].value { - yield_constr.constraint(filter * limb); - } -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints to check that we are not pushing anything. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // `PUSH0`'s opcode is odd, while `PC`'s opcode is even. - let filter = builder.mul_extension(lv.op.pc_push0, lv.opcode_bits[0]); - for limb in nv.mem_channels[0].value { - let constr = builder.mul_extension(filter, limb); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/cpu/shift.rs b/evm/src/cpu/shift.rs deleted file mode 100644 index 12ed18b9ed..0000000000 --- a/evm/src/cpu/shift.rs +++ /dev/null @@ -1,123 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::memory::segments::Segment; - -/// Evaluates constraints for shift operations on the CPU side: -/// the shifting factor is read from memory when displacement < 2^32. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let is_shift = lv.op.shift; - let displacement = lv.mem_channels[0]; // holds the shift displacement d - let two_exp = lv.mem_channels[2]; // holds 2^d - - // Not needed here; val is the input and we're verifying that output is - // val * 2^d (mod 2^256) - // let val = lv.mem_channels[0]; - // let output = lv.mem_channels[NUM_GP_CHANNELS - 1]; - - let shift_table_segment = P::Scalar::from_canonical_usize(Segment::ShiftTable.unscale()); - - // Only lookup the shifting factor when displacement is < 2^32. - // two_exp.used is true (1) if the high limbs of the displacement are - // zero and false (0) otherwise. - let high_limbs_are_zero = two_exp.used; - yield_constr.constraint(is_shift * high_limbs_are_zero * (two_exp.is_read - P::ONES)); - - let high_limbs_sum: P = displacement.value[1..].iter().copied().sum(); - let high_limbs_sum_inv = lv.general.shift().high_limb_sum_inv; - // Verify that high_limbs_are_zero = 0 implies high_limbs_sum != 0 and - // high_limbs_are_zero = 1 implies high_limbs_sum = 0. - let t = high_limbs_sum * high_limbs_sum_inv - (P::ONES - high_limbs_are_zero); - yield_constr.constraint(is_shift * t); - yield_constr.constraint(is_shift * high_limbs_sum * high_limbs_are_zero); - - // When the shift displacement is < 2^32, constrain the two_exp - // mem_channel to be the entry corresponding to `displacement` in - // the shift table lookup (will be zero if displacement >= 256). - yield_constr.constraint(is_shift * two_exp.addr_context); // read from kernel memory - yield_constr.constraint(is_shift * (two_exp.addr_segment - shift_table_segment)); - yield_constr.constraint(is_shift * (two_exp.addr_virtual - displacement.value[0])); - - // Other channels must be unused - for chan in &lv.mem_channels[3..NUM_GP_CHANNELS] { - yield_constr.constraint(is_shift * chan.used); // channel is not used - } - - // Cross-table lookup must connect the memory channels here to MUL - // (in the case of left shift) or DIV (in the case of right shift) - // in the arithmetic table. Specifically, the mapping is - // - // 1 -> 0 (value to be shifted is the same) - // 2 -> 1 (two_exp becomes the multiplicand (resp. divisor)) - // next_0 -> next_0 (output is the same) -} - -/// Circuit version. -/// Evaluates constraints for shift operations on the CPU side: -/// the shifting factor is read from memory when displacement < 2^32. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let is_shift = lv.op.shift; - let displacement = lv.mem_channels[0]; - let two_exp = lv.mem_channels[2]; - - let shift_table_segment = F::from_canonical_usize(Segment::ShiftTable.unscale()); - - // Only lookup the shifting factor when displacement is < 2^32. - // two_exp.used is true (1) if the high limbs of the displacement are - // zero and false (0) otherwise. - let high_limbs_are_zero = two_exp.used; - let one = builder.one_extension(); - let t = builder.sub_extension(two_exp.is_read, one); - let t = builder.mul_extension(high_limbs_are_zero, t); - let t = builder.mul_extension(is_shift, t); - yield_constr.constraint(builder, t); - - let high_limbs_sum = builder.add_many_extension(&displacement.value[1..]); - let high_limbs_sum_inv = lv.general.shift().high_limb_sum_inv; - // Verify that high_limbs_are_zero = 0 implies high_limbs_sum != 0 and - // high_limbs_are_zero = 1 implies high_limbs_sum = 0. - let t = builder.one_extension(); - let t = builder.sub_extension(t, high_limbs_are_zero); - let t = builder.mul_sub_extension(high_limbs_sum, high_limbs_sum_inv, t); - let t = builder.mul_extension(is_shift, t); - yield_constr.constraint(builder, t); - - let t = builder.mul_many_extension([is_shift, high_limbs_sum, high_limbs_are_zero]); - yield_constr.constraint(builder, t); - - // When the shift displacement is < 2^32, constrain the two_exp - // mem_channel to be the entry corresponding to `displacement` in - // the shift table lookup (will be zero if displacement >= 256). - let t = builder.mul_extension(is_shift, two_exp.addr_context); - yield_constr.constraint(builder, t); - let t = builder.arithmetic_extension( - F::ONE, - -shift_table_segment, - is_shift, - two_exp.addr_segment, - is_shift, - ); - yield_constr.constraint(builder, t); - let t = builder.sub_extension(two_exp.addr_virtual, displacement.value[0]); - let t = builder.mul_extension(is_shift, t); - yield_constr.constraint(builder, t); - - // Other channels must be unused - for chan in &lv.mem_channels[3..NUM_GP_CHANNELS] { - let t = builder.mul_extension(is_shift, chan.used); - yield_constr.constraint(builder, t); - } -} diff --git a/evm/src/cpu/simple_logic/eq_iszero.rs b/evm/src/cpu/simple_logic/eq_iszero.rs deleted file mode 100644 index 43333fd9ed..0000000000 --- a/evm/src/cpu/simple_logic/eq_iszero.rs +++ /dev/null @@ -1,188 +0,0 @@ -use ethereum_types::U256; -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::stack::{self, EQ_STACK_BEHAVIOR, IS_ZERO_STACK_BEHAVIOR}; - -fn limbs(x: U256) -> [u32; 8] { - let mut res = [0; 8]; - let x_u64: [u64; 4] = x.0; - for i in 0..4 { - res[2 * i] = x_u64[i] as u32; - res[2 * i + 1] = (x_u64[i] >> 32) as u32; - } - res -} -/// Form `diff_pinv`. -/// Let `diff = val0 - val1`. Consider `x[i] = diff[i]^-1` if `diff[i] != 0` and 0 otherwise. -/// Then `diff @ x = num_unequal_limbs`, where `@` denotes the dot product. We set -/// `diff_pinv = num_unequal_limbs^-1 * x` if `num_unequal_limbs != 0` and 0 otherwise. We have -/// `diff @ diff_pinv = 1 - equal` as desired. -pub(crate) fn generate_pinv_diff(val0: U256, val1: U256, lv: &mut CpuColumnsView) { - let val0_limbs = limbs(val0).map(F::from_canonical_u32); - let val1_limbs = limbs(val1).map(F::from_canonical_u32); - - let num_unequal_limbs = izip!(val0_limbs, val1_limbs) - .map(|(limb0, limb1)| (limb0 != limb1) as usize) - .sum(); - - // Form `diff_pinv`. - let logic = lv.general.logic_mut(); - let num_unequal_limbs_inv = F::from_canonical_usize(num_unequal_limbs) - .try_inverse() - .unwrap_or(F::ZERO); - for (limb_pinv, limb0, limb1) in izip!(logic.diff_pinv.iter_mut(), val0_limbs, val1_limbs) { - *limb_pinv = (limb0 - limb1).try_inverse().unwrap_or(F::ZERO) * num_unequal_limbs_inv; - } -} - -/// Evaluates the constraints for EQ and ISZERO. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let logic = lv.general.logic(); - let input0 = lv.mem_channels[0].value; - let input1 = lv.mem_channels[1].value; - let output = nv.mem_channels[0].value; - - // EQ (0x14) and ISZERO (0x15) are differentiated by their first opcode bit. - let eq_filter = lv.op.eq_iszero * (P::ONES - lv.opcode_bits[0]); - let iszero_filter = lv.op.eq_iszero * lv.opcode_bits[0]; - let eq_or_iszero_filter = lv.op.eq_iszero; - - let equal = output[0]; - let unequal = P::ONES - equal; - - // Handle `EQ` and `ISZERO`. Most limbs of the output are 0, but the least-significant one is - // either 0 or 1. - yield_constr.constraint(eq_or_iszero_filter * equal * unequal); - for &limb in &output[1..] { - yield_constr.constraint(eq_or_iszero_filter * limb); - } - - // If `ISZERO`, constrain input1 to be zero, effectively implementing ISZERO(x) as EQ(x, 0). - for limb in input1 { - yield_constr.constraint(iszero_filter * limb); - } - - // `equal` implies `input0[i] == input1[i]` for all `i`. - for (limb0, limb1) in izip!(input0, input1) { - let diff = limb0 - limb1; - yield_constr.constraint(eq_or_iszero_filter * equal * diff); - } - - // `input0[i] == input1[i]` for all `i` implies `equal`. - // If `unequal`, find `diff_pinv` such that `(input0 - input1) @ diff_pinv == 1`, where `@` - // denotes the dot product (there will be many such `diff_pinv`). This can only be done if - // `input0 != input1`. - let dot: P = izip!(input0, input1, logic.diff_pinv) - .map(|(limb0, limb1, diff_pinv_el)| (limb0 - limb1) * diff_pinv_el) - .sum(); - yield_constr.constraint(eq_or_iszero_filter * (dot - unequal)); - - // Stack constraints. - stack::eval_packed_one(lv, nv, eq_filter, EQ_STACK_BEHAVIOR.unwrap(), yield_constr); - stack::eval_packed_one( - lv, - nv, - iszero_filter, - IS_ZERO_STACK_BEHAVIOR.unwrap(), - yield_constr, - ); -} - -/// Circuit version of `eval_packed`. -/// Evaluates the constraints for EQ and ISZERO. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let zero = builder.zero_extension(); - let one = builder.one_extension(); - - let logic = lv.general.logic(); - let input0 = lv.mem_channels[0].value; - let input1 = lv.mem_channels[1].value; - let output = nv.mem_channels[0].value; - - // EQ (0x14) and ISZERO (0x15) are differentiated by their first opcode bit. - let eq_filter = builder.mul_extension(lv.op.eq_iszero, lv.opcode_bits[0]); - let eq_filter = builder.sub_extension(lv.op.eq_iszero, eq_filter); - - let iszero_filter = builder.mul_extension(lv.op.eq_iszero, lv.opcode_bits[0]); - let eq_or_iszero_filter = lv.op.eq_iszero; - - let equal = output[0]; - let unequal = builder.sub_extension(one, equal); - - // Handle `EQ` and `ISZERO`. Most limbs of the output are 0, but the least-significant one is - // either 0 or 1. - { - let constr = builder.mul_extension(equal, unequal); - let constr = builder.mul_extension(eq_or_iszero_filter, constr); - yield_constr.constraint(builder, constr); - } - for &limb in &output[1..] { - let constr = builder.mul_extension(eq_or_iszero_filter, limb); - yield_constr.constraint(builder, constr); - } - - // If `ISZERO`, constrain input1 to be zero, effectively implementing ISZERO(x) as EQ(x, 0). - for limb in input1 { - let constr = builder.mul_extension(iszero_filter, limb); - yield_constr.constraint(builder, constr); - } - - // `equal` implies `input0[i] == input1[i]` for all `i`. - for (limb0, limb1) in izip!(input0, input1) { - let diff = builder.sub_extension(limb0, limb1); - let constr = builder.mul_extension(equal, diff); - let constr = builder.mul_extension(eq_or_iszero_filter, constr); - yield_constr.constraint(builder, constr); - } - - // `input0[i] == input1[i]` for all `i` implies `equal`. - // If `unequal`, find `diff_pinv` such that `(input0 - input1) @ diff_pinv == 1`, where `@` - // denotes the dot product (there will be many such `diff_pinv`). This can only be done if - // `input0 != input1`. - { - let dot: ExtensionTarget = izip!(input0, input1, logic.diff_pinv).fold( - zero, - |cumul, (limb0, limb1, diff_pinv_el)| { - let diff = builder.sub_extension(limb0, limb1); - builder.mul_add_extension(diff, diff_pinv_el, cumul) - }, - ); - let constr = builder.sub_extension(dot, unequal); - let constr = builder.mul_extension(eq_or_iszero_filter, constr); - yield_constr.constraint(builder, constr); - } - - // Stack constraints. - stack::eval_ext_circuit_one( - builder, - lv, - nv, - eq_filter, - EQ_STACK_BEHAVIOR.unwrap(), - yield_constr, - ); - stack::eval_ext_circuit_one( - builder, - lv, - nv, - iszero_filter, - IS_ZERO_STACK_BEHAVIOR.unwrap(), - yield_constr, - ); -} diff --git a/evm/src/cpu/simple_logic/mod.rs b/evm/src/cpu/simple_logic/mod.rs deleted file mode 100644 index 748930f2ee..0000000000 --- a/evm/src/cpu/simple_logic/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub(crate) mod eq_iszero; -mod not; - -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; - -/// Evaluates constraints for NOT, EQ and ISZERO. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - not::eval_packed(lv, nv, yield_constr); - eq_iszero::eval_packed(lv, nv, yield_constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for NOT, EQ and ISZERO. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - not::eval_ext_circuit(builder, lv, nv, yield_constr); - eq_iszero::eval_ext_circuit(builder, lv, nv, yield_constr); -} diff --git a/evm/src/cpu/simple_logic/not.rs b/evm/src/cpu/simple_logic/not.rs deleted file mode 100644 index 92b1156807..0000000000 --- a/evm/src/cpu/simple_logic/not.rs +++ /dev/null @@ -1,66 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::stack; - -const LIMB_SIZE: usize = 32; -const ALL_1_LIMB: u64 = (1 << LIMB_SIZE) - 1; - -/// Evaluates constraints for NOT. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - // This is simple: just do output = 0xffffffff - input. - let input = lv.mem_channels[0].value; - let output = nv.mem_channels[0].value; - let filter = lv.op.not_pop * lv.opcode_bits[0]; - for (input_limb, output_limb) in input.into_iter().zip(output) { - yield_constr.constraint( - filter * (output_limb + input_limb - P::Scalar::from_canonical_u64(ALL_1_LIMB)), - ); - } - - // Stack constraints. - stack::eval_packed_one(lv, nv, filter, stack::BASIC_UNARY_OP.unwrap(), yield_constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for NOT. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let input = lv.mem_channels[0].value; - let output = nv.mem_channels[0].value; - let filter = builder.mul_extension(lv.op.not_pop, lv.opcode_bits[0]); - for (input_limb, output_limb) in input.into_iter().zip(output) { - let constr = builder.add_extension(output_limb, input_limb); - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_u64(ALL_1_LIMB), - filter, - constr, - filter, - ); - yield_constr.constraint(builder, constr); - } - - // Stack constraints. - stack::eval_ext_circuit_one( - builder, - lv, - nv, - filter, - stack::BASIC_UNARY_OP.unwrap(), - yield_constr, - ); -} diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs deleted file mode 100644 index e135e39175..0000000000 --- a/evm/src/cpu/stack.rs +++ /dev/null @@ -1,718 +0,0 @@ -use core::cmp::max; - -use itertools::izip; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::ops::OpsColumnsView; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::memory::segments::Segment; - -pub(crate) const MAX_USER_STACK_SIZE: usize = 1024; - -// We check for stack overflows here. An overflow occurs when the stack length is 1025 in user mode, -// which can happen after a non-kernel-only, non-popping, pushing instruction/syscall. -// The check uses `stack_len_bounds_aux`, which is either 0 if next row's `stack_len` is 1025 or -// next row is in kernel mode, or the inverse of `nv.stack_len - 1025` otherwise. -pub(crate) const MIGHT_OVERFLOW: OpsColumnsView = OpsColumnsView { - binary_op: false, - ternary_op: false, - fp254_op: false, - eq_iszero: false, - logic_op: false, - not_pop: false, - shift: false, - jumpdest_keccak_general: false, - push_prover_input: true, // PROVER_INPUT doesn't require the check, but PUSH does. - jumps: false, - pc_push0: true, - dup_swap: true, - context_op: false, - m_op_32bytes: false, - exit_kernel: true, // Doesn't directly push, but the syscall it's returning from might. - m_op_general: false, - syscall: false, - exception: false, -}; - -/// Structure to represent opcodes stack behaviours: -/// - number of pops -/// - whether the opcode(s) push -/// - whether unused channels should be disabled. -#[derive(Clone, Copy)] -pub(crate) struct StackBehavior { - pub(crate) num_pops: usize, - pub(crate) pushes: bool, - disable_other_channels: bool, -} - -/// `StackBehavior` for unary operations. -pub(crate) const BASIC_UNARY_OP: Option = Some(StackBehavior { - num_pops: 1, - pushes: true, - disable_other_channels: true, -}); -/// `StackBehavior` for binary operations. -const BASIC_BINARY_OP: Option = Some(StackBehavior { - num_pops: 2, - pushes: true, - disable_other_channels: true, -}); -/// `StackBehavior` for ternary operations. -const BASIC_TERNARY_OP: Option = Some(StackBehavior { - num_pops: 3, - pushes: true, - disable_other_channels: true, -}); -/// `StackBehavior` for JUMP. -pub(crate) const JUMP_OP: Option = Some(StackBehavior { - num_pops: 1, - pushes: false, - disable_other_channels: false, -}); -/// `StackBehavior` for JUMPI. -pub(crate) const JUMPI_OP: Option = Some(StackBehavior { - num_pops: 2, - pushes: false, - disable_other_channels: false, -}); -/// `StackBehavior` for MLOAD_GENERAL. -pub(crate) const MLOAD_GENERAL_OP: Option = Some(StackBehavior { - num_pops: 1, - pushes: true, - disable_other_channels: false, -}); - -pub(crate) const KECCAK_GENERAL_OP: StackBehavior = StackBehavior { - num_pops: 2, - pushes: true, - disable_other_channels: true, -}; - -pub(crate) const JUMPDEST_OP: StackBehavior = StackBehavior { - num_pops: 0, - pushes: false, - disable_other_channels: true, -}; - -// AUDITORS: If the value below is `None`, then the operation must be manually checked to ensure -// that every general-purpose memory channel is either disabled or has its read flag and address -// properly constrained. The same applies when `disable_other_channels` is set to `false`, -// except the first `num_pops` and the last `pushes as usize` channels have their read flag and -// address constrained automatically in this file. -pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsColumnsView { - binary_op: BASIC_BINARY_OP, - ternary_op: BASIC_TERNARY_OP, - fp254_op: BASIC_BINARY_OP, - eq_iszero: None, // EQ is binary, IS_ZERO is unary. - logic_op: BASIC_BINARY_OP, - not_pop: None, - shift: Some(StackBehavior { - num_pops: 2, - pushes: true, - disable_other_channels: false, - }), - jumpdest_keccak_general: None, - push_prover_input: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: true, - }), - jumps: None, // Depends on whether it's a JUMP or a JUMPI. - pc_push0: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: true, - }), - dup_swap: None, - context_op: None, - m_op_32bytes: Some(StackBehavior { - num_pops: 2, - pushes: true, - disable_other_channels: false, - }), - exit_kernel: Some(StackBehavior { - num_pops: 1, - pushes: false, - disable_other_channels: true, - }), - m_op_general: None, - syscall: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: false, - }), - exception: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: false, - }), -}; - -/// Stack behavior for EQ. -pub(crate) const EQ_STACK_BEHAVIOR: Option = Some(StackBehavior { - num_pops: 2, - pushes: true, - disable_other_channels: true, -}); -/// Stack behavior for ISZERO. -pub(crate) const IS_ZERO_STACK_BEHAVIOR: Option = Some(StackBehavior { - num_pops: 1, - pushes: true, - disable_other_channels: true, -}); - -/// Evaluates constraints for one `StackBehavior`. -pub(crate) fn eval_packed_one( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - filter: P, - stack_behavior: StackBehavior, - yield_constr: &mut ConstraintConsumer

, -) { - // If you have pops. - if stack_behavior.num_pops > 0 { - for i in 1..stack_behavior.num_pops { - let channel = lv.mem_channels[i]; - - yield_constr.constraint(filter * (channel.used - P::ONES)); - yield_constr.constraint(filter * (channel.is_read - P::ONES)); - - yield_constr.constraint(filter * (channel.addr_context - lv.context)); - yield_constr.constraint( - filter - * (channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. - let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); - yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); - } - - // You can't have a write of the top of the stack, so you disable the corresponding flag. - yield_constr.constraint(filter * lv.partial_channel.used); - - // If you also push, you don't need to read the new top of the stack. - // If you don't: - // - if the stack isn't empty after the pops, you read the new top from an extra pop. - // - if not, the extra read is disabled. - // These are transition constraints: they don't apply to the last row. - if !stack_behavior.pushes { - // If stack_len != N... - let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(stack_behavior.num_pops); - let new_filter = len_diff * filter; - // Read an extra element. - let channel = nv.mem_channels[0]; - yield_constr.constraint_transition(new_filter * (channel.used - P::ONES)); - yield_constr.constraint_transition(new_filter * (channel.is_read - P::ONES)); - yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); - yield_constr.constraint_transition( - new_filter - * (channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - let addr_virtual = nv.stack_len - P::ONES; - yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); - // Constrain `stack_inv_aux`. - yield_constr.constraint( - filter - * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - // Disable channel if stack_len == N. - let empty_stack_filter = filter * (lv.general.stack().stack_inv_aux - P::ONES); - yield_constr.constraint_transition(empty_stack_filter * channel.used); - } - } - // If the op only pushes, you only need to constrain the top of the stack if the stack isn't empty. - else if stack_behavior.pushes { - // If len > 0... - let new_filter = lv.stack_len * filter; - // You write the previous top of the stack in memory, in the partial channel. - // The value will be checked with the CTL. - let channel = lv.partial_channel; - yield_constr.constraint(new_filter * (channel.used - P::ONES)); - yield_constr.constraint(new_filter * channel.is_read); - yield_constr.constraint(new_filter * (channel.addr_context - lv.context)); - yield_constr.constraint( - new_filter - * (channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - let addr_virtual = lv.stack_len - P::ONES; - yield_constr.constraint(new_filter * (channel.addr_virtual - addr_virtual)); - // Else you disable the channel. - yield_constr.constraint( - filter - * (lv.stack_len * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - let empty_stack_filter = filter * (lv.general.stack().stack_inv_aux - P::ONES); - yield_constr.constraint(empty_stack_filter * channel.used); - } - // If the op doesn't pop nor push, the top of the stack must not change. - else { - yield_constr.constraint(filter * nv.mem_channels[0].used); - for (limb_old, limb_new) in lv.mem_channels[0] - .value - .iter() - .zip(nv.mem_channels[0].value.iter()) - { - yield_constr.constraint(filter * (*limb_old - *limb_new)); - } - - // You can't have a write of the top of the stack, so you disable the corresponding flag. - yield_constr.constraint(filter * lv.partial_channel.used); - } - - // Unused channels - if stack_behavior.disable_other_channels { - // The first channel contains (or not) the top of the stack and is constrained elsewhere. - for i in max(1, stack_behavior.num_pops)..NUM_GP_CHANNELS - (stack_behavior.pushes as usize) - { - let channel = lv.mem_channels[i]; - yield_constr.constraint(filter * channel.used); - } - } - - // Constrain new stack length. - let num_pops = P::Scalar::from_canonical_usize(stack_behavior.num_pops); - let push = P::Scalar::from_canonical_usize(stack_behavior.pushes as usize); - yield_constr.constraint_transition(filter * (nv.stack_len - (lv.stack_len - num_pops + push))); -} - -/// Evaluates constraints for all opcodes' `StackBehavior`s. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - for (op, stack_behavior, might_overflow) in izip!( - lv.op.into_iter(), - STACK_BEHAVIORS.into_iter(), - MIGHT_OVERFLOW.into_iter() - ) { - if let Some(stack_behavior) = stack_behavior { - eval_packed_one(lv, nv, op, stack_behavior, yield_constr); - } - - if might_overflow { - // Check for stack overflow in the next row. - let diff = nv.stack_len - P::Scalar::from_canonical_usize(MAX_USER_STACK_SIZE + 1); - let lhs = diff * lv.general.stack().stack_len_bounds_aux; - let rhs = P::ONES - nv.is_kernel_mode; - yield_constr.constraint_transition(op * (lhs - rhs)); - } - } - - // Constrain stack for JUMPDEST. - let jumpdest_filter = lv.op.jumpdest_keccak_general * lv.opcode_bits[1]; - eval_packed_one(lv, nv, jumpdest_filter, JUMPDEST_OP, yield_constr); - - // Constrain stack for KECCAK_GENERAL. - let keccak_general_filter = lv.op.jumpdest_keccak_general * (P::ONES - lv.opcode_bits[1]); - eval_packed_one( - lv, - nv, - keccak_general_filter, - KECCAK_GENERAL_OP, - yield_constr, - ); - - // Stack constraints for POP. - // The only constraints POP has are stack constraints. - // Since POP and NOT are combined into one flag and they have - // different stack behaviors, POP needs special stack constraints. - // Constrain `stack_inv_aux`. - let len_diff = lv.stack_len - P::Scalar::ONES; - yield_constr.constraint( - lv.op.not_pop - * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), - ); - - // If stack_len != 1 and POP, read new top of the stack in nv.mem_channels[0]. - let top_read_channel = nv.mem_channels[0]; - let is_top_read = lv.general.stack().stack_inv_aux * (P::ONES - lv.opcode_bits[0]); - - // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * (1 - opcode_bits[0])`. - yield_constr.constraint(lv.op.not_pop * (lv.general.stack().stack_inv_aux_2 - is_top_read)); - let new_filter = lv.op.not_pop * lv.general.stack().stack_inv_aux_2; - yield_constr.constraint_transition(new_filter * (top_read_channel.used - P::ONES)); - yield_constr.constraint_transition(new_filter * (top_read_channel.is_read - P::ONES)); - yield_constr.constraint_transition(new_filter * (top_read_channel.addr_context - nv.context)); - yield_constr.constraint_transition( - new_filter - * (top_read_channel.addr_segment - - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), - ); - let addr_virtual = nv.stack_len - P::ONES; - yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); - // If stack_len == 1 or NOT, disable the channel. - // If NOT or (len==1 and POP), then `stack_inv_aux_2` = 0. - yield_constr.constraint( - lv.op.not_pop * (lv.general.stack().stack_inv_aux_2 - P::ONES) * top_read_channel.used, - ); - - // Disable remaining memory channels. - for &channel in &lv.mem_channels[1..] { - yield_constr.constraint(lv.op.not_pop * (lv.opcode_bits[0] - P::ONES) * channel.used); - } - yield_constr - .constraint(lv.op.not_pop * (lv.opcode_bits[0] - P::ONES) * lv.partial_channel.used); - - // Constrain the new stack length for POP. - yield_constr.constraint_transition( - lv.op.not_pop * (lv.opcode_bits[0] - P::ONES) * (nv.stack_len - lv.stack_len + P::ONES), - ); -} - -/// Circuit version of `eval_packed_one`. -/// Evaluates constraints for one `StackBehavior`. -pub(crate) fn eval_ext_circuit_one, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - filter: ExtensionTarget, - stack_behavior: StackBehavior, - yield_constr: &mut RecursiveConstraintConsumer, -) { - // If you have pops. - if stack_behavior.num_pops > 0 { - for i in 1..stack_behavior.num_pops { - let channel = lv.mem_channels[i]; - - { - let constr = builder.mul_sub_extension(filter, channel.used, filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_sub_extension(filter, channel.is_read, filter); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_context, lv.context); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::Stack.unscale()), - filter, - channel.addr_segment, - filter, - ); - yield_constr.constraint(builder, constr); - } - // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. - { - let diff = builder.sub_extension(channel.addr_virtual, lv.stack_len); - let constr = builder.arithmetic_extension( - F::ONE, - F::from_canonical_usize(i + 1), - filter, - diff, - filter, - ); - yield_constr.constraint(builder, constr); - } - } - - // You can't have a write of the top of the stack, so you disable the corresponding flag. - { - let constr = builder.mul_extension(filter, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } - - // If you also push, you don't need to read the new top of the stack. - // If you don't: - // - if the stack isn't empty after the pops, you read the new top from an extra pop. - // - if not, the extra read is disabled. - // These are transition constraints: they don't apply to the last row. - if !stack_behavior.pushes { - // If stack_len != N... - let target_num_pops = - builder.constant_extension(F::from_canonical_usize(stack_behavior.num_pops).into()); - let len_diff = builder.sub_extension(lv.stack_len, target_num_pops); - let new_filter = builder.mul_extension(filter, len_diff); - // Read an extra element. - let channel = nv.mem_channels[0]; - - { - let constr = builder.mul_sub_extension(new_filter, channel.used, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.mul_sub_extension(new_filter, channel.is_read, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_context, nv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::Stack.unscale()), - new_filter, - channel.addr_segment, - new_filter, - ); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_virtual, nv.stack_len); - let constr = - builder.arithmetic_extension(F::ONE, F::ONE, new_filter, diff, new_filter); - yield_constr.constraint_transition(builder, constr); - } - // Constrain `stack_inv_aux`. - { - let prod = builder.mul_extension(len_diff, lv.general.stack().stack_inv); - let diff = builder.sub_extension(prod, lv.general.stack().stack_inv_aux); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - // Disable channel if stack_len == N. - { - let empty_stack_filter = - builder.mul_sub_extension(filter, lv.general.stack().stack_inv_aux, filter); - let constr = builder.mul_extension(empty_stack_filter, channel.used); - yield_constr.constraint_transition(builder, constr); - } - } - } - // If the op only pushes, you only need to constrain the top of the stack if the stack isn't empty. - else if stack_behavior.pushes { - // If len > 0... - let new_filter = builder.mul_extension(lv.stack_len, filter); - // You write the previous top of the stack in memory, in the last channel. - // The value will be checked with the CTL - let channel = lv.partial_channel; - { - let constr = builder.mul_sub_extension(new_filter, channel.used, new_filter); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(new_filter, channel.is_read); - yield_constr.constraint(builder, constr); - } - - { - let diff = builder.sub_extension(channel.addr_context, lv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -F::from_canonical_usize(Segment::Stack.unscale()), - new_filter, - channel.addr_segment, - new_filter, - ); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(channel.addr_virtual, lv.stack_len); - let constr = builder.arithmetic_extension(F::ONE, F::ONE, new_filter, diff, new_filter); - yield_constr.constraint(builder, constr); - } - // Else you disable the channel. - { - let diff = builder.mul_extension(lv.stack_len, lv.general.stack().stack_inv); - let diff = builder.sub_extension(diff, lv.general.stack().stack_inv_aux); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - { - let empty_stack_filter = - builder.mul_sub_extension(filter, lv.general.stack().stack_inv_aux, filter); - let constr = builder.mul_extension(empty_stack_filter, channel.used); - yield_constr.constraint(builder, constr); - } - } - // If the op doesn't pop nor push, the top of the stack must not change. - else { - { - let constr = builder.mul_extension(filter, nv.mem_channels[0].used); - yield_constr.constraint(builder, constr); - } - { - for (limb_old, limb_new) in lv.mem_channels[0] - .value - .iter() - .zip(nv.mem_channels[0].value.iter()) - { - let diff = builder.sub_extension(*limb_old, *limb_new); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint(builder, constr); - } - } - - // You can't have a write of the top of the stack, so you disable the corresponding flag. - { - let constr = builder.mul_extension(filter, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } - } - - // Unused channels - if stack_behavior.disable_other_channels { - // The first channel contains (or not) the top of the stack and is constrained elsewhere. - for i in max(1, stack_behavior.num_pops)..NUM_GP_CHANNELS - (stack_behavior.pushes as usize) - { - let channel = lv.mem_channels[i]; - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - } - - // Constrain new stack length. - let diff = builder.constant_extension( - F::Extension::from_canonical_usize(stack_behavior.num_pops) - - F::Extension::from_canonical_usize(stack_behavior.pushes as usize), - ); - let diff = builder.sub_extension(lv.stack_len, diff); - let diff = builder.sub_extension(nv.stack_len, diff); - let constr = builder.mul_extension(filter, diff); - yield_constr.constraint_transition(builder, constr); -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for all opcodes' `StackBehavior`s. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - for (op, stack_behavior, might_overflow) in izip!( - lv.op.into_iter(), - STACK_BEHAVIORS.into_iter(), - MIGHT_OVERFLOW.into_iter() - ) { - if let Some(stack_behavior) = stack_behavior { - eval_ext_circuit_one(builder, lv, nv, op, stack_behavior, yield_constr); - } - - if might_overflow { - // Check for stack overflow in the next row. - let diff = builder.add_const_extension( - nv.stack_len, - -F::from_canonical_usize(MAX_USER_STACK_SIZE + 1), - ); - let prod = builder.mul_add_extension( - diff, - lv.general.stack().stack_len_bounds_aux, - nv.is_kernel_mode, - ); - let rhs = builder.add_const_extension(prod, -F::ONE); - let constr = builder.mul_extension(op, rhs); - yield_constr.constraint_transition(builder, constr); - } - } - - // Constrain stack for JUMPDEST. - let jumpdest_filter = builder.mul_extension(lv.op.jumpdest_keccak_general, lv.opcode_bits[1]); - eval_ext_circuit_one(builder, lv, nv, jumpdest_filter, JUMPDEST_OP, yield_constr); - - // Constrain stack for KECCAK_GENERAL. - let one = builder.one_extension(); - let mut keccak_general_filter = builder.sub_extension(one, lv.opcode_bits[1]); - keccak_general_filter = - builder.mul_extension(lv.op.jumpdest_keccak_general, keccak_general_filter); - eval_ext_circuit_one( - builder, - lv, - nv, - keccak_general_filter, - KECCAK_GENERAL_OP, - yield_constr, - ); - - // Stack constraints for POP. - // The only constraints POP has are stack constraints. - // Since POP and NOT are combined into one flag and they have - // different stack behaviors, POP needs special stack constraints. - // Constrain `stack_inv_aux`. - { - let len_diff = builder.add_const_extension(lv.stack_len, F::NEG_ONE); - let diff = builder.mul_sub_extension( - len_diff, - lv.general.stack().stack_inv, - lv.general.stack().stack_inv_aux, - ); - let constr = builder.mul_extension(lv.op.not_pop, diff); - yield_constr.constraint(builder, constr); - } - // If stack_len != 4 and MSTORE, read new top of the stack in nv.mem_channels[0]. - let top_read_channel = nv.mem_channels[0]; - let is_top_read = builder.mul_extension(lv.general.stack().stack_inv_aux, lv.opcode_bits[0]); - let is_top_read = builder.sub_extension(lv.general.stack().stack_inv_aux, is_top_read); - // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * opcode_bits[0]`. - { - let diff = builder.sub_extension(lv.general.stack().stack_inv_aux_2, is_top_read); - let constr = builder.mul_extension(lv.op.not_pop, diff); - yield_constr.constraint(builder, constr); - } - let new_filter = builder.mul_extension(lv.op.not_pop, lv.general.stack().stack_inv_aux_2); - { - let constr = builder.mul_sub_extension(new_filter, top_read_channel.used, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let constr = builder.mul_sub_extension(new_filter, top_read_channel.is_read, new_filter); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.sub_extension(top_read_channel.addr_context, nv.context); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let diff = builder.add_const_extension( - top_read_channel.addr_segment, - -F::from_canonical_usize(Segment::Stack.unscale()), - ); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - { - let addr_virtual = builder.add_const_extension(nv.stack_len, -F::ONE); - let diff = builder.sub_extension(top_read_channel.addr_virtual, addr_virtual); - let constr = builder.mul_extension(new_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - // If stack_len == 1 or NOT, disable the channel. - { - let diff = builder.mul_sub_extension( - lv.op.not_pop, - lv.general.stack().stack_inv_aux_2, - lv.op.not_pop, - ); - let constr = builder.mul_extension(diff, top_read_channel.used); - yield_constr.constraint(builder, constr); - } - - // Disable remaining memory channels. - let filter = builder.mul_sub_extension(lv.op.not_pop, lv.opcode_bits[0], lv.op.not_pop); - for &channel in &lv.mem_channels[1..] { - let constr = builder.mul_extension(filter, channel.used); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.mul_extension(filter, lv.partial_channel.used); - yield_constr.constraint(builder, constr); - } - - // Constrain the new stack length for POP. - let diff = builder.sub_extension(nv.stack_len, lv.stack_len); - let mut constr = builder.add_const_extension(diff, F::ONES); - constr = builder.mul_extension(filter, constr); - yield_constr.constraint_transition(builder, constr); -} diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs deleted file mode 100644 index cf7aa72e0f..0000000000 --- a/evm/src/cpu/syscalls_exceptions.rs +++ /dev/null @@ -1,308 +0,0 @@ -//! Handle instructions that are implemented in terms of system calls. -//! -//! These are usually the ones that are too complicated to implement in one CPU table row. - -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; - -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::memory::segments::Segment; - -// Copy the constant but make it `usize`. -const BYTES_PER_OFFSET: usize = crate::cpu::kernel::assembler::BYTES_PER_OFFSET as usize; - -/// Evaluates constraints for syscalls and exceptions. -pub(crate) fn eval_packed( - lv: &CpuColumnsView

, - nv: &CpuColumnsView

, - yield_constr: &mut ConstraintConsumer

, -) { - let filter_syscall = lv.op.syscall; - let filter_exception = lv.op.exception; - let total_filter = filter_syscall + filter_exception; - - // First, constrain filters to be boolean. - // Ensuring they are mutually exclusive is done in other modules - // through the `is_cpu_cycle` variable. - yield_constr.constraint(filter_syscall * (filter_syscall - P::ONES)); - yield_constr.constraint(filter_exception * (filter_exception - P::ONES)); - - // If exception, ensure we are not in kernel mode - yield_constr.constraint(filter_exception * lv.is_kernel_mode); - - // Get the exception code as an value in {0, ..., 7}. - let exc_code_bits = lv.general.exception().exc_code_bits; - let exc_code: P = exc_code_bits - .into_iter() - .enumerate() - .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum(); - // Ensure that all bits are either 0 or 1. - for bit in exc_code_bits { - yield_constr.constraint(filter_exception * bit * (bit - P::ONES)); - } - - // Look up the handler in memory - let code_segment = P::Scalar::from_canonical_usize(Segment::Code.unscale()); - - let opcode: P = lv - .opcode_bits - .into_iter() - .enumerate() - .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum(); - - // Syscall handler - let syscall_jumptable_start = - P::Scalar::from_canonical_usize(KERNEL.global_labels["syscall_jumptable"]); - let opcode_handler_addr_start = - syscall_jumptable_start + opcode * P::Scalar::from_canonical_usize(BYTES_PER_OFFSET); - // Exceptions handler - let exc_jumptable_start = - P::Scalar::from_canonical_usize(KERNEL.global_labels["exception_jumptable"]); - let exc_handler_addr_start = - exc_jumptable_start + exc_code * P::Scalar::from_canonical_usize(BYTES_PER_OFFSET); - - let jumpdest_channel = lv.mem_channels[1]; - - // Set `used` and `is_read`. - // The channel is not used: the reads will be done with the byte packing CTL. - yield_constr.constraint(total_filter * (jumpdest_channel.used)); - yield_constr.constraint(total_filter * (jumpdest_channel.is_read - P::ONES)); - - // Set kernel context and code segment - yield_constr.constraint(total_filter * jumpdest_channel.addr_context); - yield_constr.constraint(total_filter * (jumpdest_channel.addr_segment - code_segment)); - - // Set address. - yield_constr - .constraint(filter_syscall * (jumpdest_channel.addr_virtual - opcode_handler_addr_start)); - yield_constr - .constraint(filter_exception * (jumpdest_channel.addr_virtual - exc_handler_addr_start)); - - // Set higher limbs to zero. - for &limb in &jumpdest_channel.value[1..] { - yield_constr.constraint(total_filter * limb); - } - - // Disable unused channels - for channel in &lv.mem_channels[2..NUM_GP_CHANNELS] { - yield_constr.constraint(total_filter * channel.used); - } - - // Set program counter to the handler address - yield_constr - .constraint_transition(total_filter * (nv.program_counter - jumpdest_channel.value[0])); - // Set kernel mode - yield_constr.constraint_transition(total_filter * (nv.is_kernel_mode - P::ONES)); - // Reset gas counter to zero. - yield_constr.constraint_transition(total_filter * nv.gas); - - let output = nv.mem_channels[0].value; - // New top of the stack: current PC + 1 (limb 0), kernel flag (limb 1), gas counter (limbs 6 and 7). - yield_constr.constraint(filter_syscall * (output[0] - (lv.program_counter + P::ONES))); - yield_constr.constraint(filter_exception * (output[0] - lv.program_counter)); - // Check the kernel mode, for syscalls only - yield_constr.constraint(filter_syscall * (output[1] - lv.is_kernel_mode)); - yield_constr.constraint(total_filter * (output[6] - lv.gas)); - yield_constr.constraint(total_filter * output[7]); // High limb of gas is zero. - - // Zero the rest of that register - // output[1] is 0 for exceptions, but not for syscalls - yield_constr.constraint(filter_exception * output[1]); - for &limb in &output[2..6] { - yield_constr.constraint(total_filter * limb); - } -} - -/// Circuit version of `eval_packed`. -/// Evaluates constraints for syscalls and exceptions. -pub(crate) fn eval_ext_circuit, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - lv: &CpuColumnsView>, - nv: &CpuColumnsView>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let filter_syscall = lv.op.syscall; - let filter_exception = lv.op.exception; - let total_filter = builder.add_extension(filter_syscall, filter_exception); - - // First, constrain filters to be boolean. - // Ensuring they are mutually exclusive is done in other modules - // through the `is_cpu_cycle` variable. - let constr = builder.mul_sub_extension(filter_syscall, filter_syscall, filter_syscall); - yield_constr.constraint(builder, constr); - let constr = builder.mul_sub_extension(filter_exception, filter_exception, filter_exception); - yield_constr.constraint(builder, constr); - - // Ensure that, if exception, we are not in kernel mode - let constr = builder.mul_extension(filter_exception, lv.is_kernel_mode); - yield_constr.constraint(builder, constr); - - let exc_code_bits = lv.general.exception().exc_code_bits; - let exc_code = - exc_code_bits - .into_iter() - .enumerate() - .fold(builder.zero_extension(), |cumul, (i, bit)| { - builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) - }); - - // Ensure that all bits are either 0 or 1. - for bit in exc_code_bits { - let constr = builder.mul_sub_extension(bit, bit, bit); - let constr = builder.mul_extension(filter_exception, constr); - yield_constr.constraint(builder, constr); - } - - // Look up the handler in memory - let code_segment = F::from_canonical_usize(Segment::Code.unscale()); - - let opcode = lv - .opcode_bits - .into_iter() - .rev() - .fold(builder.zero_extension(), |cumul, bit| { - builder.mul_const_add_extension(F::TWO, cumul, bit) - }); - - // Syscall handler - let syscall_jumptable_start = builder.constant_extension( - F::from_canonical_usize(KERNEL.global_labels["syscall_jumptable"]).into(), - ); - let opcode_handler_addr_start = builder.mul_const_add_extension( - F::from_canonical_usize(BYTES_PER_OFFSET), - opcode, - syscall_jumptable_start, - ); - - // Exceptions handler - let exc_jumptable_start = builder.constant_extension( - F::from_canonical_usize(KERNEL.global_labels["exception_jumptable"]).into(), - ); - let exc_handler_addr_start = builder.mul_const_add_extension( - F::from_canonical_usize(BYTES_PER_OFFSET), - exc_code, - exc_jumptable_start, - ); - - let jumpdest_channel = lv.mem_channels[1]; - - // Set `used` and `is_read`. - // The channel is not used: the reads will be done with the byte packing CTL. - { - let constr = builder.mul_extension(total_filter, jumpdest_channel.used); - yield_constr.constraint(builder, constr); - } - { - let constr = - builder.mul_sub_extension(total_filter, jumpdest_channel.is_read, total_filter); - yield_constr.constraint(builder, constr); - } - - // Set kernel context and code segment - { - let constr = builder.mul_extension(total_filter, jumpdest_channel.addr_context); - yield_constr.constraint(builder, constr); - } - { - let constr = builder.arithmetic_extension( - F::ONE, - -code_segment, - total_filter, - jumpdest_channel.addr_segment, - total_filter, - ); - yield_constr.constraint(builder, constr); - } - - // Set address. - { - let diff_syscall = - builder.sub_extension(jumpdest_channel.addr_virtual, opcode_handler_addr_start); - let constr = builder.mul_extension(filter_syscall, diff_syscall); - yield_constr.constraint(builder, constr); - } - { - let diff_exception = - builder.sub_extension(jumpdest_channel.addr_virtual, exc_handler_addr_start); - let constr = builder.mul_extension(filter_exception, diff_exception); - yield_constr.constraint(builder, constr); - } - - // Set higher limbs to zero. - for &limb in &jumpdest_channel.value[1..] { - let constr = builder.mul_extension(total_filter, limb); - yield_constr.constraint(builder, constr); - } - - // Disable unused channels - for channel in &lv.mem_channels[2..NUM_GP_CHANNELS] { - let constr = builder.mul_extension(total_filter, channel.used); - yield_constr.constraint(builder, constr); - } - - // Set program counter to the handler address - // The addresses are big-endian in memory - { - let diff = builder.sub_extension(nv.program_counter, jumpdest_channel.value[0]); - let constr = builder.mul_extension(total_filter, diff); - yield_constr.constraint_transition(builder, constr); - } - // Set kernel mode - { - let constr = builder.mul_sub_extension(total_filter, nv.is_kernel_mode, total_filter); - yield_constr.constraint_transition(builder, constr); - } - // Reset gas counter to zero. - { - let constr = builder.mul_extension(total_filter, nv.gas); - yield_constr.constraint_transition(builder, constr); - } - - // New top of the stack. - let output = nv.mem_channels[0].value; - // Push to stack (syscall): current PC + 1 (limb 0), kernel flag (limb 1), gas counter (limbs 6 and 7). - { - let pc_plus_1 = builder.add_const_extension(lv.program_counter, F::ONE); - let diff = builder.sub_extension(output[0], pc_plus_1); - let constr = builder.mul_extension(filter_syscall, diff); - yield_constr.constraint(builder, constr); - } - // Push to stack (exception): current PC (limb 0), kernel flag (limb 1), gas counter (limbs 6 and 7). - { - let diff = builder.sub_extension(output[0], lv.program_counter); - let constr = builder.mul_extension(filter_exception, diff); - yield_constr.constraint(builder, constr); - } - // Push to stack(exception): current PC (limb 0), gas counter (limbs 6 and 7). - { - let diff = builder.sub_extension(output[1], lv.is_kernel_mode); - let constr = builder.mul_extension(filter_syscall, diff); - yield_constr.constraint(builder, constr); - } - { - let diff = builder.sub_extension(output[6], lv.gas); - let constr = builder.mul_extension(total_filter, diff); - yield_constr.constraint(builder, constr); - } - { - // High limb of gas is zero. - let constr = builder.mul_extension(total_filter, output[7]); - yield_constr.constraint(builder, constr); - } - - // Zero the rest of that register - let constr = builder.mul_extension(filter_exception, output[1]); - yield_constr.constraint(builder, constr); - for &limb in &output[2..6] { - let constr = builder.mul_extension(total_filter, limb); - yield_constr.constraint(builder, constr); - } -} diff --git a/evm/src/curve_pairings.rs b/evm/src/curve_pairings.rs deleted file mode 100644 index af155cc506..0000000000 --- a/evm/src/curve_pairings.rs +++ /dev/null @@ -1,513 +0,0 @@ -use core::ops::{Add, Mul, Neg}; - -use ethereum_types::U256; -use rand::distributions::Standard; -use rand::prelude::Distribution; -use rand::Rng; - -use crate::extension_tower::{FieldExt, Fp12, Fp2, Fp6, Stack, BN254}; - -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct Curve -where - T: FieldExt, -{ - pub x: T, - pub y: T, -} - -impl Curve { - pub(crate) const fn unit() -> Self { - Curve { - x: T::ZERO, - y: T::ZERO, - } - } -} - -impl Stack for Curve { - const SIZE: usize = 2 * T::SIZE; - - fn to_stack(&self) -> Vec { - let mut stack = self.x.to_stack(); - stack.extend(self.y.to_stack()); - stack - } - - fn from_stack(stack: &[U256]) -> Self { - Curve { - x: T::from_stack(&stack[0..T::SIZE]), - y: T::from_stack(&stack[T::SIZE..2 * T::SIZE]), - } - } -} - -impl Curve -where - T: FieldExt, - Curve: CyclicGroup, -{ - pub(crate) fn int(z: i32) -> Self { - Curve::::GENERATOR * z - } -} - -impl Distribution> for Standard -where - T: FieldExt, - Curve: CyclicGroup, -{ - fn sample(&self, rng: &mut R) -> Curve { - Curve::::GENERATOR * rng.gen::() - } -} - -/// Standard addition formula for elliptic curves, restricted to the cases -/// -impl Add for Curve { - type Output = Self; - - fn add(self, other: Self) -> Self { - if self == Curve::::unit() { - return other; - } - if other == Curve::::unit() { - return self; - } - if self == -other { - return Curve::::unit(); - } - let m = if self == other { - T::new(3) * self.x * self.x / (T::new(2) * self.y) - } else { - (other.y - self.y) / (other.x - self.x) - }; - let x = m * m - (self.x + other.x); - Curve { - x, - y: m * (self.x - x) - self.y, - } - } -} - -impl Neg for Curve { - type Output = Curve; - - fn neg(self) -> Self { - Curve { - x: self.x, - y: -self.y, - } - } -} - -pub trait CyclicGroup { - const GENERATOR: Self; -} - -/// The BN curve consists of pairs -/// (x, y): (BN254, BN254) | y^2 = x^3 + 2 -// with generator given by (1, 2) -impl CyclicGroup for Curve { - const GENERATOR: Curve = Curve { - x: BN254 { val: U256::one() }, - y: BN254 { - val: U256([2, 0, 0, 0]), - }, - }; -} - -impl Mul for Curve -where - T: FieldExt, - Curve: CyclicGroup, -{ - type Output = Curve; - - fn mul(self, other: i32) -> Self { - if other == 0 { - return Curve::::unit(); - } - if self == Curve::::unit() { - return Curve::::unit(); - } - - let mut x: Curve = self; - if other.is_negative() { - x = -x; - } - let mut result = Curve::::unit(); - - let mut exp = other.unsigned_abs() as usize; - while exp > 0 { - if exp % 2 == 1 { - result = result + x; - } - exp >>= 1; - x = x + x; - } - result - } -} - -/// The twisted curve consists of pairs -/// (x, y): (Fp2, Fp2) | y^2 = x^3 + 3/(9 + i) -/// with generator given as follows -impl CyclicGroup for Curve> { - const GENERATOR: Curve> = Curve { - x: Fp2 { - re: BN254 { - val: U256([ - 0x46debd5cd992f6ed, - 0x674322d4f75edadd, - 0x426a00665e5c4479, - 0x1800deef121f1e76, - ]), - }, - im: BN254 { - val: U256([ - 0x97e485b7aef312c2, - 0xf1aa493335a9e712, - 0x7260bfb731fb5d25, - 0x198e9393920d483a, - ]), - }, - }, - y: Fp2 { - re: BN254 { - val: U256([ - 0x4ce6cc0166fa7daa, - 0xe3d1e7690c43d37b, - 0x4aab71808dcb408f, - 0x12c85ea5db8c6deb, - ]), - }, - im: BN254 { - val: U256([ - 0x55acdadcd122975b, - 0xbc4b313370b38ef3, - 0xec9e99ad690c3395, - 0x090689d0585ff075, - ]), - }, - }, - }; -} - -// The tate pairing takes a point each from the curve and its twist and outputs an Fp12 element -pub(crate) fn bn_tate(p: Curve, q: Curve>) -> Fp12 { - let miller_output = bn_miller_loop(p, q); - bn_final_exponent(miller_output) -} - -/// Standard code for miller loop, can be found on page 99 at this url: -/// -/// where BN_EXP is a hardcoding of the array of Booleans that the loop traverses -pub(crate) fn bn_miller_loop(p: Curve, q: Curve>) -> Fp12 { - let mut r = p; - let mut acc: Fp12 = Fp12::::UNIT; - let mut line: Fp12; - - for i in BN_EXP { - line = bn_tangent(r, q); - r = r + r; - acc = line * acc * acc; - if i { - line = bn_cord(p, r, q); - r = r + p; - acc = line * acc; - } - } - acc -} - -/// The sloped line function for doubling a point -pub(crate) fn bn_tangent(p: Curve, q: Curve>) -> Fp12 { - let cx = -BN254::new(3) * p.x * p.x; - let cy = BN254::new(2) * p.y; - bn_sparse_embed(p.y * p.y - BN254::new(9), q.x * cx, q.y * cy) -} - -/// The sloped line function for adding two points -pub(crate) fn bn_cord(p1: Curve, p2: Curve, q: Curve>) -> Fp12 { - let cx = p2.y - p1.y; - let cy = p1.x - p2.x; - bn_sparse_embed(p1.y * p2.x - p2.y * p1.x, q.x * cx, q.y * cy) -} - -/// The tangent and cord functions output sparse Fp12 elements. -/// This map embeds the nonzero coefficients into an Fp12. -pub(crate) const fn bn_sparse_embed(g000: BN254, g01: Fp2, g11: Fp2) -> Fp12 { - let g0 = Fp6 { - t0: Fp2 { - re: g000, - im: BN254::ZERO, - }, - t1: g01, - t2: Fp2::::ZERO, - }; - - let g1 = Fp6 { - t0: Fp2::::ZERO, - t1: g11, - t2: Fp2::::ZERO, - }; - - Fp12 { z0: g0, z1: g1 } -} - -pub(crate) fn gen_bn_fp12_sparse(rng: &mut R) -> Fp12 { - bn_sparse_embed( - rng.gen::(), - rng.gen::>(), - rng.gen::>(), - ) -} - -/// The output y of the miller loop is not an invariant, -/// but one gets an invariant by raising y to the power -/// (p^12 - 1)/N = (p^6 - 1)(p^2 + 1)(p^4 - p^2 + 1)/N -/// where N is the cyclic group order of the curve. -/// To achieve this, we first exponentiate y by p^6 - 1 via -/// y = y_6 / y -/// and then exponentiate the result by p^2 + 1 via -/// y = y_2 * y -/// We then note that (p^4 - p^2 + 1)/N can be rewritten as -/// (p^4 - p^2 + 1)/N = p^3 + (a2)p^2 - (a1)p - a0 -/// where 0 < a0, a1, a2 < p. Then the final power is given by -/// y = y_3 * (y^a2)_2 * (y^-a1)_1 * (y^-a0) -pub(crate) fn bn_final_exponent(f: Fp12) -> Fp12 { - let mut y = f.frob(6) / f; - y = y.frob(2) * y; - let (y_a2, y_a1, y_a0) = get_bn_custom_powers(y); - y.frob(3) * y_a2.frob(2) * y_a1.frob(1) * y_a0 -} - -/// We first together (so as to avoid repeated steps) compute -/// y^a4, y^a2, y^a0 -/// where a1 is given by -/// a1 = a4 + 2a2 - a0 -/// we then invert y^a0 and return -/// y^a2, y^a1 = y^a4 * y^a2 * y^a2 * y^(-a0), y^(-a0) -/// -/// Representing a4, a2, a0 in *little endian* binary, define -/// BN_EXPS4 = [(a4[i], a2[i], a0[i]) for i in 0..len(a4)] -/// BN_EXPS2 = [ (a2[i], a0[i]) for i in len(a4)..len(a2)] -/// BN_EXPS0 = [ a0[i] for i in len(a2)..len(a0)] -fn get_bn_custom_powers(f: Fp12) -> (Fp12, Fp12, Fp12) { - let mut sq: Fp12 = f; - let mut y0: Fp12 = Fp12::::UNIT; - let mut y2: Fp12 = Fp12::::UNIT; - let mut y4: Fp12 = Fp12::::UNIT; - - // proceed via standard squaring algorithm for exponentiation - - // must keep multiplying all three values: a4, a2, a0 - for (a, b, c) in BN_EXPS4 { - if a { - y4 = y4 * sq; - } - if b { - y2 = y2 * sq; - } - if c { - y0 = y0 * sq; - } - sq = sq * sq; - } - // leading term of a4 is always 1 - y4 = y4 * sq; - - // must keep multiplying remaining two values: a2, a0 - for (a, b) in BN_EXPS2 { - if a { - y2 = y2 * sq; - } - if b { - y0 = y0 * sq; - } - sq = sq * sq; - } - // leading term of a2 is always 1 - y2 = y2 * sq; - - // must keep multiplying final remaining value: a0 - for a in BN_EXPS0 { - if a { - y0 = y0 * sq; - } - sq = sq * sq; - } - // leading term of a0 is always 1 - y0 = y0 * sq; - - // invert y0 to compute y^(-a0) - let y0_inv = y0.inv(); - - // return y^a2 = y2, y^a1 = y4 * y2^2 * y^(-a0), y^(-a0) - (y2, y4 * y2 * y2 * y0_inv, y0_inv) -} - -const BN_EXP: [bool; 253] = [ - true, false, false, false, false, false, true, true, false, false, true, false, false, false, - true, false, false, true, true, true, false, false, true, true, true, false, false, true, - false, true, true, true, false, false, false, false, true, false, false, true, true, false, - false, false, true, true, false, true, false, false, false, false, false, false, false, true, - false, true, false, false, true, true, false, true, true, true, false, false, false, false, - true, false, true, false, false, false, false, false, true, false, false, false, true, false, - true, true, false, true, true, false, true, true, false, true, false, false, false, false, - false, false, true, true, false, false, false, false, false, false, true, false, true, false, - true, true, false, false, false, false, true, false, true, true, true, false, true, false, - false, true, false, true, false, false, false, false, false, true, true, false, false, true, - true, true, true, true, false, true, false, false, false, false, true, false, false, true, - false, false, false, false, true, true, true, true, false, false, true, true, false, true, - true, true, false, false, true, false, true, true, true, false, false, false, false, true, - false, false, true, false, false, false, true, false, true, false, false, false, false, true, - true, true, true, true, false, false, false, false, true, true, true, true, true, false, true, - false, true, true, false, false, true, false, false, true, true, true, true, true, true, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, - false, -]; - -// The following constants are defined above get_custom_powers - -const BN_EXPS4: [(bool, bool, bool); 64] = [ - (true, true, false), - (true, true, true), - (true, true, true), - (false, false, false), - (false, false, true), - (true, false, true), - (false, true, false), - (true, false, true), - (true, true, false), - (true, false, true), - (false, true, false), - (true, true, false), - (true, true, false), - (true, true, false), - (false, true, false), - (false, true, false), - (false, false, true), - (true, false, true), - (true, true, false), - (false, true, false), - (true, true, false), - (true, true, false), - (true, true, false), - (false, false, true), - (false, false, true), - (true, false, true), - (true, false, true), - (true, true, false), - (true, false, false), - (true, true, false), - (false, true, false), - (true, true, false), - (true, false, false), - (false, true, false), - (false, false, false), - (true, false, false), - (true, false, false), - (true, false, true), - (false, false, true), - (false, true, true), - (false, false, true), - (false, true, true), - (false, true, true), - (false, false, false), - (true, true, true), - (true, false, true), - (true, false, true), - (false, true, true), - (true, false, true), - (false, true, true), - (false, true, true), - (true, true, false), - (true, true, false), - (true, true, false), - (true, false, false), - (false, false, true), - (true, false, false), - (false, false, true), - (true, false, true), - (true, true, false), - (true, true, true), - (false, true, true), - (false, true, false), - (true, true, true), -]; - -const BN_EXPS2: [(bool, bool); 62] = [ - (true, false), - (true, true), - (false, false), - (true, false), - (true, false), - (true, true), - (true, false), - (true, true), - (true, false), - (false, true), - (false, true), - (true, true), - (true, true), - (false, false), - (true, true), - (false, false), - (false, false), - (false, true), - (false, true), - (true, true), - (true, true), - (true, true), - (false, true), - (true, true), - (false, false), - (true, true), - (true, false), - (true, true), - (false, false), - (true, true), - (true, true), - (true, false), - (false, false), - (false, true), - (false, false), - (true, true), - (false, true), - (false, false), - (true, false), - (false, true), - (false, true), - (true, false), - (false, true), - (false, false), - (false, false), - (false, false), - (false, true), - (true, false), - (true, true), - (false, true), - (true, true), - (true, false), - (false, true), - (false, false), - (true, false), - (false, true), - (true, false), - (true, true), - (true, false), - (true, true), - (false, true), - (true, true), -]; - -const BN_EXPS0: [bool; 65] = [ - false, false, true, false, false, true, true, false, true, false, true, true, true, false, - true, false, false, false, true, false, false, true, false, true, false, true, true, false, - false, false, false, false, true, false, true, false, true, true, true, false, false, true, - true, true, true, false, true, false, true, true, false, false, true, false, false, false, - true, true, true, true, false, false, true, true, false, -]; diff --git a/evm/src/extension_tower.rs b/evm/src/extension_tower.rs deleted file mode 100644 index ea4e317641..0000000000 --- a/evm/src/extension_tower.rs +++ /dev/null @@ -1,1321 +0,0 @@ -use core::fmt::Debug; -use core::ops::{Add, Div, Mul, Neg, Sub}; - -use ethereum_types::{U256, U512}; -use rand::distributions::{Distribution, Standard}; -use rand::Rng; - -pub trait FieldExt: - Copy - + std::fmt::Debug - + std::cmp::PartialEq - + std::ops::Add - + std::ops::Neg - + std::ops::Sub - + std::ops::Mul - + std::ops::Div -{ - const ZERO: Self; - const UNIT: Self; - fn new(val: usize) -> Self; - fn inv(self) -> Self; -} - -pub(crate) const BN_BASE: U256 = U256([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, -]); - -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct BN254 { - pub val: U256, -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> BN254 { - let xs = rng.gen::<[u64; 4]>(); - BN254 { - val: U256(xs) % BN_BASE, - } - } -} - -impl Add for BN254 { - type Output = Self; - - fn add(self, other: Self) -> Self { - BN254 { - val: (self.val + other.val) % BN_BASE, - } - } -} - -impl Neg for BN254 { - type Output = Self; - - fn neg(self) -> Self::Output { - BN254 { - val: (BN_BASE - self.val) % BN_BASE, - } - } -} - -impl Sub for BN254 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - BN254 { - val: (BN_BASE + self.val - other.val) % BN_BASE, - } - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for BN254 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - BN254 { - val: U256::try_from((self.val).full_mul(other.val) % BN_BASE).unwrap(), - } - } -} - -impl FieldExt for BN254 { - const ZERO: Self = BN254 { val: U256::zero() }; - const UNIT: Self = BN254 { val: U256::one() }; - fn new(val: usize) -> BN254 { - BN254 { - val: U256::from(val), - } - } - fn inv(self) -> BN254 { - let exp = BN_BASE - 2; - let mut current = self; - let mut product = BN254 { val: U256::one() }; - for j in 0..256 { - if exp.bit(j) { - product = product * current; - } - current = current * current; - } - product - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for BN254 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -pub(crate) const BLS_BASE: U512 = U512([ - 0xb9feffffffffaaab, - 0x1eabfffeb153ffff, - 0x6730d2a0f6b0f624, - 0x64774b84f38512bf, - 0x4b1ba7b6434bacd7, - 0x1a0111ea397fe69a, - 0x0, - 0x0, -]); - -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct BLS381 { - pub val: U512, -} - -impl BLS381 { - pub(crate) fn lo(self) -> U256 { - U256(self.val.0[..4].try_into().unwrap()) - } - - pub(crate) fn hi(self) -> U256 { - U256(self.val.0[4..].try_into().unwrap()) - } -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> BLS381 { - let xs = rng.gen::<[u64; 8]>(); - BLS381 { - val: U512(xs) % BLS_BASE, - } - } -} - -impl Add for BLS381 { - type Output = Self; - - fn add(self, other: Self) -> Self { - BLS381 { - val: (self.val + other.val) % BLS_BASE, - } - } -} - -impl Neg for BLS381 { - type Output = Self; - - fn neg(self) -> Self::Output { - BLS381 { - val: (BLS_BASE - self.val) % BLS_BASE, - } - } -} - -impl Sub for BLS381 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - BLS381 { - val: (BLS_BASE + self.val - other.val) % BLS_BASE, - } - } -} - -impl BLS381 { - fn lsh_128(self) -> BLS381 { - let b128: U512 = U512([0, 0, 1, 0, 0, 0, 0, 0]); - // since BLS_BASE < 2^384, multiplying by 2^128 doesn't overflow the U512 - BLS381 { - val: self.val.saturating_mul(b128) % BLS_BASE, - } - } - - fn lsh_256(self) -> BLS381 { - self.lsh_128().lsh_128() - } - - fn lsh_512(self) -> BLS381 { - self.lsh_256().lsh_256() - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for BLS381 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - // x1, y1 are at most ((q-1) // 2^256) < 2^125 - let x0 = U512::from(self.lo()); - let x1 = U512::from(self.hi()); - let y0 = U512::from(other.lo()); - let y1 = U512::from(other.hi()); - - let z00 = BLS381 { - val: x0.saturating_mul(y0) % BLS_BASE, - }; - let z01 = BLS381 { - val: x0.saturating_mul(y1), - }; - let z10 = BLS381 { - val: x1.saturating_mul(y0), - }; - let z11 = BLS381 { - val: x1.saturating_mul(y1), - }; - - z00 + (z01 + z10).lsh_256() + z11.lsh_512() - } -} - -impl FieldExt for BLS381 { - const ZERO: Self = BLS381 { val: U512::zero() }; - const UNIT: Self = BLS381 { val: U512::one() }; - fn new(val: usize) -> BLS381 { - BLS381 { - val: U512::from(val), - } - } - fn inv(self) -> BLS381 { - let exp = BLS_BASE - 2; - let mut current = self; - let mut product = BLS381 { val: U512::one() }; - - for j in 0..512 { - if exp.bit(j) { - product = product * current; - } - current = current * current; - } - product - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for BLS381 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -/// The degree 2 field extension Fp2 is given by adjoining i, the square root of -1, to BN254 -/// The arithmetic in this extension is standard complex arithmetic -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct Fp2 -where - T: FieldExt, -{ - pub re: T, - pub im: T, -} - -impl Distribution> for Standard -where - T: FieldExt, - Standard: Distribution, -{ - fn sample(&self, rng: &mut R) -> Fp2 { - let (re, im) = rng.gen::<(T, T)>(); - Fp2 { re, im } - } -} - -impl Add for Fp2 { - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp2 { - re: self.re + other.re, - im: self.im + other.im, - } - } -} - -impl Neg for Fp2 { - type Output = Self; - - fn neg(self) -> Self::Output { - Fp2 { - re: -self.re, - im: -self.im, - } - } -} - -impl Sub for Fp2 { - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp2 { - re: self.re - other.re, - im: self.im - other.im, - } - } -} - -impl Mul for Fp2 { - type Output = Self; - - fn mul(self, other: Self) -> Self { - Fp2 { - re: self.re * other.re - self.im * other.im, - im: self.re * other.im + self.im * other.re, - } - } -} - -/// This function scalar multiplies an Fp2 by an Fp -impl Mul for Fp2 { - type Output = Fp2; - - fn mul(self, other: T) -> Self { - Fp2 { - re: other * self.re, - im: other * self.im, - } - } -} - -impl Fp2 { - /// Return the complex conjugate z' of z: Fp2 - /// This also happens to be the frobenius map - /// z -> z^p - /// since p == 3 mod 4 and hence - /// i^p = i^(4k) * i^3 = 1*(-i) = -i - fn conj(self) -> Self { - Fp2 { - re: self.re, - im: -self.im, - } - } - - // Return the magnitude squared of a complex number - fn norm_sq(self) -> T { - self.re * self.re + self.im * self.im - } -} - -impl FieldExt for Fp2 { - const ZERO: Fp2 = Fp2 { - re: T::ZERO, - im: T::ZERO, - }; - - const UNIT: Fp2 = Fp2 { - re: T::UNIT, - im: T::ZERO, - }; - - fn new(val: usize) -> Fp2 { - Fp2 { - re: T::new(val), - im: T::ZERO, - } - } - - /// The inverse of z is given by z'/||z||^2 since ||z||^2 = zz' - fn inv(self) -> Fp2 { - let norm_sq = self.norm_sq(); - self.conj() * norm_sq.inv() - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp2 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -/// This trait defines the method which multiplies -/// by the Fp2 element t^3 whose cube root we will -/// adjoin in the subsequent cubic extension. -/// For BN254 this is 9+i, and for BLS381 it is 1+i. -/// It also defines the relevant FROB constants, -/// given by t^(p^n) and t^(p^2n) for various n, -/// used to compute the frobenius operations. -pub trait Adj: Sized { - fn mul_adj(self) -> Self; - const FROB_T: [[Self; 6]; 2]; - const FROB_Z: [Self; 12]; -} - -impl Adj for Fp2 { - fn mul_adj(self) -> Self { - let nine = BN254::new(9); - Fp2 { - re: nine * self.re - self.im, - im: self.re + nine * self.im, - } - } - - const FROB_T: [[Fp2; 6]; 2] = [ - [ - Fp2 { - re: BN254 { val: U256::one() }, - im: BN254 { val: U256::zero() }, - }, - Fp2 { - re: BN254 { - val: U256([ - 0x99e39557176f553d, - 0xb78cc310c2c3330c, - 0x4c0bec3cf559b143, - 0x2fb347984f7911f7, - ]), - }, - im: BN254 { - val: U256([ - 0x1665d51c640fcba2, - 0x32ae2a1d0b7c9dce, - 0x4ba4cc8bd75a0794, - 0x16c9e55061ebae20, - ]), - }, - }, - Fp2 { - re: BN254 { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - }, - im: BN254 { val: U256::zero() }, - }, - Fp2 { - re: BN254 { - val: U256([ - 0x7b746ee87bdcfb6d, - 0x805ffd3d5d6942d3, - 0xbaff1c77959f25ac, - 0x0856e078b755ef0a, - ]), - }, - im: BN254 { - val: U256([ - 0x380cab2baaa586de, - 0x0fdf31bf98ff2631, - 0xa9f30e6dec26094f, - 0x04f1de41b3d1766f, - ]), - }, - }, - Fp2 { - re: BN254 { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - }, - im: BN254 { val: U256::zero() }, - }, - Fp2 { - re: BN254 { - val: U256([ - 0x62e913ee1dada9e4, - 0xf71614d4b0b71f3a, - 0x699582b87809d9ca, - 0x28be74d4bb943f51, - ]), - }, - im: BN254 { - val: U256([ - 0xedae0bcec9c7aac7, - 0x54f40eb4c3f6068d, - 0xc2b86abcbe01477a, - 0x14a88ae0cb747b99, - ]), - }, - }, - ], - [ - Fp2 { - re: BN254 { val: U256::one() }, - im: BN254 { val: U256::zero() }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x848a1f55921ea762, - 0xd33365f7be94ec72, - 0x80f3c0b75a181e84, - 0x05b54f5e64eea801, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0xc13b4711cd2b8126, - 0x3685d2ea1bdec763, - 0x9f3a80b03b0b1c92, - 0x2c145edbe7fd8aee, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x0e1a92bc3ccbf066, - 0xe633094575b06bcb, - 0x19bee0f7b5b2444e, - 0xbc58c6611c08dab, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0x5fe3ed9d730c239f, - 0xa44a9e08737f96e5, - 0xfeb0f6ef0cd21d04, - 0x23d5e999e1910a12, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xa97bda050992657f, - 0xde1afb54342c724f, - 0x1d9da40771b6f589, - 0x1ee972ae6a826a7d, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0x5721e37e70c255c9, - 0x54326430418536d1, - 0xd2b513cdbb257724, - 0x10de546ff8d4ab51, - ]), - } - }, - }, - ], - ]; - - const FROB_Z: [Fp2; 12] = [ - Fp2 { - re: { BN254 { val: U256::one() } }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xd60b35dadcc9e470, - 0x5c521e08292f2176, - 0xe8b99fdd76e68b60, - 0x1284b71c2865a7df, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0xca5cf05f80f362ac, - 0x747992778eeec7e5, - 0xa6327cfe12150b8e, - 0x246996f3b4fae7e6, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xe4bd44e5607cfd49, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xe86f7d391ed4a67f, - 0x894cb38dbe55d24a, - 0xefe9608cd0acaa90, - 0x19dc81cfcc82e4bb, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0x7694aa2bf4c0c101, - 0x7f03a5e397d439ec, - 0x06cbeee33576139d, - 0xabf8b60be77d73, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0xe4bd44e5607cfd48, - 0xc28f069fbb966e3d, - 0x5e6dd9e7e0acccb0, - 0x30644e72e131a029, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x1264475e420ac20f, - 0x2cfa95859526b0d4, - 0x072fc0af59c61f30, - 0x757cab3a41d3cdc, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0xe85845e34c4a5b9c, - 0xa20b7dfd71573c93, - 0x18e9b79ba4e2606c, - 0xca6b035381e35b6, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x3c208c16d87cfd46, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x6615563bfbb318d7, - 0x3b2f4c893f42a916, - 0xcf96a5d90a9accfd, - 0x1ddf9756b8cbf849, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0x71c39bb757899a9b, - 0x2307d819d98302a7, - 0x121dc8b86f6c4ccf, - 0x0bfab77f2c36b843, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x5763473177fffffe, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x53b10eddb9a856c8, - 0x0e34b703aa1bf842, - 0xc866e529b0d4adcd, - 0x1687cca314aebb6d, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0xc58be1eae3bc3c46, - 0x187dc4add09d90a0, - 0xb18456d34c0b44c0, - 0x2fb855bcd54a22b6, - ]), - } - }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x5763473177ffffff, - 0xd4f263f1acdb5c4f, - 0x59e26bcea0d48bac, - 0x0, - ]), - } - }, - im: { BN254 { val: U256::zero() } }, - }, - Fp2 { - re: { - BN254 { - val: U256([ - 0x29bc44b896723b38, - 0x6a86d50bd34b19b9, - 0xb120850727bb392d, - 0x290c83bf3d14634d, - ]), - } - }, - im: { - BN254 { - val: U256([ - 0x53c846338c32a1ab, - 0xf575ec93f71a8df9, - 0x9f668e1adc9ef7f0, - 0x23bd9e3da9136a73, - ]), - } - }, - }, - ]; -} - -impl Adj for Fp2 { - fn mul_adj(self) -> Self { - Fp2 { - re: self.re - self.im, - im: self.re + self.im, - } - } - const FROB_T: [[Fp2; 6]; 2] = [[Fp2::::ZERO; 6]; 2]; - const FROB_Z: [Fp2; 12] = [Fp2::::ZERO; 12]; -} - -/// The degree 3 field extension Fp6 over Fp2 is given by adjoining t, where t^3 = 1 + i -/// Fp6 has basis 1, t, t^2 over Fp2 -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - pub t0: Fp2, - pub t1: Fp2, - pub t2: Fp2, -} - -impl Distribution> for Standard -where - T: FieldExt, - Fp2: Adj, - Standard: Distribution, -{ - fn sample(&self, rng: &mut R) -> Fp6 { - let (t0, t1, t2) = rng.gen::<(Fp2, Fp2, Fp2)>(); - Fp6 { t0, t1, t2 } - } -} - -impl Add for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp6 { - t0: self.t0 + other.t0, - t1: self.t1 + other.t1, - t2: self.t2 + other.t2, - } - } -} - -impl Neg for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn neg(self) -> Self::Output { - Fp6 { - t0: -self.t0, - t1: -self.t1, - t2: -self.t2, - } - } -} - -impl Sub for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp6 { - t0: self.t0 - other.t0, - t1: self.t1 - other.t1, - t2: self.t2 - other.t2, - } - } -} - -impl Mul for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn mul(self, other: Self) -> Self { - Fp6 { - t0: self.t0 * other.t0 + (self.t1 * other.t2 + self.t2 * other.t1).mul_adj(), - t1: self.t0 * other.t1 + self.t1 * other.t0 + (self.t2 * other.t2).mul_adj(), - t2: self.t0 * other.t2 + self.t1 * other.t1 + self.t2 * other.t0, - } - } -} - -/// This function scalar multiplies an Fp6 by an Fp2 -impl Mul> for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Fp6; - - fn mul(self, other: Fp2) -> Self { - Fp6 { - t0: other * self.t0, - t1: other * self.t1, - t2: other * self.t2, - } - } -} - -impl Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - /// This function multiplies an Fp6 element by t, and hence shifts the bases, - /// where the t^2 coefficient picks up a factor of 1+i as the 1 coefficient of the output - fn sh(self) -> Fp6 { - Fp6 { - t0: self.t2.mul_adj(), - t1: self.t0, - t2: self.t1, - } - } -} - -impl Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - /// The nth frobenius endomorphism of a p^q field is given by mapping - /// x to x^(p^n) - /// which sends a + bt + ct^2: Fp6 to - /// a^(p^n) + b^(p^n) * t^(p^n) + c^(p^n) * t^(2p^n) - /// The Fp2 coefficients are determined by the comment in the conj method, - /// while the values of - /// t^(p^n) and t^(2p^n) - /// are precomputed in the constant arrays FROB_T1 and FROB_T2 - pub(crate) fn frob(self, n: usize) -> Fp6 { - let n = n % 6; - let frob_t1 = Fp2::::FROB_T[0][n]; - let frob_t2 = Fp2::::FROB_T[1][n]; - - if n % 2 != 0 { - Fp6 { - t0: self.t0.conj(), - t1: frob_t1 * self.t1.conj(), - t2: frob_t2 * self.t2.conj(), - } - } else { - Fp6 { - t0: self.t0, - t1: frob_t1 * self.t1, - t2: frob_t2 * self.t2, - } - } - } -} - -impl FieldExt for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - const ZERO: Fp6 = Fp6 { - t0: Fp2::::ZERO, - t1: Fp2::::ZERO, - t2: Fp2::::ZERO, - }; - - const UNIT: Fp6 = Fp6 { - t0: Fp2::::UNIT, - t1: Fp2::::ZERO, - t2: Fp2::::ZERO, - }; - - fn new(val: usize) -> Fp6 { - Fp6 { - t0: Fp2::::new(val), - t1: Fp2::::ZERO, - t2: Fp2::::ZERO, - } - } - - /// Let x_n = x^(p^n) and note that - /// x_0 = x^(p^0) = x^1 = x - /// (x_n)_m = (x^(p^n))^(p^m) = x^(p^n * p^m) = x^(p^(n+m)) = x_{n+m} - /// By Galois Theory, given x: Fp6, the product - /// phi = x_0 * x_1 * x_2 * x_3 * x_4 * x_5 - /// lands in BN254, and hence the inverse of x is given by - /// (x_1 * x_2 * x_3 * x_4 * x_5) / phi - /// We can save compute by rearranging the numerator: - /// (x_1 * x_3) * x_5 * (x_1 * x_3)_1 - /// By Galois theory, the following are in Fp2 and are complex conjugates - /// x_1 * x_3 * x_5, x_0 * x_2 * x_4 - /// and therefore - /// phi = ||x_1 * x_3 * x_5||^2 - /// and hence the inverse is given by - /// ([x_1 * x_3] * x_5) * [x_1 * x_3]_1 / ||[x_1 * x_3] * x_5||^2 - fn inv(self) -> Fp6 { - let prod_13 = self.frob(1) * self.frob(3); - let prod_135 = (prod_13 * self.frob(5)).t0; - let phi = prod_135.norm_sq(); - let prod_odds_over_phi = prod_135 * phi.inv(); - let prod_24 = prod_13.frob(1); - prod_24 * prod_odds_over_phi - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp6 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -/// The degree 2 field extension Fp12 over Fp6 is given by -/// adjoining z, where z^2 = t. It thus has basis 1, z over Fp6 -#[derive(Debug, Copy, Clone, PartialEq)] -pub(crate) struct Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - pub z0: Fp6, - pub z1: Fp6, -} - -impl FieldExt for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - const ZERO: Fp12 = Fp12 { - z0: Fp6::::ZERO, - z1: Fp6::::ZERO, - }; - - const UNIT: Fp12 = Fp12 { - z0: Fp6::::UNIT, - z1: Fp6::::ZERO, - }; - - fn new(val: usize) -> Fp12 { - Fp12 { - z0: Fp6::::new(val), - z1: Fp6::::ZERO, - } - } - - /// By Galois Theory, given x: Fp12, the product - /// phi = Prod_{i=0}^11 x_i - /// lands in BN254, and hence the inverse of x is given by - /// (Prod_{i=1}^11 x_i) / phi - /// The 6th Frob map is nontrivial but leaves Fp6 fixed and hence must be the conjugate: - /// x_6 = (a + bz)_6 = a - bz = x.conj() - /// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expressed as: - /// [(prod_17) * (prod_17)_2] * (prod_17)_4 * [(prod_17) * (prod_17)_2]_1 - /// By Galois theory, both the following are in Fp2 and are complex conjugates - /// prod_odds, prod_evens - /// Thus phi = ||prod_odds||^2, and hence the inverse is given by - /// prod_odds * prod_evens_except_six * x.conj() / ||prod_odds||^2 - fn inv(self) -> Fp12 { - let prod_17 = (self.frob(1) * self.frob(7)).z0; - let prod_1379 = prod_17 * prod_17.frob(2); - let prod_odds = (prod_1379 * prod_17.frob(4)).t0; - let phi = prod_odds.norm_sq(); - let prod_odds_over_phi = prod_odds * phi.inv(); - let prod_evens_except_six = prod_1379.frob(1); - let prod_except_six = prod_evens_except_six * prod_odds_over_phi; - self.conj() * prod_except_six - } -} - -impl Distribution> for Standard -where - T: FieldExt, - Fp2: Adj, - Standard: Distribution, -{ - fn sample(&self, rng: &mut R) -> Fp12 { - let (z0, z1) = rng.gen::<(Fp6, Fp6)>(); - Fp12 { z0, z1 } - } -} - -impl Add for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn add(self, other: Self) -> Self { - Fp12 { - z0: self.z0 + other.z0, - z1: self.z1 + other.z1, - } - } -} - -impl Neg for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn neg(self) -> Self::Output { - Fp12 { - z0: -self.z0, - z1: -self.z1, - } - } -} - -impl Sub for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn sub(self, other: Self) -> Self { - Fp12 { - z0: self.z0 - other.z0, - z1: self.z1 - other.z1, - } - } -} - -impl Mul for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn mul(self, other: Self) -> Self { - let h0 = self.z0 * other.z0; - let h1 = self.z1 * other.z1; - let h01 = (self.z0 + self.z1) * (other.z0 + other.z1); - Fp12 { - z0: h0 + h1.sh(), - z1: h01 - (h0 + h1), - } - } -} - -/// This function scalar multiplies an Fp12 by an Fp6 -impl Mul> for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Fp12; - - fn mul(self, other: Fp6) -> Self { - Fp12 { - z0: other * self.z0, - z1: other * self.z1, - } - } -} - -impl Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - fn conj(self) -> Fp12 { - Fp12 { - z0: self.z0, - z1: -self.z1, - } - } -} - -impl Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - /// The nth frobenius endomorphism of a p^q field is given by mapping - /// x to x^(p^n) - /// which sends a + bz: Fp12 to - /// a^(p^n) + b^(p^n) * z^(p^n) - /// where the values of z^(p^n) are precomputed in the constant array FROB_Z - pub(crate) fn frob(self, n: usize) -> Fp12 { - let n = n % 12; - Fp12 { - z0: self.z0.frob(n), - z1: self.z1.frob(n) * (Fp2::::FROB_Z[n]), - } - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for Fp12 -where - T: FieldExt, - Fp2: Adj, -{ - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inv() - } -} - -pub trait Stack { - const SIZE: usize; - - fn to_stack(&self) -> Vec; - - fn from_stack(stack: &[U256]) -> Self; -} - -impl Stack for BN254 { - const SIZE: usize = 1; - - fn to_stack(&self) -> Vec { - vec![self.val] - } - - fn from_stack(stack: &[U256]) -> BN254 { - BN254 { val: stack[0] } - } -} - -impl Stack for BLS381 { - const SIZE: usize = 2; - - fn to_stack(&self) -> Vec { - vec![self.lo(), self.hi()] - } - - fn from_stack(stack: &[U256]) -> BLS381 { - let mut val = [0u64; 8]; - val[..4].copy_from_slice(&stack[0].0); - val[4..].copy_from_slice(&stack[1].0); - BLS381 { val: U512(val) } - } -} - -impl Stack for Fp2 { - const SIZE: usize = 2 * T::SIZE; - - fn to_stack(&self) -> Vec { - let mut stack = self.re.to_stack(); - stack.extend(self.im.to_stack()); - stack - } - - fn from_stack(stack: &[U256]) -> Fp2 { - let field_size = T::SIZE; - let re = T::from_stack(&stack[0..field_size]); - let im = T::from_stack(&stack[field_size..2 * field_size]); - Fp2 { re, im } - } -} - -impl Stack for Fp6 -where - T: FieldExt, - Fp2: Adj, - Fp2: Stack, -{ - const SIZE: usize = 3 * Fp2::::SIZE; - - fn to_stack(&self) -> Vec { - let mut stack = self.t0.to_stack(); - stack.extend(self.t1.to_stack()); - stack.extend(self.t2.to_stack()); - stack - } - - fn from_stack(stack: &[U256]) -> Self { - let field_size = Fp2::::SIZE; - let t0 = Fp2::::from_stack(&stack[0..field_size]); - let t1 = Fp2::::from_stack(&stack[field_size..2 * field_size]); - let t2 = Fp2::::from_stack(&stack[2 * field_size..3 * field_size]); - Fp6 { t0, t1, t2 } - } -} - -impl Stack for Fp12 -where - T: FieldExt, - Fp2: Adj, - Fp6: Stack, -{ - const SIZE: usize = 2 * Fp6::::SIZE; - - fn to_stack(&self) -> Vec { - let mut stack = self.z0.to_stack(); - stack.extend(self.z1.to_stack()); - stack - } - - fn from_stack(stack: &[U256]) -> Self { - let field_size = Fp6::::SIZE; - let z0 = Fp6::::from_stack(&stack[0..field_size]); - let z1 = Fp6::::from_stack(&stack[field_size..2 * field_size]); - Fp12 { z0, z1 } - } -} diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs deleted file mode 100644 index f12a485e8e..0000000000 --- a/evm/src/fixed_recursive_verifier.rs +++ /dev/null @@ -1,1653 +0,0 @@ -use core::mem::{self, MaybeUninit}; -use core::ops::Range; -use std::collections::BTreeMap; -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - -use anyhow::anyhow; -use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie}; -use hashbrown::HashMap; -use itertools::{zip_eq, Itertools}; -use plonky2::field::extension::Extendable; -use plonky2::fri::FriParams; -use plonky2::gates::constant::ConstantGate; -use plonky2::gates::noop::NoopGate; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::challenger::RecursiveChallenger; -use plonky2::iop::target::{BoolTarget, Target}; -use plonky2::iop::witness::{PartialWitness, WitnessWrite}; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::circuit_data::{ - CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget, -}; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; -use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; -use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data; -use plonky2::recursion::dummy_circuit::cyclic_base_proof; -use plonky2::util::serialization::{ - Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, -}; -use plonky2::util::timing::TimingTree; -use plonky2_util::log2_ceil; -use starky::config::StarkConfig; -use starky::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; -use starky::lookup::{get_grand_product_challenge_set_target, GrandProductChallengeSet}; -use starky::proof::StarkProofWithMetadata; -use starky::stark::Stark; - -use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; -use crate::generation::GenerationInputs; -use crate::get_challenges::observe_public_values_target; -use crate::proof::{ - AllProof, BlockHashesTarget, BlockMetadataTarget, ExtraBlockData, ExtraBlockDataTarget, - PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, -}; -use crate::prover::{check_abort_signal, prove}; -use crate::recursive_verifier::{ - add_common_recursion_gates, add_virtual_public_values, get_memory_extra_looking_sum_circuit, - recursive_stark_circuit, set_public_value_targets, PlonkWrapperCircuit, PublicInputs, - StarkWrapperCircuit, -}; -use crate::util::h256_limbs; - -/// The recursion threshold. We end a chain of recursive proofs once we reach this size. -const THRESHOLD_DEGREE_BITS: usize = 13; - -/// Contains all recursive circuits used in the system. For each STARK and each initial -/// `degree_bits`, this contains a chain of recursive circuits for shrinking that STARK from -/// `degree_bits` to a constant `THRESHOLD_DEGREE_BITS`. It also contains a special root circuit -/// for combining each STARK's shrunk wrapper proof into a single proof. -#[derive(Eq, PartialEq, Debug)] -pub struct AllRecursiveCircuits -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - /// The EVM root circuit, which aggregates the (shrunk) per-table recursive proofs. - pub root: RootCircuitData, - /// The aggregation circuit, which verifies two proofs that can either be root or - /// aggregation proofs. - pub aggregation: AggregationCircuitData, - /// The block circuit, which verifies an aggregation root proof and an optional previous block proof. - pub block: BlockCircuitData, - /// Holds chains of circuits for each table and for each initial `degree_bits`. - pub by_table: [RecursiveCircuitsForTable; NUM_TABLES], -} - -/// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof -/// into a single proof. -#[derive(Eq, PartialEq, Debug)] -pub struct RootCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub circuit: CircuitData, - proof_with_pis: [ProofWithPublicInputsTarget; NUM_TABLES], - /// For each table, various inner circuits may be used depending on the initial table size. - /// This target holds the index of the circuit (within `final_circuits()`) that was used. - index_verifier_data: [Target; NUM_TABLES], - /// Public inputs containing public values. - public_values: PublicValuesTarget, - /// Public inputs used for cyclic verification. These aren't actually used for EVM root - /// proofs; the circuit has them just to match the structure of aggregation proofs. - cyclic_vk: VerifierCircuitTarget, -} - -impl RootCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - for proof in &self.proof_with_pis { - buffer.write_target_proof_with_public_inputs(proof)?; - } - for index in self.index_verifier_data { - buffer.write_target(index)?; - } - self.public_values.to_buffer(buffer)?; - buffer.write_target_verifier_circuit(&self.cyclic_vk)?; - Ok(()) - } - - fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let mut proof_with_pis = Vec::with_capacity(NUM_TABLES); - for _ in 0..NUM_TABLES { - proof_with_pis.push(buffer.read_target_proof_with_public_inputs()?); - } - let mut index_verifier_data = Vec::with_capacity(NUM_TABLES); - for _ in 0..NUM_TABLES { - index_verifier_data.push(buffer.read_target()?); - } - let public_values = PublicValuesTarget::from_buffer(buffer)?; - let cyclic_vk = buffer.read_target_verifier_circuit()?; - - Ok(Self { - circuit, - proof_with_pis: proof_with_pis.try_into().unwrap(), - index_verifier_data: index_verifier_data.try_into().unwrap(), - public_values, - cyclic_vk, - }) - } -} - -/// Data for the aggregation circuit, which is used to compress two proofs into one. Each inner -/// proof can be either an EVM root proof or another aggregation proof. -#[derive(Eq, PartialEq, Debug)] -pub struct AggregationCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub circuit: CircuitData, - lhs: AggregationChildTarget, - rhs: AggregationChildTarget, - public_values: PublicValuesTarget, - cyclic_vk: VerifierCircuitTarget, -} - -impl AggregationCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - buffer.write_target_verifier_circuit(&self.cyclic_vk)?; - self.public_values.to_buffer(buffer)?; - self.lhs.to_buffer(buffer)?; - self.rhs.to_buffer(buffer)?; - Ok(()) - } - - fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let cyclic_vk = buffer.read_target_verifier_circuit()?; - let public_values = PublicValuesTarget::from_buffer(buffer)?; - let lhs = AggregationChildTarget::from_buffer(buffer)?; - let rhs = AggregationChildTarget::from_buffer(buffer)?; - Ok(Self { - circuit, - lhs, - rhs, - public_values, - cyclic_vk, - }) - } -} - -#[derive(Eq, PartialEq, Debug)] -struct AggregationChildTarget { - is_agg: BoolTarget, - agg_proof: ProofWithPublicInputsTarget, - evm_proof: ProofWithPublicInputsTarget, -} - -impl AggregationChildTarget { - fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - buffer.write_target_bool(self.is_agg)?; - buffer.write_target_proof_with_public_inputs(&self.agg_proof)?; - buffer.write_target_proof_with_public_inputs(&self.evm_proof)?; - Ok(()) - } - - fn from_buffer(buffer: &mut Buffer) -> IoResult { - let is_agg = buffer.read_target_bool()?; - let agg_proof = buffer.read_target_proof_with_public_inputs()?; - let evm_proof = buffer.read_target_proof_with_public_inputs()?; - Ok(Self { - is_agg, - agg_proof, - evm_proof, - }) - } - - fn public_values>( - &self, - builder: &mut CircuitBuilder, - ) -> PublicValuesTarget { - let agg_pv = PublicValuesTarget::from_public_inputs(&self.agg_proof.public_inputs); - let evm_pv = PublicValuesTarget::from_public_inputs(&self.evm_proof.public_inputs); - PublicValuesTarget::select(builder, self.is_agg, agg_pv, evm_pv) - } -} - -/// Data for the block circuit, which is used to generate a final block proof, -/// and compress it with an optional parent proof if present. -#[derive(Eq, PartialEq, Debug)] -pub struct BlockCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub circuit: CircuitData, - has_parent_block: BoolTarget, - parent_block_proof: ProofWithPublicInputsTarget, - agg_root_proof: ProofWithPublicInputsTarget, - public_values: PublicValuesTarget, - cyclic_vk: VerifierCircuitTarget, -} - -impl BlockCircuitData -where - F: RichField + Extendable, - C: GenericConfig, -{ - fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - buffer.write_target_bool(self.has_parent_block)?; - buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?; - buffer.write_target_proof_with_public_inputs(&self.agg_root_proof)?; - self.public_values.to_buffer(buffer)?; - buffer.write_target_verifier_circuit(&self.cyclic_vk)?; - Ok(()) - } - - fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let has_parent_block = buffer.read_target_bool()?; - let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; - let agg_root_proof = buffer.read_target_proof_with_public_inputs()?; - let public_values = PublicValuesTarget::from_buffer(buffer)?; - let cyclic_vk = buffer.read_target_verifier_circuit()?; - Ok(Self { - circuit, - has_parent_block, - parent_block_proof, - agg_root_proof, - public_values, - cyclic_vk, - }) - } -} - -impl AllRecursiveCircuits -where - F: RichField + Extendable, - C: GenericConfig + 'static, - C::Hasher: AlgebraicHasher, -{ - /// Serializes all these preprocessed circuits into a sequence of bytes. - /// - /// # Arguments - /// - /// - `skip_tables`: a boolean indicating whether to serialize only the upper circuits - /// or the entire prover state, including recursive circuits to shrink STARK proofs. - /// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits - /// common data. - /// - `generator_serializer`: a custom generator serializer needed to serialize recursive - /// circuits proving data. - pub fn to_bytes( - &self, - skip_tables: bool, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult> { - // TODO: would be better to initialize it dynamically based on the supported max degree. - let mut buffer = Vec::with_capacity(1 << 34); - self.root - .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; - self.aggregation - .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; - self.block - .to_buffer(&mut buffer, gate_serializer, generator_serializer)?; - if !skip_tables { - for table in &self.by_table { - table.to_buffer(&mut buffer, gate_serializer, generator_serializer)?; - } - } - Ok(buffer) - } - - /// Deserializes a sequence of bytes into an entire prover state containing all recursive circuits. - /// - /// # Arguments - /// - /// - `bytes`: a slice of bytes to deserialize this prover state from. - /// - `skip_tables`: a boolean indicating whether to deserialize only the upper circuits - /// or the entire prover state, including recursive circuits to shrink STARK proofs. - /// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits - /// common data. - /// - `generator_serializer`: a custom generator serializer needed to serialize recursive - /// circuits proving data. - pub fn from_bytes( - bytes: &[u8], - skip_tables: bool, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let mut buffer = Buffer::new(bytes); - let root = - RootCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - let aggregation = AggregationCircuitData::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; - let block = - BlockCircuitData::from_buffer(&mut buffer, gate_serializer, generator_serializer)?; - - let by_table = match skip_tables { - true => (0..NUM_TABLES) - .map(|_| RecursiveCircuitsForTable { - by_stark_size: BTreeMap::default(), - }) - .collect_vec() - .try_into() - .unwrap(), - false => { - // Tricky use of MaybeUninit to remove the need for implementing Debug - // for all underlying types, necessary to convert a by_table Vec to an array. - let mut by_table: [MaybeUninit>; NUM_TABLES] = - unsafe { MaybeUninit::uninit().assume_init() }; - for table in &mut by_table[..] { - let value = RecursiveCircuitsForTable::from_buffer( - &mut buffer, - gate_serializer, - generator_serializer, - )?; - *table = MaybeUninit::new(value); - } - unsafe { - mem::transmute::<_, [RecursiveCircuitsForTable; NUM_TABLES]>(by_table) - } - } - }; - - Ok(Self { - root, - aggregation, - block, - by_table, - }) - } - - /// Preprocess all recursive circuits used by the system. - /// - /// # Arguments - /// - /// - `all_stark`: a structure defining the logic of all STARK modules and their associated - /// cross-table lookups. - /// - `degree_bits_ranges`: the logarithmic ranges to be supported for the recursive tables. - /// Transactions may yield arbitrary trace lengths for each STARK module (within some bounds), - /// unknown prior generating the witness to create a proof. Thus, for each STARK module, we - /// construct a map from `2^{degree_bits} = length` to a chain of shrinking recursion circuits, - /// starting from that length, for each `degree_bits` in the range specified for this STARK module. - /// Specifying a wide enough range allows a prover to cover all possible scenarios. - /// - `stark_config`: the configuration to be used for the STARK prover. It will usually be a fast - /// one yielding large proofs. - pub fn new( - all_stark: &AllStark, - degree_bits_ranges: &[Range; NUM_TABLES], - stark_config: &StarkConfig, - ) -> Self { - let arithmetic = RecursiveCircuitsForTable::new( - Table::Arithmetic, - &all_stark.arithmetic_stark, - degree_bits_ranges[*Table::Arithmetic].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let byte_packing = RecursiveCircuitsForTable::new( - Table::BytePacking, - &all_stark.byte_packing_stark, - degree_bits_ranges[*Table::BytePacking].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let cpu = RecursiveCircuitsForTable::new( - Table::Cpu, - &all_stark.cpu_stark, - degree_bits_ranges[*Table::Cpu].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let keccak = RecursiveCircuitsForTable::new( - Table::Keccak, - &all_stark.keccak_stark, - degree_bits_ranges[*Table::Keccak].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let keccak_sponge = RecursiveCircuitsForTable::new( - Table::KeccakSponge, - &all_stark.keccak_sponge_stark, - degree_bits_ranges[*Table::KeccakSponge].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let logic = RecursiveCircuitsForTable::new( - Table::Logic, - &all_stark.logic_stark, - degree_bits_ranges[*Table::Logic].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - let memory = RecursiveCircuitsForTable::new( - Table::Memory, - &all_stark.memory_stark, - degree_bits_ranges[*Table::Memory].clone(), - &all_stark.cross_table_lookups, - stark_config, - ); - - let by_table = [ - arithmetic, - byte_packing, - cpu, - keccak, - keccak_sponge, - logic, - memory, - ]; - let root = Self::create_root_circuit(&by_table, stark_config); - let aggregation = Self::create_aggregation_circuit(&root); - let block = Self::create_block_circuit(&aggregation); - Self { - root, - aggregation, - block, - by_table, - } - } - - /// Outputs the `VerifierCircuitData` needed to verify any block proof - /// generated by an honest prover. - /// While the [`AllRecursiveCircuits`] prover state can also verify proofs, verifiers - /// only need a fraction of the state to verify proofs. This allows much less powerful - /// entities to behave as verifiers, by only loading the necessary data to verify block proofs. - /// - /// # Usage - /// - /// ```ignore - /// let prover_state = AllRecursiveCircuits { ... }; - /// let verifier_state = prover_state.final_verifier_data(); - /// - /// // Verify a provided block proof - /// assert!(verifier_state.verify(&block_proof).is_ok()); - /// ``` - pub fn final_verifier_data(&self) -> VerifierCircuitData { - self.block.circuit.verifier_data() - } - - fn create_root_circuit( - by_table: &[RecursiveCircuitsForTable; NUM_TABLES], - stark_config: &StarkConfig, - ) -> RootCircuitData { - let inner_common_data: [_; NUM_TABLES] = - core::array::from_fn(|i| &by_table[i].final_circuits()[0].common); - - let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); - - let public_values = add_virtual_public_values(&mut builder); - - let recursive_proofs = - core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i])); - let pis: [_; NUM_TABLES] = core::array::from_fn(|i| { - PublicInputs::>::AlgebraicPermutation>::from_vec( - &recursive_proofs[i].public_inputs, - stark_config, - ) - }); - let index_verifier_data = core::array::from_fn(|_i| builder.add_virtual_target()); - - let mut challenger = RecursiveChallenger::::new(&mut builder); - for pi in &pis { - for h in &pi.trace_cap { - challenger.observe_elements(h); - } - } - - observe_public_values_target::(&mut challenger, &public_values); - - let ctl_challenges = get_grand_product_challenge_set_target( - &mut builder, - &mut challenger, - stark_config.num_challenges, - ); - // Check that the correct CTL challenges are used in every proof. - for pi in &pis { - for i in 0..stark_config.num_challenges { - builder.connect( - ctl_challenges.challenges[i].beta, - pi.ctl_challenges.challenges[i].beta, - ); - builder.connect( - ctl_challenges.challenges[i].gamma, - pi.ctl_challenges.challenges[i].gamma, - ); - } - } - - let state = challenger.compact(&mut builder); - for (&before, &s) in zip_eq(state.as_ref(), pis[0].challenger_state_before.as_ref()) { - builder.connect(before, s); - } - // Check that the challenger state is consistent between proofs. - for i in 1..NUM_TABLES { - for (&before, &after) in zip_eq( - pis[i].challenger_state_before.as_ref(), - pis[i - 1].challenger_state_after.as_ref(), - ) { - builder.connect(before, after); - } - } - - // Extra sums to add to the looked last value. - // Only necessary for the Memory values. - let mut extra_looking_sums = - vec![vec![builder.zero(); stark_config.num_challenges]; NUM_TABLES]; - - // Memory - extra_looking_sums[*Table::Memory] = (0..stark_config.num_challenges) - .map(|c| { - get_memory_extra_looking_sum_circuit( - &mut builder, - &public_values, - ctl_challenges.challenges[c], - ) - }) - .collect_vec(); - - // Verify the CTL checks. - verify_cross_table_lookups_circuit::( - &mut builder, - all_cross_table_lookups(), - pis.map(|p| p.ctl_zs_first), - Some(&extra_looking_sums), - stark_config, - ); - - for (i, table_circuits) in by_table.iter().enumerate() { - let final_circuits = table_circuits.final_circuits(); - for final_circuit in &final_circuits { - assert_eq!( - &final_circuit.common, inner_common_data[i], - "common_data mismatch" - ); - } - let mut possible_vks = final_circuits - .into_iter() - .map(|c| builder.constant_verifier_data(&c.verifier_only)) - .collect_vec(); - // random_access_verifier_data expects a vector whose length is a power of two. - // To satisfy this, we will just add some duplicates of the first VK. - while !possible_vks.len().is_power_of_two() { - possible_vks.push(possible_vks[0].clone()); - } - let inner_verifier_data = - builder.random_access_verifier_data(index_verifier_data[i], possible_vks); - - builder.verify_proof::( - &recursive_proofs[i], - &inner_verifier_data, - inner_common_data[i], - ); - } - - // We want EVM root proofs to have the exact same structure as aggregation proofs, so we add - // public inputs for cyclic verification, even though they'll be ignored. - let cyclic_vk = builder.add_verifier_data_public_inputs(); - - builder.add_gate( - ConstantGate::new(inner_common_data[0].config.num_constants), - vec![], - ); - - RootCircuitData { - circuit: builder.build::(), - proof_with_pis: recursive_proofs, - index_verifier_data, - public_values, - cyclic_vk, - } - } - - fn create_aggregation_circuit( - root: &RootCircuitData, - ) -> AggregationCircuitData { - let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); - let public_values = add_virtual_public_values(&mut builder); - let cyclic_vk = builder.add_verifier_data_public_inputs(); - let lhs = Self::add_agg_child(&mut builder, root); - let rhs = Self::add_agg_child(&mut builder, root); - - let lhs_public_values = lhs.public_values(&mut builder); - let rhs_public_values = rhs.public_values(&mut builder); - // Connect all block hash values - BlockHashesTarget::connect( - &mut builder, - public_values.block_hashes, - lhs_public_values.block_hashes, - ); - BlockHashesTarget::connect( - &mut builder, - public_values.block_hashes, - rhs_public_values.block_hashes, - ); - // Connect all block metadata values. - BlockMetadataTarget::connect( - &mut builder, - public_values.block_metadata, - lhs_public_values.block_metadata, - ); - BlockMetadataTarget::connect( - &mut builder, - public_values.block_metadata, - rhs_public_values.block_metadata, - ); - // Connect aggregation `trie_roots_before` with lhs `trie_roots_before`. - TrieRootsTarget::connect( - &mut builder, - public_values.trie_roots_before, - lhs_public_values.trie_roots_before, - ); - // Connect aggregation `trie_roots_after` with rhs `trie_roots_after`. - TrieRootsTarget::connect( - &mut builder, - public_values.trie_roots_after, - rhs_public_values.trie_roots_after, - ); - // Connect lhs `trie_roots_after` with rhs `trie_roots_before`. - TrieRootsTarget::connect( - &mut builder, - lhs_public_values.trie_roots_after, - rhs_public_values.trie_roots_before, - ); - - Self::connect_extra_public_values( - &mut builder, - &public_values.extra_block_data, - &lhs_public_values.extra_block_data, - &rhs_public_values.extra_block_data, - ); - - // Pad to match the root circuit's degree. - while log2_ceil(builder.num_gates()) < root.circuit.common.degree_bits() { - builder.add_gate(NoopGate, vec![]); - } - - let circuit = builder.build::(); - AggregationCircuitData { - circuit, - lhs, - rhs, - public_values, - cyclic_vk, - } - } - - fn connect_extra_public_values( - builder: &mut CircuitBuilder, - pvs: &ExtraBlockDataTarget, - lhs: &ExtraBlockDataTarget, - rhs: &ExtraBlockDataTarget, - ) { - // Connect checkpoint state root values. - for (&limb0, &limb1) in pvs - .checkpoint_state_trie_root - .iter() - .zip(&rhs.checkpoint_state_trie_root) - { - builder.connect(limb0, limb1); - } - for (&limb0, &limb1) in pvs - .checkpoint_state_trie_root - .iter() - .zip(&lhs.checkpoint_state_trie_root) - { - builder.connect(limb0, limb1); - } - - // Connect the transaction number in public values to the lhs and rhs values correctly. - builder.connect(pvs.txn_number_before, lhs.txn_number_before); - builder.connect(pvs.txn_number_after, rhs.txn_number_after); - - // Connect lhs `txn_number_after` with rhs `txn_number_before`. - builder.connect(lhs.txn_number_after, rhs.txn_number_before); - - // Connect the gas used in public values to the lhs and rhs values correctly. - builder.connect(pvs.gas_used_before, lhs.gas_used_before); - builder.connect(pvs.gas_used_after, rhs.gas_used_after); - - // Connect lhs `gas_used_after` with rhs `gas_used_before`. - builder.connect(lhs.gas_used_after, rhs.gas_used_before); - } - - fn add_agg_child( - builder: &mut CircuitBuilder, - root: &RootCircuitData, - ) -> AggregationChildTarget { - let common = &root.circuit.common; - let root_vk = builder.constant_verifier_data(&root.circuit.verifier_only); - let is_agg = builder.add_virtual_bool_target_safe(); - let agg_proof = builder.add_virtual_proof_with_pis(common); - let evm_proof = builder.add_virtual_proof_with_pis(common); - builder - .conditionally_verify_cyclic_proof::( - is_agg, &agg_proof, &evm_proof, &root_vk, common, - ) - .expect("Failed to build cyclic recursion circuit"); - AggregationChildTarget { - is_agg, - agg_proof, - evm_proof, - } - } - - fn create_block_circuit(agg: &AggregationCircuitData) -> BlockCircuitData { - // The block circuit is similar to the agg circuit; both verify two inner proofs. - // We need to adjust a few things, but it's easier than making a new CommonCircuitData. - let expected_common_data = CommonCircuitData { - fri_params: FriParams { - degree_bits: 14, - ..agg.circuit.common.fri_params.clone() - }, - ..agg.circuit.common.clone() - }; - - let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); - let public_values = add_virtual_public_values(&mut builder); - let has_parent_block = builder.add_virtual_bool_target_safe(); - let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data); - let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common); - - // Connect block hashes - Self::connect_block_hashes(&mut builder, &parent_block_proof, &agg_root_proof); - - let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); - let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); - - // Connect block `trie_roots_before` with parent_pv `trie_roots_before`. - TrieRootsTarget::connect( - &mut builder, - public_values.trie_roots_before, - parent_pv.trie_roots_before, - ); - // Connect the rest of block `public_values` with agg_pv. - TrieRootsTarget::connect( - &mut builder, - public_values.trie_roots_after, - agg_pv.trie_roots_after, - ); - BlockMetadataTarget::connect( - &mut builder, - public_values.block_metadata, - agg_pv.block_metadata, - ); - BlockHashesTarget::connect( - &mut builder, - public_values.block_hashes, - agg_pv.block_hashes, - ); - ExtraBlockDataTarget::connect( - &mut builder, - public_values.extra_block_data, - agg_pv.extra_block_data, - ); - - // Make connections between block proofs, and check initial and final block values. - Self::connect_block_proof(&mut builder, has_parent_block, &parent_pv, &agg_pv); - - let cyclic_vk = builder.add_verifier_data_public_inputs(); - builder - .conditionally_verify_cyclic_proof_or_dummy::( - has_parent_block, - &parent_block_proof, - &expected_common_data, - ) - .expect("Failed to build cyclic recursion circuit"); - - let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only); - builder.verify_proof::(&agg_root_proof, &agg_verifier_data, &agg.circuit.common); - - let circuit = builder.build::(); - BlockCircuitData { - circuit, - has_parent_block, - parent_block_proof, - agg_root_proof, - public_values, - cyclic_vk, - } - } - - /// Connect the 256 block hashes between two blocks - fn connect_block_hashes( - builder: &mut CircuitBuilder, - lhs: &ProofWithPublicInputsTarget, - rhs: &ProofWithPublicInputsTarget, - ) { - let lhs_public_values = PublicValuesTarget::from_public_inputs(&lhs.public_inputs); - let rhs_public_values = PublicValuesTarget::from_public_inputs(&rhs.public_inputs); - for i in 0..255 { - for j in 0..8 { - builder.connect( - lhs_public_values.block_hashes.prev_hashes[8 * (i + 1) + j], - rhs_public_values.block_hashes.prev_hashes[8 * i + j], - ); - } - } - let expected_hash = lhs_public_values.block_hashes.cur_hash; - let prev_block_hash = &rhs_public_values.block_hashes.prev_hashes[255 * 8..256 * 8]; - for i in 0..expected_hash.len() { - builder.connect(expected_hash[i], prev_block_hash[i]); - } - } - - fn connect_block_proof( - builder: &mut CircuitBuilder, - has_parent_block: BoolTarget, - lhs: &PublicValuesTarget, - rhs: &PublicValuesTarget, - ) { - // Between blocks, we only connect state tries. - for (&limb0, limb1) in lhs - .trie_roots_after - .state_root - .iter() - .zip(rhs.trie_roots_before.state_root) - { - builder.connect(limb0, limb1); - } - - // Between blocks, the checkpoint state trie remains unchanged. - for (&limb0, limb1) in lhs - .extra_block_data - .checkpoint_state_trie_root - .iter() - .zip(rhs.extra_block_data.checkpoint_state_trie_root) - { - builder.connect(limb0, limb1); - } - - // Connect block numbers. - let one = builder.one(); - let prev_block_nb = builder.sub(rhs.block_metadata.block_number, one); - builder.connect(lhs.block_metadata.block_number, prev_block_nb); - - // Check initial block values. - Self::connect_initial_values_block(builder, rhs); - - // Connect intermediary values for gas_used and bloom filters to the block's final values. We only plug on the right, so there is no need to check the left-handside block. - Self::connect_final_block_values_to_intermediary(builder, rhs); - - let has_not_parent_block = builder.sub(one, has_parent_block.target); - - // Check that the checkpoint block has the predetermined state trie root in `ExtraBlockData`. - Self::connect_checkpoint_block(builder, rhs, has_not_parent_block); - } - - fn connect_checkpoint_block( - builder: &mut CircuitBuilder, - x: &PublicValuesTarget, - has_not_parent_block: Target, - ) where - F: RichField + Extendable, - { - for (&limb0, limb1) in x - .trie_roots_before - .state_root - .iter() - .zip(x.extra_block_data.checkpoint_state_trie_root) - { - let mut constr = builder.sub(limb0, limb1); - constr = builder.mul(has_not_parent_block, constr); - builder.assert_zero(constr); - } - } - - fn connect_final_block_values_to_intermediary( - builder: &mut CircuitBuilder, - x: &PublicValuesTarget, - ) where - F: RichField + Extendable, - { - builder.connect( - x.block_metadata.block_gas_used, - x.extra_block_data.gas_used_after, - ); - } - - fn connect_initial_values_block(builder: &mut CircuitBuilder, x: &PublicValuesTarget) - where - F: RichField + Extendable, - { - // The initial number of transactions is 0. - builder.assert_zero(x.extra_block_data.txn_number_before); - // The initial gas used is 0. - builder.assert_zero(x.extra_block_data.gas_used_before); - - // The transactions and receipts tries are empty at the beginning of the block. - let initial_trie = HashedPartialTrie::from(Node::Empty).hash(); - - for (i, limb) in h256_limbs::(initial_trie).into_iter().enumerate() { - let limb_target = builder.constant(limb); - builder.connect(x.trie_roots_before.transactions_root[i], limb_target); - builder.connect(x.trie_roots_before.receipts_root[i], limb_target); - } - } - - /// For a given transaction payload passed as [`GenerationInputs`], create a proof - /// for each STARK module, then recursively shrink and combine them, eventually - /// culminating in a transaction proof, also called root proof. - /// - /// # Arguments - /// - /// - `all_stark`: a structure defining the logic of all STARK modules and their associated - /// cross-table lookups. - /// - `config`: the configuration to be used for the STARK prover. It will usually be a fast - /// one yielding large proofs. - /// - `generation_inputs`: a transaction and auxiliary data needed to generate a proof, provided - /// in Intermediary Representation. - /// - `timing`: a profiler defining a scope hierarchy and the time consumed by each one. - /// - `abort_signal`: an optional [`AtomicBool`] wrapped behind an [`Arc`], to send a kill signal - /// early. This is only necessary in a distributed setting where a worker may be blocking the entire - /// queue. - /// - /// # Outputs - /// - /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only - /// the proof with public inputs is necessary for a verifier to assert correctness of the computation, - /// but the public values are output for the prover convenience, as these are necessary during proof - /// aggregation. - pub fn prove_root( - &self, - all_stark: &AllStark, - config: &StarkConfig, - generation_inputs: GenerationInputs, - timing: &mut TimingTree, - abort_signal: Option>, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { - let all_proof = prove::( - all_stark, - config, - generation_inputs, - timing, - abort_signal.clone(), - )?; - let mut root_inputs = PartialWitness::new(); - - for table in 0..NUM_TABLES { - let stark_proof = &all_proof.multi_proof.stark_proofs[table]; - let original_degree_bits = stark_proof.proof.recover_degree_bits(config); - let table_circuits = &self.by_table[table]; - let shrunk_proof = table_circuits - .by_stark_size - .get(&original_degree_bits) - .ok_or_else(|| { - anyhow!(format!( - "Missing preprocessed circuits for {:?} table with size {}.", - Table::all()[table], - original_degree_bits, - )) - })? - .shrink(stark_proof, &all_proof.multi_proof.ctl_challenges)?; - let index_verifier_data = table_circuits - .by_stark_size - .keys() - .position(|&size| size == original_degree_bits) - .unwrap(); - root_inputs.set_target( - self.root.index_verifier_data[table], - F::from_canonical_usize(index_verifier_data), - ); - root_inputs.set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof); - - check_abort_signal(abort_signal.clone())?; - } - - root_inputs.set_verifier_data_target( - &self.root.cyclic_vk, - &self.aggregation.circuit.verifier_only, - ); - - set_public_value_targets( - &mut root_inputs, - &self.root.public_values, - &all_proof.public_values, - ) - .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let root_proof = self.root.circuit.prove(root_inputs)?; - - Ok((root_proof, all_proof.public_values)) - } - - /// From an initial set of STARK proofs passed with their associated recursive table circuits, - /// generate a recursive transaction proof. - /// It is aimed at being used when preprocessed table circuits have not been loaded to memory. - /// - /// **Note**: - /// The type of the `table_circuits` passed as arguments is - /// `&[(RecursiveCircuitsForTableSize, u8); NUM_TABLES]`. In particular, for each STARK - /// proof contained within the `AllProof` object provided to this method, we need to pass a tuple - /// of [`RecursiveCircuitsForTableSize`] and a [`u8`]. The former is the recursive chain - /// corresponding to the initial degree size of the associated STARK proof. The latter is the - /// index of this degree in the range that was originally passed when constructing the entire prover - /// state. - /// - /// # Usage - /// - /// ```ignore - /// // Load a prover state without its recursive table circuits. - /// let gate_serializer = DefaultGateSerializer; - /// let generator_serializer = DefaultGeneratorSerializer::::new(); - /// let initial_ranges = [16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30]; - /// let prover_state = AllRecursiveCircuits::::new( - /// &all_stark, - /// &initial_ranges, - /// &config, - /// ); - /// - /// // Generate a proof from the provided inputs. - /// let stark_proof = prove::(&all_stark, &config, inputs, &mut timing, abort_signal).unwrap(); - /// - /// // Read the degrees of the internal STARK proofs. - /// // Indices to be passed along the recursive tables - /// // can be easily recovered as `initial_ranges[i]` - `degrees[i]`. - /// let degrees = proof.degree_bits(&config); - /// - /// // Retrieve the corresponding recursive table circuits for each table with the corresponding degree. - /// let table_circuits = { ... }; - /// - /// // Finally shrink the STARK proof. - /// let (proof, public_values) = prove_root_after_initial_stark( - /// &all_stark, - /// &config, - /// &stark_proof, - /// &table_circuits, - /// &mut timing, - /// abort_signal, - /// ).unwrap(); - /// ``` - pub fn prove_root_after_initial_stark( - &self, - all_proof: AllProof, - table_circuits: &[(RecursiveCircuitsForTableSize, u8); NUM_TABLES], - abort_signal: Option>, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { - let mut root_inputs = PartialWitness::new(); - - for table in 0..NUM_TABLES { - let (table_circuit, index_verifier_data) = &table_circuits[table]; - - let stark_proof = &all_proof.multi_proof.stark_proofs[table]; - - let shrunk_proof = - table_circuit.shrink(stark_proof, &all_proof.multi_proof.ctl_challenges)?; - root_inputs.set_target( - self.root.index_verifier_data[table], - F::from_canonical_u8(*index_verifier_data), - ); - root_inputs.set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof); - - check_abort_signal(abort_signal.clone())?; - } - - root_inputs.set_verifier_data_target( - &self.root.cyclic_vk, - &self.aggregation.circuit.verifier_only, - ); - - set_public_value_targets( - &mut root_inputs, - &self.root.public_values, - &all_proof.public_values, - ) - .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let root_proof = self.root.circuit.prove(root_inputs)?; - - Ok((root_proof, all_proof.public_values)) - } - - pub fn verify_root(&self, agg_proof: ProofWithPublicInputs) -> anyhow::Result<()> { - self.root.circuit.verify(agg_proof) - } - - /// Create an aggregation proof, combining two contiguous proofs into a single one. The combined - /// proofs can either be transaction (aka root) proofs, or other aggregation proofs, as long as - /// their states are contiguous, meaning that the final state of the left child proof is the initial - /// state of the right child proof. - /// - /// While regular transaction proofs can only assert validity of a single transaction, aggregation - /// proofs can cover an arbitrary range, up to an entire block with all its transactions. - /// - /// # Arguments - /// - /// - `lhs_is_agg`: a boolean indicating whether the left child proof is an aggregation proof or - /// a regular transaction proof. - /// - `lhs_proof`: the left child proof. - /// - `lhs_public_values`: the public values associated to the right child proof. - /// - `rhs_is_agg`: a boolean indicating whether the right child proof is an aggregation proof or - /// a regular transaction proof. - /// - `rhs_proof`: the right child proof. - /// - `rhs_public_values`: the public values associated to the right child proof. - /// - /// # Outputs - /// - /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only - /// the proof with public inputs is necessary for a verifier to assert correctness of the computation, - /// but the public values are output for the prover convenience, as these are necessary during proof - /// aggregation. - pub fn prove_aggregation( - &self, - lhs_is_agg: bool, - lhs_proof: &ProofWithPublicInputs, - lhs_public_values: PublicValues, - rhs_is_agg: bool, - rhs_proof: &ProofWithPublicInputs, - rhs_public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { - let mut agg_inputs = PartialWitness::new(); - - agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg); - agg_inputs.set_proof_with_pis_target(&self.aggregation.lhs.agg_proof, lhs_proof); - agg_inputs.set_proof_with_pis_target(&self.aggregation.lhs.evm_proof, lhs_proof); - - agg_inputs.set_bool_target(self.aggregation.rhs.is_agg, rhs_is_agg); - agg_inputs.set_proof_with_pis_target(&self.aggregation.rhs.agg_proof, rhs_proof); - agg_inputs.set_proof_with_pis_target(&self.aggregation.rhs.evm_proof, rhs_proof); - - agg_inputs.set_verifier_data_target( - &self.aggregation.cyclic_vk, - &self.aggregation.circuit.verifier_only, - ); - - // Aggregates both `PublicValues` from the provided proofs into a single one. - let agg_public_values = PublicValues { - trie_roots_before: lhs_public_values.trie_roots_before, - trie_roots_after: rhs_public_values.trie_roots_after, - extra_block_data: ExtraBlockData { - checkpoint_state_trie_root: lhs_public_values - .extra_block_data - .checkpoint_state_trie_root, - txn_number_before: lhs_public_values.extra_block_data.txn_number_before, - txn_number_after: rhs_public_values.extra_block_data.txn_number_after, - gas_used_before: lhs_public_values.extra_block_data.gas_used_before, - gas_used_after: rhs_public_values.extra_block_data.gas_used_after, - }, - block_metadata: rhs_public_values.block_metadata, - block_hashes: rhs_public_values.block_hashes, - }; - - set_public_value_targets( - &mut agg_inputs, - &self.aggregation.public_values, - &agg_public_values, - ) - .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?; - Ok((aggregation_proof, agg_public_values)) - } - - pub fn verify_aggregation( - &self, - agg_proof: &ProofWithPublicInputs, - ) -> anyhow::Result<()> { - self.aggregation.circuit.verify(agg_proof.clone())?; - check_cyclic_proof_verifier_data( - agg_proof, - &self.aggregation.circuit.verifier_only, - &self.aggregation.circuit.common, - ) - } - - /// Create a final block proof, once all transactions of a given block have been combined into a - /// single aggregation proof. - /// - /// Block proofs can either be generated as standalone, or combined with a previous block proof - /// to assert validity of a range of blocks. - /// - /// # Arguments - /// - /// - `opt_parent_block_proof`: an optional parent block proof. Passing one will generate a proof of - /// validity for both the block range covered by the previous proof and the current block. - /// - `agg_root_proof`: the final aggregation proof containing all transactions within the current block. - /// - `public_values`: the public values associated to the aggregation proof. - /// - /// # Outputs - /// - /// This method outputs a tuple of [`ProofWithPublicInputs`] and its [`PublicValues`]. Only - /// the proof with public inputs is necessary for a verifier to assert correctness of the computation. - pub fn prove_block( - &self, - opt_parent_block_proof: Option<&ProofWithPublicInputs>, - agg_root_proof: &ProofWithPublicInputs, - public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { - let mut block_inputs = PartialWitness::new(); - - block_inputs.set_bool_target( - self.block.has_parent_block, - opt_parent_block_proof.is_some(), - ); - if let Some(parent_block_proof) = opt_parent_block_proof { - block_inputs - .set_proof_with_pis_target(&self.block.parent_block_proof, parent_block_proof); - } else { - if public_values.trie_roots_before.state_root - != public_values.extra_block_data.checkpoint_state_trie_root - { - return Err(anyhow::Error::msg(format!( - "Inconsistent pre-state for first block {:?} with checkpoint state {:?}.", - public_values.trie_roots_before.state_root, - public_values.extra_block_data.checkpoint_state_trie_root, - ))); - } - - // Initialize some public inputs for correct connection between the checkpoint block and the current one. - let mut nonzero_pis = HashMap::new(); - - // Initialize the checkpoint block roots before, and state root after. - let state_trie_root_before_keys = 0..TrieRootsTarget::HASH_SIZE; - for (key, &value) in state_trie_root_before_keys - .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) - { - nonzero_pis.insert(key, value); - } - let txn_trie_root_before_keys = - TrieRootsTarget::HASH_SIZE..TrieRootsTarget::HASH_SIZE * 2; - for (key, &value) in txn_trie_root_before_keys.clone().zip_eq(&h256_limbs::( - public_values.trie_roots_before.transactions_root, - )) { - nonzero_pis.insert(key, value); - } - let receipts_trie_root_before_keys = - TrieRootsTarget::HASH_SIZE * 2..TrieRootsTarget::HASH_SIZE * 3; - for (key, &value) in receipts_trie_root_before_keys - .clone() - .zip_eq(&h256_limbs::( - public_values.trie_roots_before.receipts_root, - )) - { - nonzero_pis.insert(key, value); - } - let state_trie_root_after_keys = - TrieRootsTarget::SIZE..TrieRootsTarget::SIZE + TrieRootsTarget::HASH_SIZE; - for (key, &value) in state_trie_root_after_keys - .zip_eq(&h256_limbs::(public_values.trie_roots_before.state_root)) - { - nonzero_pis.insert(key, value); - } - - // Initialize the checkpoint state root extra data. - let checkpoint_state_trie_keys = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - ..TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + 8; - for (key, &value) in checkpoint_state_trie_keys.zip_eq(&h256_limbs::( - public_values.extra_block_data.checkpoint_state_trie_root, - )) { - nonzero_pis.insert(key, value); - } - - // Initialize checkpoint block hashes. - // These will be all zeros the initial genesis checkpoint. - let block_hashes_keys = TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - - 8; - - for i in 0..public_values.block_hashes.prev_hashes.len() - 1 { - let targets = h256_limbs::(public_values.block_hashes.prev_hashes[i]); - for j in 0..8 { - nonzero_pis.insert(block_hashes_keys.start + 8 * (i + 1) + j, targets[j]); - } - } - let block_hashes_current_start = - TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - 8; - let cur_targets = h256_limbs::(public_values.block_hashes.prev_hashes[255]); - for i in 0..8 { - nonzero_pis.insert(block_hashes_current_start + i, cur_targets[i]); - } - - // Initialize the checkpoint block number. - // Subtraction would result in an invalid proof for genesis, but we shouldn't try proving this block anyway. - let block_number_key = TrieRootsTarget::SIZE * 2 + 6; - nonzero_pis.insert( - block_number_key, - F::from_canonical_u64(public_values.block_metadata.block_number.low_u64() - 1), - ); - - block_inputs.set_proof_with_pis_target( - &self.block.parent_block_proof, - &cyclic_base_proof( - &self.block.circuit.common, - &self.block.circuit.verifier_only, - nonzero_pis, - ), - ); - } - - block_inputs.set_proof_with_pis_target(&self.block.agg_root_proof, agg_root_proof); - - block_inputs - .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); - - // This is basically identical to this block public values, apart from the `trie_roots_before` - // that may come from the previous proof, if any. - let block_public_values = PublicValues { - trie_roots_before: opt_parent_block_proof - .map(|p| TrieRoots::from_public_inputs(&p.public_inputs[0..TrieRootsTarget::SIZE])) - .unwrap_or(public_values.trie_roots_before), - ..public_values - }; - - set_public_value_targets( - &mut block_inputs, - &self.block.public_values, - &block_public_values, - ) - .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values targets.") - })?; - - let block_proof = self.block.circuit.prove(block_inputs)?; - Ok((block_proof, block_public_values)) - } - - pub fn verify_block(&self, block_proof: &ProofWithPublicInputs) -> anyhow::Result<()> { - self.block.circuit.verify(block_proof.clone())?; - check_cyclic_proof_verifier_data( - block_proof, - &self.block.circuit.verifier_only, - &self.block.circuit.common, - ) - } -} - -/// A map between initial degree sizes and their associated shrinking recursion circuits. -#[derive(Eq, PartialEq, Debug)] -pub struct RecursiveCircuitsForTable -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - /// A map from `log_2(height)` to a chain of shrinking recursion circuits starting at that - /// height. - pub by_stark_size: BTreeMap>, -} - -impl RecursiveCircuitsForTable -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_usize(self.by_stark_size.len())?; - for (&size, table) in &self.by_stark_size { - buffer.write_usize(size)?; - table.to_buffer(buffer, gate_serializer, generator_serializer)?; - } - Ok(()) - } - - fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let length = buffer.read_usize()?; - let mut by_stark_size = BTreeMap::new(); - for _ in 0..length { - let key = buffer.read_usize()?; - let table = RecursiveCircuitsForTableSize::from_buffer( - buffer, - gate_serializer, - generator_serializer, - )?; - by_stark_size.insert(key, table); - } - Ok(Self { by_stark_size }) - } - - fn new>( - table: Table, - stark: &S, - degree_bits_range: Range, - all_ctls: &[CrossTableLookup], - stark_config: &StarkConfig, - ) -> Self { - let by_stark_size = degree_bits_range - .map(|degree_bits| { - ( - degree_bits, - RecursiveCircuitsForTableSize::new::( - table, - stark, - degree_bits, - all_ctls, - stark_config, - ), - ) - }) - .collect(); - Self { by_stark_size } - } - - /// For each initial `degree_bits`, get the final circuit at the end of that shrinking chain. - /// Each of these final circuits should have degree `THRESHOLD_DEGREE_BITS`. - fn final_circuits(&self) -> Vec<&CircuitData> { - self.by_stark_size - .values() - .map(|chain| { - chain - .shrinking_wrappers - .last() - .map(|wrapper| &wrapper.circuit) - .unwrap_or(&chain.initial_wrapper.circuit) - }) - .collect() - } -} - -/// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits` -/// `THRESHOLD_DEGREE_BITS`. -#[derive(Eq, PartialEq, Debug)] -pub struct RecursiveCircuitsForTableSize -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - initial_wrapper: StarkWrapperCircuit, - shrinking_wrappers: Vec>, -} - -impl RecursiveCircuitsForTableSize -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - pub fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_usize(self.shrinking_wrappers.len())?; - if !self.shrinking_wrappers.is_empty() { - buffer.write_common_circuit_data( - &self.shrinking_wrappers[0].circuit.common, - gate_serializer, - )?; - } - for wrapper in &self.shrinking_wrappers { - buffer.write_prover_only_circuit_data( - &wrapper.circuit.prover_only, - generator_serializer, - &wrapper.circuit.common, - )?; - buffer.write_verifier_only_circuit_data(&wrapper.circuit.verifier_only)?; - buffer.write_target_proof_with_public_inputs(&wrapper.proof_with_pis_target)?; - } - self.initial_wrapper - .to_buffer(buffer, gate_serializer, generator_serializer)?; - Ok(()) - } - - pub fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let length = buffer.read_usize()?; - let mut shrinking_wrappers = Vec::with_capacity(length); - if length != 0 { - let common = buffer.read_common_circuit_data(gate_serializer)?; - - for _ in 0..length { - let prover_only = - buffer.read_prover_only_circuit_data(generator_serializer, &common)?; - let verifier_only = buffer.read_verifier_only_circuit_data()?; - let proof_with_pis_target = buffer.read_target_proof_with_public_inputs()?; - shrinking_wrappers.push(PlonkWrapperCircuit { - circuit: CircuitData { - common: common.clone(), - prover_only, - verifier_only, - }, - proof_with_pis_target, - }) - } - }; - - let initial_wrapper = - StarkWrapperCircuit::from_buffer(buffer, gate_serializer, generator_serializer)?; - - Ok(Self { - initial_wrapper, - shrinking_wrappers, - }) - } - - fn new>( - table: Table, - stark: &S, - degree_bits: usize, - all_ctls: &[CrossTableLookup], - stark_config: &StarkConfig, - ) -> Self { - let initial_wrapper = recursive_stark_circuit( - table, - stark, - degree_bits, - all_ctls, - stark_config, - &shrinking_config(), - THRESHOLD_DEGREE_BITS, - ); - let mut shrinking_wrappers = vec![]; - - // Shrinking recursion loop. - loop { - let last = shrinking_wrappers - .last() - .map(|wrapper: &PlonkWrapperCircuit| &wrapper.circuit) - .unwrap_or(&initial_wrapper.circuit); - let last_degree_bits = last.common.degree_bits(); - assert!(last_degree_bits >= THRESHOLD_DEGREE_BITS); - if last_degree_bits == THRESHOLD_DEGREE_BITS { - break; - } - - let mut builder = CircuitBuilder::new(shrinking_config()); - let proof_with_pis_target = builder.add_virtual_proof_with_pis(&last.common); - let last_vk = builder.constant_verifier_data(&last.verifier_only); - builder.verify_proof::(&proof_with_pis_target, &last_vk, &last.common); - builder.register_public_inputs(&proof_with_pis_target.public_inputs); // carry PIs forward - add_common_recursion_gates(&mut builder); - let circuit = builder.build::(); - - assert!( - circuit.common.degree_bits() < last_degree_bits, - "Couldn't shrink to expected recursion threshold of 2^{}; stalled at 2^{}", - THRESHOLD_DEGREE_BITS, - circuit.common.degree_bits() - ); - shrinking_wrappers.push(PlonkWrapperCircuit { - circuit, - proof_with_pis_target, - }); - } - - Self { - initial_wrapper, - shrinking_wrappers, - } - } - - pub fn shrink( - &self, - stark_proof_with_metadata: &StarkProofWithMetadata, - ctl_challenges: &GrandProductChallengeSet, - ) -> anyhow::Result> { - let mut proof = self - .initial_wrapper - .prove(stark_proof_with_metadata, ctl_challenges)?; - for wrapper_circuit in &self.shrinking_wrappers { - proof = wrapper_circuit.prove(&proof)?; - } - Ok(proof) - } -} - -/// Our usual recursion threshold is 2^12 gates, but for these shrinking circuits, we use a few more -/// gates for a constant inner VK and for public inputs. This pushes us over the threshold to 2^13. -/// As long as we're at 2^13 gates, we might as well use a narrower witness. -fn shrinking_config() -> CircuitConfig { - CircuitConfig { - num_routed_wires: 40, - ..CircuitConfig::standard_recursion_config() - } -} diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs deleted file mode 100644 index 105fb6a906..0000000000 --- a/evm/src/generation/mod.rs +++ /dev/null @@ -1,335 +0,0 @@ -use std::collections::{BTreeSet, HashMap}; - -use anyhow::anyhow; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use plonky2::field::extension::Extendable; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use serde::{Deserialize, Serialize}; -use starky::config::StarkConfig; -use GlobalMetadata::{ - ReceiptTrieRootDigestAfter, ReceiptTrieRootDigestBefore, StateTrieRootDigestAfter, - StateTrieRootDigestBefore, TransactionTrieRootDigestAfter, TransactionTrieRootDigestBefore, -}; - -use crate::all_stark::{AllStark, NUM_TABLES}; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::generation::state::GenerationState; -use crate::generation::trie_extractor::{get_receipt_trie, get_state_trie, get_txn_trie}; -use crate::memory::segments::Segment; -use crate::proof::{BlockHashes, BlockMetadata, ExtraBlockData, PublicValues, TrieRoots}; -use crate::util::{h2u, u256_to_u8, u256_to_usize}; -use crate::witness::memory::{MemoryAddress, MemoryChannel}; -use crate::witness::transition::transition; - -pub mod mpt; -pub(crate) mod prover_input; -pub(crate) mod rlp; -pub(crate) mod state; -mod trie_extractor; - -use crate::witness::util::{mem_write_log, stack_peek}; - -/// Inputs needed for trace generation. -#[derive(Clone, Debug, Deserialize, Serialize, Default)] -pub struct GenerationInputs { - /// The index of the transaction being proven within its block. - pub txn_number_before: U256, - /// The cumulative gas used through the execution of all transactions prior the current one. - pub gas_used_before: U256, - /// The cumulative gas used after the execution of the current transaction. The exact gas used - /// by the current transaction is `gas_used_after` - `gas_used_before`. - pub gas_used_after: U256, - - /// A None would yield an empty proof, otherwise this contains the encoding of a transaction. - pub signed_txn: Option>, - /// Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895. - pub withdrawals: Vec<(Address, U256)>, - pub tries: TrieInputs, - /// Expected trie roots after the transactions are executed. - pub trie_roots_after: TrieRoots, - - /// State trie root of the checkpoint block. - /// This could always be the genesis block of the chain, but it allows a prover to continue proving blocks - /// from certain checkpoint heights without requiring proofs for blocks past this checkpoint. - pub checkpoint_state_trie_root: H256, - - /// Mapping between smart contract code hashes and the contract byte code. - /// All account smart contracts that are invoked will have an entry present. - pub contract_code: HashMap>, - - /// Information contained in the block header. - pub block_metadata: BlockMetadata, - - /// The hash of the current block, and a list of the 256 previous block hashes. - pub block_hashes: BlockHashes, -} - -#[derive(Clone, Debug, Deserialize, Serialize, Default)] -pub struct TrieInputs { - /// A partial version of the state trie prior to these transactions. It should include all nodes - /// that will be accessed by these transactions. - pub state_trie: HashedPartialTrie, - - /// A partial version of the transaction trie prior to these transactions. It should include all - /// nodes that will be accessed by these transactions. - pub transactions_trie: HashedPartialTrie, - - /// A partial version of the receipt trie prior to these transactions. It should include all nodes - /// that will be accessed by these transactions. - pub receipts_trie: HashedPartialTrie, - - /// A partial version of each storage trie prior to these transactions. It should include all - /// storage tries, and nodes therein, that will be accessed by these transactions. - pub storage_tries: Vec<(H256, HashedPartialTrie)>, -} - -fn apply_metadata_and_tries_memops, const D: usize>( - state: &mut GenerationState, - inputs: &GenerationInputs, -) { - let metadata = &inputs.block_metadata; - let tries = &inputs.tries; - let trie_roots_after = &inputs.trie_roots_after; - let fields = [ - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian(&metadata.block_beneficiary.0), - ), - (GlobalMetadata::BlockTimestamp, metadata.block_timestamp), - (GlobalMetadata::BlockNumber, metadata.block_number), - (GlobalMetadata::BlockDifficulty, metadata.block_difficulty), - ( - GlobalMetadata::BlockRandom, - metadata.block_random.into_uint(), - ), - (GlobalMetadata::BlockGasLimit, metadata.block_gaslimit), - (GlobalMetadata::BlockChainId, metadata.block_chain_id), - (GlobalMetadata::BlockBaseFee, metadata.block_base_fee), - ( - GlobalMetadata::BlockCurrentHash, - h2u(inputs.block_hashes.cur_hash), - ), - (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), - (GlobalMetadata::BlockGasUsedBefore, inputs.gas_used_before), - (GlobalMetadata::BlockGasUsedAfter, inputs.gas_used_after), - (GlobalMetadata::TxnNumberBefore, inputs.txn_number_before), - ( - GlobalMetadata::TxnNumberAfter, - inputs.txn_number_before + if inputs.signed_txn.is_some() { 1 } else { 0 }, - ), - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(tries.state_trie.hash()), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(tries.transactions_trie.hash()), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(tries.receipts_trie.hash()), - ), - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(trie_roots_after.receipts_root), - ), - (GlobalMetadata::KernelHash, h2u(KERNEL.code_hash)), - (GlobalMetadata::KernelLen, KERNEL.code.len().into()), - ]; - - let channel = MemoryChannel::GeneralPurpose(0); - let mut ops = fields - .map(|(field, val)| { - mem_write_log( - channel, - // These fields are already scaled by their segment, and are in context 0 (kernel). - MemoryAddress::new_bundle(U256::from(field as usize)).unwrap(), - state, - val, - ) - }) - .to_vec(); - - // Write the block's final block bloom filter. - ops.extend((0..8).map(|i| { - mem_write_log( - channel, - MemoryAddress::new(0, Segment::GlobalBlockBloom, i), - state, - metadata.block_bloom[i], - ) - })); - - // Write previous block hashes. - ops.extend( - (0..256) - .map(|i| { - mem_write_log( - channel, - MemoryAddress::new(0, Segment::BlockHashes, i), - state, - h2u(inputs.block_hashes.prev_hashes[i]), - ) - }) - .collect::>(), - ); - - state.memory.apply_ops(&ops); - state.traces.memory_ops.extend(ops); -} - -pub fn generate_traces, const D: usize>( - all_stark: &AllStark, - inputs: GenerationInputs, - config: &StarkConfig, - timing: &mut TimingTree, -) -> anyhow::Result<([Vec>; NUM_TABLES], PublicValues)> { - let mut state = GenerationState::::new(inputs.clone(), &KERNEL.code) - .map_err(|err| anyhow!("Failed to parse all the initial prover inputs: {:?}", err))?; - - apply_metadata_and_tries_memops(&mut state, &inputs); - - let cpu_res = timed!(timing, "simulate CPU", simulate_cpu(&mut state)); - if cpu_res.is_err() { - // Retrieve previous PC (before jumping to KernelPanic), to see if we reached `hash_final_tries`. - // We will output debugging information on the final tries only if we got a root mismatch. - let previous_pc = state - .traces - .cpu - .last() - .expect("We should have CPU rows") - .program_counter - .to_canonical_u64() as usize; - - if KERNEL.offset_name(previous_pc).contains("hash_final_tries") { - let state_trie_ptr = u256_to_usize( - state - .memory - .read_global_metadata(GlobalMetadata::StateTrieRoot), - ) - .map_err(|_| anyhow!("State trie pointer is too large to fit in a usize."))?; - log::debug!( - "Computed state trie: {:?}", - get_state_trie::(&state.memory, state_trie_ptr) - ); - - let txn_trie_ptr = u256_to_usize( - state - .memory - .read_global_metadata(GlobalMetadata::TransactionTrieRoot), - ) - .map_err(|_| anyhow!("Transactions trie pointer is too large to fit in a usize."))?; - log::debug!( - "Computed transactions trie: {:?}", - get_txn_trie::(&state.memory, txn_trie_ptr) - ); - - let receipt_trie_ptr = u256_to_usize( - state - .memory - .read_global_metadata(GlobalMetadata::ReceiptTrieRoot), - ) - .map_err(|_| anyhow!("Receipts trie pointer is too large to fit in a usize."))?; - log::debug!( - "Computed receipts trie: {:?}", - get_receipt_trie::(&state.memory, receipt_trie_ptr) - ); - } - - cpu_res?; - } - - log::info!( - "Trace lengths (before padding): {:?}", - state.traces.get_lengths() - ); - - let read_metadata = |field| state.memory.read_global_metadata(field); - let trie_roots_before = TrieRoots { - state_root: H256::from_uint(&read_metadata(StateTrieRootDigestBefore)), - transactions_root: H256::from_uint(&read_metadata(TransactionTrieRootDigestBefore)), - receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestBefore)), - }; - let trie_roots_after = TrieRoots { - state_root: H256::from_uint(&read_metadata(StateTrieRootDigestAfter)), - transactions_root: H256::from_uint(&read_metadata(TransactionTrieRootDigestAfter)), - receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestAfter)), - }; - - let gas_used_after = read_metadata(GlobalMetadata::BlockGasUsedAfter); - let txn_number_after = read_metadata(GlobalMetadata::TxnNumberAfter); - - let extra_block_data = ExtraBlockData { - checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, - txn_number_before: inputs.txn_number_before, - txn_number_after, - gas_used_before: inputs.gas_used_before, - gas_used_after, - }; - - let public_values = PublicValues { - trie_roots_before, - trie_roots_after, - block_metadata: inputs.block_metadata, - block_hashes: inputs.block_hashes, - extra_block_data, - }; - - let tables = timed!( - timing, - "convert trace data to tables", - state.traces.into_tables(all_stark, config, timing) - ); - Ok((tables, public_values)) -} - -fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { - let halt_pc = KERNEL.global_labels["halt"]; - - loop { - // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. - let pc = state.registers.program_counter; - let halt = state.registers.is_kernel && pc == halt_pc; - if halt { - log::info!("CPU halted after {} cycles", state.traces.clock()); - - // Padding - let mut row = CpuColumnsView::::default(); - row.clock = F::from_canonical_usize(state.traces.clock()); - row.context = F::from_canonical_usize(state.registers.context); - row.program_counter = F::from_canonical_usize(pc); - row.is_kernel_mode = F::ONE; - row.gas = F::from_canonical_u64(state.registers.gas_used); - row.stack_len = F::from_canonical_usize(state.registers.stack_len); - - loop { - state.traces.push_cpu(row); - row.clock += F::ONE; - if state.traces.clock().is_power_of_two() { - break; - } - } - - log::info!("CPU trace padded to {} cycles", state.traces.clock()); - - return Ok(()); - } - - transition(state)?; - } -} diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs deleted file mode 100644 index ee530ddef5..0000000000 --- a/evm/src/generation/mpt.rs +++ /dev/null @@ -1,427 +0,0 @@ -use core::ops::Deref; -use std::collections::HashMap; - -use bytes::Bytes; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256, U512}; -use keccak_hash::keccak; -use rlp::{Decodable, DecoderError, Encodable, PayloadInfo, Rlp, RlpStream}; -use rlp_derive::{RlpDecodable, RlpEncodable}; - -use crate::cpu::kernel::constants::trie_type::PartialTrieType; -use crate::generation::TrieInputs; -use crate::util::h2u; -use crate::witness::errors::{ProgramError, ProverInputError}; -use crate::Node; - -#[derive(RlpEncodable, RlpDecodable, Debug)] -pub struct AccountRlp { - pub nonce: U256, - pub balance: U256, - pub storage_root: H256, - pub code_hash: H256, -} - -#[derive(Clone, Debug)] -pub struct TrieRootPtrs { - pub state_root_ptr: usize, - pub txn_root_ptr: usize, - pub receipt_root_ptr: usize, -} - -impl Default for AccountRlp { - fn default() -> Self { - Self { - nonce: U256::zero(), - balance: U256::zero(), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - } - } -} - -#[derive(RlpEncodable, RlpDecodable, Debug, Clone)] -pub struct LogRlp { - pub address: Address, - pub topics: Vec, - pub data: Bytes, -} - -#[derive(RlpEncodable, RlpDecodable, Debug, Clone)] -pub struct LegacyReceiptRlp { - pub status: bool, - pub cum_gas_used: U256, - pub bloom: Bytes, - pub logs: Vec, -} - -impl LegacyReceiptRlp { - // RLP encode the receipt and prepend the tx type. - pub fn encode(&self, tx_type: u8) -> Vec { - let mut bytes = rlp::encode(self).to_vec(); - if tx_type != 0 { - bytes.insert(0, tx_type); - } - bytes - } -} - -pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { - let txn_type = match rlp.first().ok_or(ProgramError::InvalidRlp)? { - 1 => 1, - 2 => 2, - _ => 0, - }; - - // If this is not a legacy transaction, we skip the leading byte. - let rlp = if txn_type == 0 { rlp } else { &rlp[1..] }; - - let payload_info = PayloadInfo::from(rlp).map_err(|_| ProgramError::InvalidRlp)?; - let decoded_receipt: LegacyReceiptRlp = - rlp::decode(rlp).map_err(|_| ProgramError::InvalidRlp)?; - - let mut parsed_receipt = if txn_type == 0 { - Vec::new() - } else { - vec![txn_type.into()] - }; - - parsed_receipt.push(payload_info.value_len.into()); // payload_len of the entire receipt - parsed_receipt.push((decoded_receipt.status as u8).into()); - parsed_receipt.push(decoded_receipt.cum_gas_used); - parsed_receipt.extend(decoded_receipt.bloom.iter().map(|byte| U256::from(*byte))); - let encoded_logs = rlp::encode_list(&decoded_receipt.logs); - let logs_payload_info = - PayloadInfo::from(&encoded_logs).map_err(|_| ProgramError::InvalidRlp)?; - parsed_receipt.push(logs_payload_info.value_len.into()); // payload_len of all the logs - parsed_receipt.push(decoded_receipt.logs.len().into()); - - for log in decoded_receipt.logs { - let encoded_log = rlp::encode(&log); - let log_payload_info = - PayloadInfo::from(&encoded_log).map_err(|_| ProgramError::InvalidRlp)?; - parsed_receipt.push(log_payload_info.value_len.into()); // payload of one log - parsed_receipt.push(U256::from_big_endian(&log.address.to_fixed_bytes())); - parsed_receipt.push(log.topics.len().into()); - parsed_receipt.extend(log.topics.iter().map(|topic| U256::from(topic.as_bytes()))); - parsed_receipt.push(log.data.len().into()); - parsed_receipt.extend(log.data.iter().map(|byte| U256::from(*byte))); - } - - Ok(parsed_receipt) -} - -fn parse_storage_value(value_rlp: &[u8]) -> Result, ProgramError> { - let value: U256 = rlp::decode(value_rlp).map_err(|_| ProgramError::InvalidRlp)?; - Ok(vec![value]) -} - -const fn empty_nibbles() -> Nibbles { - Nibbles { - count: 0, - packed: U512::zero(), - } -} - -fn load_mpt( - trie: &HashedPartialTrie, - trie_data: &mut Vec, - parse_value: &F, -) -> Result -where - F: Fn(&[u8]) -> Result, ProgramError>, -{ - let node_ptr = trie_data.len(); - let type_of_trie = PartialTrieType::of(trie) as u32; - if type_of_trie > 0 { - trie_data.push(type_of_trie.into()); - } - - match trie.deref() { - Node::Empty => Ok(0), - Node::Hash(h) => { - trie_data.push(h2u(*h)); - - Ok(node_ptr) - } - Node::Branch { children, value } => { - // First, set children pointers to 0. - let first_child_ptr = trie_data.len(); - trie_data.extend(vec![U256::zero(); 16]); - // Then, set value. - if value.is_empty() { - trie_data.push(U256::zero()); - } else { - let parsed_value = parse_value(value)?; - trie_data.push((trie_data.len() + 1).into()); - trie_data.extend(parsed_value); - } - - // Now, load all children and update their pointers. - for (i, child) in children.iter().enumerate() { - let child_ptr = load_mpt(child, trie_data, parse_value)?; - trie_data[first_child_ptr + i] = child_ptr.into(); - } - - Ok(node_ptr) - } - - Node::Extension { nibbles, child } => { - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into_u256() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - trie_data.push((trie_data.len() + 1).into()); - - let child_ptr = load_mpt(child, trie_data, parse_value)?; - if child_ptr == 0 { - trie_data.push(0.into()); - } - - Ok(node_ptr) - } - Node::Leaf { nibbles, value } => { - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into_u256() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - - // Set `value_ptr_ptr`. - trie_data.push((trie_data.len() + 1).into()); - - let leaf = parse_value(value)?; - trie_data.extend(leaf); - - Ok(node_ptr) - } - } -} - -fn load_state_trie( - trie: &HashedPartialTrie, - key: Nibbles, - trie_data: &mut Vec, - storage_tries_by_state_key: &HashMap, -) -> Result { - let node_ptr = trie_data.len(); - let type_of_trie = PartialTrieType::of(trie) as u32; - if type_of_trie > 0 { - trie_data.push(type_of_trie.into()); - } - match trie.deref() { - Node::Empty => Ok(0), - Node::Hash(h) => { - trie_data.push(h2u(*h)); - - Ok(node_ptr) - } - Node::Branch { children, value } => { - if !value.is_empty() { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidMptInput, - )); - } - // First, set children pointers to 0. - let first_child_ptr = trie_data.len(); - trie_data.extend(vec![U256::zero(); 16]); - // Then, set value pointer to 0. - trie_data.push(U256::zero()); - - // Now, load all children and update their pointers. - for (i, child) in children.iter().enumerate() { - let extended_key = key.merge_nibbles(&Nibbles { - count: 1, - packed: i.into(), - }); - let child_ptr = - load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; - - trie_data[first_child_ptr + i] = child_ptr.into(); - } - - Ok(node_ptr) - } - Node::Extension { nibbles, child } => { - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into_u256() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - // Set `value_ptr_ptr`. - trie_data.push((trie_data.len() + 1).into()); - let extended_key = key.merge_nibbles(nibbles); - let child_ptr = - load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; - if child_ptr == 0 { - trie_data.push(0.into()); - } - - Ok(node_ptr) - } - Node::Leaf { nibbles, value } => { - let account: AccountRlp = rlp::decode(value).map_err(|_| ProgramError::InvalidRlp)?; - let AccountRlp { - nonce, - balance, - storage_root, - code_hash, - } = account; - - let storage_hash_only = HashedPartialTrie::new(Node::Hash(storage_root)); - let merged_key = key.merge_nibbles(nibbles); - let storage_trie: &HashedPartialTrie = storage_tries_by_state_key - .get(&merged_key) - .copied() - .unwrap_or(&storage_hash_only); - - assert_eq!(storage_trie.hash(), storage_root, - "In TrieInputs, an account's storage_root didn't match the associated storage trie hash"); - - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into_u256() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - // Set `value_ptr_ptr`. - trie_data.push((trie_data.len() + 1).into()); - - trie_data.push(nonce); - trie_data.push(balance); - // Storage trie ptr. - let storage_ptr_ptr = trie_data.len(); - trie_data.push((trie_data.len() + 2).into()); - trie_data.push(code_hash.into_uint()); - let storage_ptr = load_mpt(storage_trie, trie_data, &parse_storage_value)?; - if storage_ptr == 0 { - trie_data[storage_ptr_ptr] = 0.into(); - } - - Ok(node_ptr) - } - } -} - -pub(crate) fn load_all_mpts( - trie_inputs: &TrieInputs, -) -> Result<(TrieRootPtrs, Vec), ProgramError> { - let mut trie_data = vec![U256::zero()]; - let storage_tries_by_state_key = trie_inputs - .storage_tries - .iter() - .map(|(hashed_address, storage_trie)| { - let key = Nibbles::from_bytes_be(hashed_address.as_bytes()) - .expect("An H256 is 32 bytes long"); - (key, storage_trie) - }) - .collect(); - - let state_root_ptr = load_state_trie( - &trie_inputs.state_trie, - empty_nibbles(), - &mut trie_data, - &storage_tries_by_state_key, - )?; - - let txn_root_ptr = load_mpt(&trie_inputs.transactions_trie, &mut trie_data, &|rlp| { - let mut parsed_txn = vec![U256::from(rlp.len())]; - parsed_txn.extend(rlp.iter().copied().map(U256::from)); - Ok(parsed_txn) - })?; - - let receipt_root_ptr = load_mpt(&trie_inputs.receipts_trie, &mut trie_data, &parse_receipts)?; - - let trie_root_ptrs = TrieRootPtrs { - state_root_ptr, - txn_root_ptr, - receipt_root_ptr, - }; - - Ok((trie_root_ptrs, trie_data)) -} - -pub mod transaction_testing { - use super::*; - - #[derive(RlpEncodable, RlpDecodable, Debug, Clone, PartialEq, Eq)] - pub struct AccessListItemRlp { - pub address: Address, - pub storage_keys: Vec, - } - - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct AddressOption(pub Option

); - - impl Encodable for AddressOption { - fn rlp_append(&self, s: &mut RlpStream) { - match self.0 { - None => s.encoder().encode_value(&[]), - Some(value) => { - s.encoder().encode_value(&value.to_fixed_bytes()); - } - } - } - } - - impl Decodable for AddressOption { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_int() && rlp.is_empty() { - return Ok(AddressOption(None)); - } - if rlp.is_data() && rlp.size() == 20 { - return Ok(AddressOption(Some(Address::decode(rlp)?))); - } - Err(DecoderError::RlpExpectedToBeData) - } - } - - #[derive(RlpEncodable, RlpDecodable, Debug, Clone, PartialEq, Eq)] - pub struct LegacyTransactionRlp { - pub nonce: U256, - pub gas_price: U256, - pub gas: U256, - pub to: AddressOption, - pub value: U256, - pub data: Bytes, - pub v: U256, - pub r: U256, - pub s: U256, - } - - #[derive(RlpEncodable, RlpDecodable, Debug, Clone, PartialEq, Eq)] - pub struct AccessListTransactionRlp { - pub chain_id: u64, - pub nonce: U256, - pub gas_price: U256, - pub gas: U256, - pub to: AddressOption, - pub value: U256, - pub data: Bytes, - pub access_list: Vec, - pub y_parity: U256, - pub r: U256, - pub s: U256, - } - - #[derive(RlpEncodable, RlpDecodable, Debug, Clone, PartialEq, Eq)] - pub struct FeeMarketTransactionRlp { - pub chain_id: u64, - pub nonce: U256, - pub max_priority_fee_per_gas: U256, - pub max_fee_per_gas: U256, - pub gas: U256, - pub to: AddressOption, - pub value: U256, - pub data: Bytes, - pub access_list: Vec, - pub y_parity: U256, - pub r: U256, - pub s: U256, - } -} diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs deleted file mode 100644 index e6fb4dbf8d..0000000000 --- a/evm/src/generation/prover_input.rs +++ /dev/null @@ -1,627 +0,0 @@ -use core::mem::transmute; -use std::collections::{BTreeSet, HashMap}; -use std::str::FromStr; - -use anyhow::{bail, Error}; -use ethereum_types::{BigEndianHash, H256, U256, U512}; -use itertools::Itertools; -use num_bigint::BigUint; -use plonky2::field::types::Field; -use serde::{Deserialize, Serialize}; - -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::cpu::kernel::interpreter::simulate_cpu_and_get_user_jumps; -use crate::cpu::kernel::opcodes::get_push_opcode; -use crate::extension_tower::{FieldExt, Fp12, BLS381, BN254}; -use crate::generation::prover_input::EvmField::{ - Bls381Base, Bls381Scalar, Bn254Base, Bn254Scalar, Secp256k1Base, Secp256k1Scalar, -}; -use crate::generation::prover_input::FieldOp::{Inverse, Sqrt}; -use crate::generation::state::GenerationState; -use crate::memory::segments::Segment; -use crate::memory::segments::Segment::BnPairing; -use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_u8, u256_to_usize}; -use crate::witness::errors::ProverInputError::*; -use crate::witness::errors::{ProgramError, ProverInputError}; -use crate::witness::memory::MemoryAddress; -use crate::witness::operation::CONTEXT_SCALING_FACTOR; -use crate::witness::util::{current_context_peek, stack_peek}; - -/// Prover input function represented as a scoped function name. -/// Example: `PROVER_INPUT(ff::bn254_base::inverse)` is represented as `ProverInputFn([ff, bn254_base, inverse])`. -#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] -pub struct ProverInputFn(Vec); - -impl From> for ProverInputFn { - fn from(v: Vec) -> Self { - Self(v) - } -} - -impl GenerationState { - pub(crate) fn prover_input(&mut self, input_fn: &ProverInputFn) -> Result { - match input_fn.0[0].as_str() { - "no_txn" => self.no_txn(), - "trie_ptr" => self.run_trie_ptr(input_fn), - "ff" => self.run_ff(input_fn), - "sf" => self.run_sf(input_fn), - "ffe" => self.run_ffe(input_fn), - "rlp" => self.run_rlp(), - "current_hash" => self.run_current_hash(), - "account_code" => self.run_account_code(), - "bignum_modmul" => self.run_bignum_modmul(), - "withdrawal" => self.run_withdrawal(), - "num_bits" => self.run_num_bits(), - "jumpdest_table" => self.run_jumpdest_table(input_fn), - _ => Err(ProgramError::ProverInputError(InvalidFunction)), - } - } - - fn no_txn(&mut self) -> Result { - Ok(U256::from(self.inputs.signed_txn.is_none() as u8)) - } - - fn run_trie_ptr(&mut self, input_fn: &ProverInputFn) -> Result { - let trie = input_fn.0[1].as_str(); - match trie { - "state" => Ok(U256::from(self.trie_root_ptrs.state_root_ptr)), - "txn" => Ok(U256::from(self.trie_root_ptrs.txn_root_ptr)), - "receipt" => Ok(U256::from(self.trie_root_ptrs.receipt_root_ptr)), - _ => Err(ProgramError::ProverInputError(InvalidInput)), - } - } - - /// Finite field operations. - fn run_ff(&self, input_fn: &ProverInputFn) -> Result { - let field = EvmField::from_str(input_fn.0[1].as_str()) - .map_err(|_| ProgramError::ProverInputError(InvalidFunction))?; - let op = FieldOp::from_str(input_fn.0[2].as_str()) - .map_err(|_| ProgramError::ProverInputError(InvalidFunction))?; - let x = stack_peek(self, 0)?; - field.op(op, x) - } - - /// Special finite field operations. - fn run_sf(&self, input_fn: &ProverInputFn) -> Result { - let field = EvmField::from_str(input_fn.0[1].as_str()) - .map_err(|_| ProgramError::ProverInputError(InvalidFunction))?; - let inputs: [U256; 4] = match field { - Bls381Base => (0..4) - .map(|i| stack_peek(self, i)) - .collect::, _>>()? - .try_into() - .unwrap(), - _ => todo!(), - }; - let res = match input_fn.0[2].as_str() { - "add_lo" => field.add_lo(inputs), - "add_hi" => field.add_hi(inputs), - "mul_lo" => field.mul_lo(inputs), - "mul_hi" => field.mul_hi(inputs), - "sub_lo" => field.sub_lo(inputs), - "sub_hi" => field.sub_hi(inputs), - _ => return Err(ProgramError::ProverInputError(InvalidFunction)), - }; - - Ok(res) - } - - /// Finite field extension operations. - fn run_ffe(&self, input_fn: &ProverInputFn) -> Result { - let field = EvmField::from_str(input_fn.0[1].as_str()) - .map_err(|_| ProgramError::ProverInputError(InvalidFunction))?; - let n = input_fn.0[2] - .as_str() - .split('_') - .nth(1) - .unwrap() - .parse::() - .unwrap(); - let ptr = stack_peek(self, 11 - n).map(u256_to_usize)??; - - let f: [U256; 12] = match field { - Bn254Base => std::array::from_fn(|i| current_context_peek(self, BnPairing, ptr + i)), - _ => todo!(), - }; - Ok(field.field_extension_inverse(n, f)) - } - - /// RLP data. - fn run_rlp(&mut self) -> Result { - self.rlp_prover_inputs - .pop() - .ok_or(ProgramError::ProverInputError(OutOfRlpData)) - } - - fn run_current_hash(&mut self) -> Result { - Ok(U256::from_big_endian(&self.inputs.block_hashes.cur_hash.0)) - } - - /// Account code loading. - /// Initializes the code segment of the given context with the code corresponding - /// to the provided hash. - /// Returns the length of the code. - fn run_account_code(&mut self) -> Result { - // stack: codehash, ctx, ... - let codehash = stack_peek(self, 0)?; - let context = stack_peek(self, 1)? >> CONTEXT_SCALING_FACTOR; - let context = u256_to_usize(context)?; - let mut address = MemoryAddress::new(context, Segment::Code, 0); - let code = self - .inputs - .contract_code - .get(&H256::from_uint(&codehash)) - .ok_or(ProgramError::ProverInputError(CodeHashNotFound))?; - for &byte in code { - self.memory.set(address, byte.into()); - address.increment(); - } - Ok(code.len().into()) - } - - // Bignum modular multiplication. - // On the first call, calculates the remainder and quotient of the given inputs. - // These are stored, as limbs, in self.bignum_modmul_result_limbs. - // Subsequent calls return one limb at a time, in order (first remainder and then quotient). - fn run_bignum_modmul(&mut self) -> Result { - if self.bignum_modmul_result_limbs.is_empty() { - let len = stack_peek(self, 2).map(u256_to_usize)??; - let a_start_loc = stack_peek(self, 3).map(u256_to_usize)??; - let b_start_loc = stack_peek(self, 4).map(u256_to_usize)??; - let m_start_loc = stack_peek(self, 5).map(u256_to_usize)??; - - let (remainder, quotient) = - self.bignum_modmul(len, a_start_loc, b_start_loc, m_start_loc); - - self.bignum_modmul_result_limbs = remainder - .iter() - .cloned() - .pad_using(len, |_| 0.into()) - .chain(quotient.iter().cloned().pad_using(2 * len, |_| 0.into())) - .collect(); - self.bignum_modmul_result_limbs.reverse(); - } - - self.bignum_modmul_result_limbs - .pop() - .ok_or(ProgramError::ProverInputError(InvalidInput)) - } - - fn bignum_modmul( - &mut self, - len: usize, - a_start_loc: usize, - b_start_loc: usize, - m_start_loc: usize, - ) -> (Vec, Vec) { - let n = self.memory.contexts.len(); - let a = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content - [a_start_loc..a_start_loc + len]; - let b = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content - [b_start_loc..b_start_loc + len]; - let m = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content - [m_start_loc..m_start_loc + len]; - - let a_biguint = mem_vec_to_biguint(a); - let b_biguint = mem_vec_to_biguint(b); - let m_biguint = mem_vec_to_biguint(m); - - let prod = a_biguint * b_biguint; - let quo = if m_biguint == BigUint::default() { - BigUint::default() - } else { - &prod / &m_biguint - }; - let rem = prod - m_biguint * &quo; - - (biguint_to_mem_vec(rem), biguint_to_mem_vec(quo)) - } - - /// Withdrawal data. - fn run_withdrawal(&mut self) -> Result { - self.withdrawal_prover_inputs - .pop() - .ok_or(ProgramError::ProverInputError(OutOfWithdrawalData)) - } - - /// Return the number of bits of the top of the stack or an error if - /// the top of the stack is zero or empty. - fn run_num_bits(&mut self) -> Result { - let value = stack_peek(self, 0)?; - if value.is_zero() { - Err(ProgramError::ProverInputError(NumBitsError)) - } else { - let num_bits = value.bits(); - Ok(num_bits.into()) - } - } - - /// Generate either the next used jump address or the proof for the last jump address. - fn run_jumpdest_table(&mut self, input_fn: &ProverInputFn) -> Result { - match input_fn.0[1].as_str() { - "next_address" => self.run_next_jumpdest_table_address(), - "next_proof" => self.run_next_jumpdest_table_proof(), - _ => Err(ProgramError::ProverInputError(InvalidInput)), - } - } - - /// Returns the next used jump address. - fn run_next_jumpdest_table_address(&mut self) -> Result { - let context = u256_to_usize(stack_peek(self, 0)? >> CONTEXT_SCALING_FACTOR)?; - - if self.jumpdest_table.is_none() { - self.generate_jumpdest_table()?; - log::debug!("jdt = {:?}", self.jumpdest_table); - } - - let Some(jumpdest_table) = &mut self.jumpdest_table else { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidJumpdestSimulation, - )); - }; - - let jd_len = jumpdest_table.len(); - - if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) - && let Some(next_jumpdest_address) = ctx_jumpdest_table.pop() - { - log::debug!( - "jumpdest_table_len = {:?}, ctx_jumpdest_table.len = {:?}", - jd_len, - ctx_jumpdest_table.len() - ); - Ok((next_jumpdest_address + 1).into()) - } else { - jumpdest_table.remove(&context); - Ok(U256::zero()) - } - } - - /// Returns the proof for the last jump address. - fn run_next_jumpdest_table_proof(&mut self) -> Result { - let context = u256_to_usize(stack_peek(self, 1)? >> CONTEXT_SCALING_FACTOR)?; - let Some(jumpdest_table) = &mut self.jumpdest_table else { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidJumpdestSimulation, - )); - }; - - let jd_len = jumpdest_table.len(); - - if let Some(ctx_jumpdest_table) = jumpdest_table.get_mut(&context) - && let Some(next_jumpdest_proof) = ctx_jumpdest_table.pop() - { - log::debug!( - "jumpdest_table_len = {:?}, ctx_jumpdest_table.len = {:?}", - jd_len, - ctx_jumpdest_table.len() - ); - Ok(next_jumpdest_proof.into()) - } else { - Err(ProgramError::ProverInputError( - ProverInputError::InvalidJumpdestSimulation, - )) - } - } -} - -impl GenerationState { - /// Simulate the user's code and store all the jump addresses with their respective contexts. - fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { - let checkpoint = self.checkpoint(); - - // Simulate the user's code and (unnecessarily) part of the kernel code, skipping the validate table call - self.jumpdest_table = simulate_cpu_and_get_user_jumps("terminate_common", self); - - Ok(()) - } - - /// Given a HashMap containing the contexts and the jumpdest addresses, compute their respective proofs, - /// by calling `get_proofs_and_jumpdests` - pub(crate) fn set_jumpdest_analysis_inputs( - &mut self, - jumpdest_table: HashMap>, - ) { - self.jumpdest_table = Some(HashMap::from_iter(jumpdest_table.into_iter().map( - |(ctx, jumpdest_table)| { - let code = self.get_code(ctx).unwrap(); - if let Some(&largest_address) = jumpdest_table.last() { - let proofs = get_proofs_and_jumpdests(&code, largest_address, jumpdest_table); - (ctx, proofs) - } else { - (ctx, vec![]) - } - }, - ))); - } - - pub(crate) fn get_current_code(&self) -> Result, ProgramError> { - self.get_code(self.registers.context) - } - - fn get_code(&self, context: usize) -> Result, ProgramError> { - let code_len = self.get_code_len(context)?; - let code = (0..code_len) - .map(|i| { - u256_to_u8( - self.memory - .get(MemoryAddress::new(context, Segment::Code, i)), - ) - }) - .collect::, _>>()?; - Ok(code) - } - - fn get_code_len(&self, context: usize) -> Result { - let code_len = u256_to_usize(self.memory.get(MemoryAddress::new( - context, - Segment::ContextMetadata, - ContextMetadata::CodeSize.unscale(), - )))?; - Ok(code_len) - } - - fn get_current_code_len(&self) -> Result { - self.get_code_len(self.registers.context) - } - - pub(crate) fn set_jumpdest_bits(&mut self, code: &[u8]) { - const JUMPDEST_OPCODE: u8 = 0x5b; - for (pos, opcode) in CodeIterator::new(code) { - if opcode == JUMPDEST_OPCODE { - self.memory.set( - MemoryAddress::new(self.registers.context, Segment::JumpdestBits, pos), - U256::one(), - ); - } - } - } -} - -/// For all address in `jumpdest_table` smaller than `largest_address`, -/// this function searches for a proof. A proof is the closest address -/// for which none of the previous 32 bytes in the code (including opcodes -/// and pushed bytes) is a PUSHXX and the address is in its range. It returns -/// a vector of even size containing proofs followed by their addresses. -fn get_proofs_and_jumpdests( - code: &[u8], - largest_address: usize, - jumpdest_table: std::collections::BTreeSet, -) -> Vec { - const PUSH1_OPCODE: u8 = 0x60; - const PUSH32_OPCODE: u8 = 0x7f; - let (proofs, _) = CodeIterator::until(code, largest_address + 1).fold( - (vec![], 0), - |(mut proofs, last_proof), (addr, opcode)| { - let has_prefix = if let Some(prefix_start) = addr.checked_sub(32) { - code[prefix_start..addr] - .iter() - .rev() - .zip(0..32) - .all(|(&byte, i)| byte > PUSH32_OPCODE || byte < PUSH1_OPCODE + i) - } else { - false - }; - let last_proof = if has_prefix { addr - 32 } else { last_proof }; - if jumpdest_table.contains(&addr) { - // Push the proof - proofs.push(last_proof); - // Push the address - proofs.push(addr); - } - (proofs, last_proof) - }, - ); - proofs -} - -/// An iterator over the EVM code contained in `code`, which skips the bytes -/// that are the arguments of a PUSHXX opcode. -struct CodeIterator<'a> { - code: &'a [u8], - pos: usize, - end: usize, -} - -impl<'a> CodeIterator<'a> { - fn new(code: &'a [u8]) -> Self { - CodeIterator { - end: code.len(), - code, - pos: 0, - } - } - fn until(code: &'a [u8], end: usize) -> Self { - CodeIterator { - end: std::cmp::min(code.len(), end), - code, - pos: 0, - } - } -} - -impl<'a> Iterator for CodeIterator<'a> { - type Item = (usize, u8); - - fn next(&mut self) -> Option { - const PUSH1_OPCODE: u8 = 0x60; - const PUSH32_OPCODE: u8 = 0x7f; - let CodeIterator { code, pos, end } = self; - if *pos >= *end { - return None; - } - let opcode = code[*pos]; - let old_pos = *pos; - *pos += if (PUSH1_OPCODE..=PUSH32_OPCODE).contains(&opcode) { - (opcode - PUSH1_OPCODE + 2).into() - } else { - 1 - }; - Some((old_pos, opcode)) - } -} - -enum EvmField { - Bls381Base, - Bls381Scalar, - Bn254Base, - Bn254Scalar, - Secp256k1Base, - Secp256k1Scalar, -} - -enum FieldOp { - Inverse, - Sqrt, -} - -impl FromStr for EvmField { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "bls381_base" => Bls381Base, - "bls381_scalar" => Bls381Scalar, - "bn254_base" => Bn254Base, - "bn254_scalar" => Bn254Scalar, - "secp256k1_base" => Secp256k1Base, - "secp256k1_scalar" => Secp256k1Scalar, - _ => bail!("Unrecognized field."), - }) - } -} - -impl FromStr for FieldOp { - type Err = Error; - - fn from_str(s: &str) -> Result { - Ok(match s { - "inverse" => Inverse, - "sqrt" => Sqrt, - _ => bail!("Unrecognized field operation."), - }) - } -} - -impl EvmField { - fn order(&self) -> U256 { - match self { - EvmField::Bls381Base => todo!(), - EvmField::Bls381Scalar => todo!(), - EvmField::Bn254Base => { - U256::from_str("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47") - .unwrap() - } - EvmField::Bn254Scalar => todo!(), - EvmField::Secp256k1Base => { - U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f") - .unwrap() - } - EvmField::Secp256k1Scalar => { - U256::from_str("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") - .unwrap() - } - } - } - - fn op(&self, op: FieldOp, x: U256) -> Result { - match op { - FieldOp::Inverse => self.inverse(x), - FieldOp::Sqrt => self.sqrt(x), - } - } - - fn inverse(&self, x: U256) -> Result { - let n = self.order(); - if x >= n { - return Err(ProgramError::ProverInputError(InvalidInput)); - }; - modexp(x, n - 2, n) - } - - fn sqrt(&self, x: U256) -> Result { - let n = self.order(); - if x >= n { - return Err(ProgramError::ProverInputError(InvalidInput)); - }; - let (q, r) = (n + 1).div_mod(4.into()); - - if !r.is_zero() { - return Err(ProgramError::ProverInputError(InvalidInput)); - }; - - // Only naive sqrt implementation for now. If needed implement Tonelli-Shanks - modexp(x, q, n) - } - - fn add_lo(&self, inputs: [U256; 4]) -> U256 { - let [y1, x0, x1, y0] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } + BLS381 { val: y }; - z.lo() - } - - fn add_hi(&self, inputs: [U256; 4]) -> U256 { - let [x0, x1, y0, y1] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } + BLS381 { val: y }; - z.hi() - } - - fn mul_lo(&self, inputs: [U256; 4]) -> U256 { - let [y1, x0, x1, y0] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } * BLS381 { val: y }; - z.lo() - } - - fn mul_hi(&self, inputs: [U256; 4]) -> U256 { - let [x0, x1, y0, y1] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } * BLS381 { val: y }; - z.hi() - } - - fn sub_lo(&self, inputs: [U256; 4]) -> U256 { - let [y1, x0, x1, y0] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } - BLS381 { val: y }; - z.lo() - } - - fn sub_hi(&self, inputs: [U256; 4]) -> U256 { - let [x0, x1, y0, y1] = inputs; - let x = U512::from(x0) + (U512::from(x1) << 256); - let y = U512::from(y0) + (U512::from(y1) << 256); - let z = BLS381 { val: x } - BLS381 { val: y }; - z.hi() - } - - fn field_extension_inverse(&self, n: usize, f: [U256; 12]) -> U256 { - let f: Fp12 = unsafe { transmute(f) }; - let f_inv: [U256; 12] = unsafe { transmute(f.inv()) }; - f_inv[n] - } -} - -fn modexp(x: U256, e: U256, n: U256) -> Result { - let mut current = x; - let mut product = U256::one(); - - for j in 0..256 { - if e.bit(j) { - product = U256::try_from(product.full_mul(current) % n) - .map_err(|_| ProgramError::ProverInputError(InvalidInput))?; - } - current = U256::try_from(current.full_mul(current) % n) - .map_err(|_| ProgramError::ProverInputError(InvalidInput))?; - } - - Ok(product) -} diff --git a/evm/src/generation/rlp.rs b/evm/src/generation/rlp.rs deleted file mode 100644 index ffc302fd54..0000000000 --- a/evm/src/generation/rlp.rs +++ /dev/null @@ -1,22 +0,0 @@ -use ethereum_types::U256; - -pub(crate) fn all_rlp_prover_inputs_reversed(signed_txn: &[u8]) -> Vec { - let mut inputs = all_rlp_prover_inputs(signed_txn); - inputs.reverse(); - inputs -} - -fn all_rlp_prover_inputs(signed_txn: &[u8]) -> Vec { - let mut prover_inputs = vec![]; - prover_inputs.push(signed_txn.len().into()); - let mut chunks = signed_txn.chunks_exact(32); - for bytes in chunks.by_ref() { - prover_inputs.push(U256::from_big_endian(bytes)); - } - let mut last_chunk = chunks.remainder().to_vec(); - if !last_chunk.is_empty() { - last_chunk.extend_from_slice(&vec![0u8; 32 - last_chunk.len()]); - prover_inputs.push(U256::from_big_endian(&last_chunk)); - } - prover_inputs -} diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs deleted file mode 100644 index a6df4b3331..0000000000 --- a/evm/src/generation/state.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::collections::HashMap; - -use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; -use keccak_hash::keccak; -use plonky2::field::types::Field; - -use super::mpt::{load_all_mpts, TrieRootPtrs}; -use super::TrieInputs; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::generation::rlp::all_rlp_prover_inputs_reversed; -use crate::generation::GenerationInputs; -use crate::memory::segments::Segment; -use crate::util::u256_to_usize; -use crate::witness::errors::ProgramError; -use crate::witness::memory::{MemoryAddress, MemoryState}; -use crate::witness::state::RegistersState; -use crate::witness::traces::{TraceCheckpoint, Traces}; -use crate::witness::util::stack_peek; - -pub(crate) struct GenerationStateCheckpoint { - pub(crate) registers: RegistersState, - pub(crate) traces: TraceCheckpoint, -} - -#[derive(Debug)] -pub(crate) struct GenerationState { - pub(crate) inputs: GenerationInputs, - pub(crate) registers: RegistersState, - pub(crate) memory: MemoryState, - pub(crate) traces: Traces, - - /// Prover inputs containing RLP data, in reverse order so that the next input can be obtained - /// via `pop()`. - pub(crate) rlp_prover_inputs: Vec, - - pub(crate) withdrawal_prover_inputs: Vec, - - /// The state trie only stores state keys, which are hashes of addresses, but sometimes it is - /// useful to see the actual addresses for debugging. Here we store the mapping for all known - /// addresses. - pub(crate) state_key_to_address: HashMap, - - /// Prover inputs containing the result of a MODMUL operation, in little-endian order (so that - /// inputs are obtained in big-endian order via `pop()`). Contains both the remainder and the - /// quotient, in that order. - pub(crate) bignum_modmul_result_limbs: Vec, - - /// Pointers, within the `TrieData` segment, of the three MPTs. - pub(crate) trie_root_ptrs: TrieRootPtrs, - - /// A hash map where the key is a context in the user's code and the value is the set of - /// jump destinations with its corresponding "proof". A "proof" for a jump destination is - /// either 0 or an address i > 32 in the code (not necessarily pointing to an opcode) such that - /// for every j in [i, i+32] it holds that code[j] < 0x7f - j + i. - pub(crate) jumpdest_table: Option>>, -} - -impl GenerationState { - fn preinitialize_mpts(&mut self, trie_inputs: &TrieInputs) -> TrieRootPtrs { - let (trie_roots_ptrs, trie_data) = - load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - - self.memory.contexts[0].segments[Segment::TrieData.unscale()].content = trie_data; - - trie_roots_ptrs - } - pub(crate) fn new(inputs: GenerationInputs, kernel_code: &[u8]) -> Result { - log::debug!("Input signed_txn: {:?}", &inputs.signed_txn); - log::debug!("Input state_trie: {:?}", &inputs.tries.state_trie); - log::debug!( - "Input transactions_trie: {:?}", - &inputs.tries.transactions_trie - ); - log::debug!("Input receipts_trie: {:?}", &inputs.tries.receipts_trie); - log::debug!("Input storage_tries: {:?}", &inputs.tries.storage_tries); - log::debug!("Input contract_code: {:?}", &inputs.contract_code); - - let rlp_prover_inputs = - all_rlp_prover_inputs_reversed(inputs.clone().signed_txn.as_ref().unwrap_or(&vec![])); - let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); - let bignum_modmul_result_limbs = Vec::new(); - - let mut state = Self { - inputs: inputs.clone(), - registers: Default::default(), - memory: MemoryState::new(kernel_code), - traces: Traces::default(), - rlp_prover_inputs, - withdrawal_prover_inputs, - state_key_to_address: HashMap::new(), - bignum_modmul_result_limbs, - trie_root_ptrs: TrieRootPtrs { - state_root_ptr: 0, - txn_root_ptr: 0, - receipt_root_ptr: 0, - }, - jumpdest_table: None, - }; - let trie_root_ptrs = state.preinitialize_mpts(&inputs.tries); - - state.trie_root_ptrs = trie_root_ptrs; - Ok(state) - } - - /// Updates `program_counter`, and potentially adds some extra handling if we're jumping to a - /// special location. - pub(crate) fn jump_to(&mut self, dst: usize) -> Result<(), ProgramError> { - self.registers.program_counter = dst; - if dst == KERNEL.global_labels["observe_new_address"] { - let tip_u256 = stack_peek(self, 0)?; - let tip_h256 = H256::from_uint(&tip_u256); - let tip_h160 = H160::from(tip_h256); - self.observe_address(tip_h160); - } else if dst == KERNEL.global_labels["observe_new_contract"] { - let tip_u256 = stack_peek(self, 0)?; - let tip_h256 = H256::from_uint(&tip_u256); - self.observe_contract(tip_h256)?; - } - - Ok(()) - } - - /// Observe the given address, so that we will be able to recognize the associated state key. - /// This is just for debugging purposes. - pub(crate) fn observe_address(&mut self, address: Address) { - let state_key = keccak(address.0); - self.state_key_to_address.insert(state_key, address); - } - - /// Observe the given code hash and store the associated code. - /// When called, the code corresponding to `codehash` should be stored in the return data. - pub(crate) fn observe_contract(&mut self, codehash: H256) -> Result<(), ProgramError> { - if self.inputs.contract_code.contains_key(&codehash) { - return Ok(()); // Return early if the code hash has already been observed. - } - - let ctx = self.registers.context; - let returndata_offset = ContextMetadata::ReturndataSize.unscale(); - let returndata_size_addr = - MemoryAddress::new(ctx, Segment::ContextMetadata, returndata_offset); - let returndata_size = u256_to_usize(self.memory.get(returndata_size_addr))?; - let code = self.memory.contexts[ctx].segments[Segment::Returndata.unscale()].content - [..returndata_size] - .iter() - .map(|x| x.low_u32() as u8) - .collect::>(); - debug_assert_eq!(keccak(&code), codehash); - - self.inputs.contract_code.insert(codehash, code); - - Ok(()) - } - - pub(crate) fn checkpoint(&self) -> GenerationStateCheckpoint { - GenerationStateCheckpoint { - registers: self.registers, - traces: self.traces.checkpoint(), - } - } - - pub(crate) fn rollback(&mut self, checkpoint: GenerationStateCheckpoint) { - self.registers = checkpoint.registers; - self.traces.rollback(checkpoint.traces); - } - - pub(crate) fn stack(&self) -> Vec { - const MAX_TO_SHOW: usize = 10; - (0..self.registers.stack_len.min(MAX_TO_SHOW)) - .map(|i| stack_peek(self, i).unwrap()) - .collect() - } - - /// Clones everything but the traces. - pub(crate) fn soft_clone(&self) -> GenerationState { - Self { - inputs: self.inputs.clone(), - registers: self.registers, - memory: self.memory.clone(), - traces: Traces::default(), - rlp_prover_inputs: self.rlp_prover_inputs.clone(), - state_key_to_address: self.state_key_to_address.clone(), - bignum_modmul_result_limbs: self.bignum_modmul_result_limbs.clone(), - withdrawal_prover_inputs: self.withdrawal_prover_inputs.clone(), - trie_root_ptrs: TrieRootPtrs { - state_root_ptr: 0, - txn_root_ptr: 0, - receipt_root_ptr: 0, - }, - jumpdest_table: None, - } - } -} - -/// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. -/// Returns the reversed array. -pub(crate) fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { - let mut withdrawal_prover_inputs = withdrawals - .iter() - .flat_map(|w| [U256::from((w.0).0.as_slice()), w.1]) - .collect::>(); - withdrawal_prover_inputs.push(U256::MAX); - withdrawal_prover_inputs.push(U256::MAX); - withdrawal_prover_inputs.reverse(); - withdrawal_prover_inputs -} diff --git a/evm/src/generation/trie_extractor.rs b/evm/src/generation/trie_extractor.rs deleted file mode 100644 index 4d3a745a19..0000000000 --- a/evm/src/generation/trie_extractor.rs +++ /dev/null @@ -1,313 +0,0 @@ -//! Code for extracting trie data after witness generation. This is intended only for debugging. - -use std::collections::HashMap; - -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie, WrappedNode}; -use ethereum_types::{BigEndianHash, H256, U256, U512}; - -use super::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; -use crate::cpu::kernel::constants::trie_type::PartialTrieType; -use crate::memory::segments::Segment; -use crate::util::{u256_to_bool, u256_to_h160, u256_to_u8, u256_to_usize}; -use crate::witness::errors::ProgramError; -use crate::witness::memory::{MemoryAddress, MemoryState}; - -/// Account data as it's stored in the state trie, with a pointer to the storage trie. -#[derive(Debug)] -pub(crate) struct AccountTrieRecord { - pub(crate) nonce: u64, - pub(crate) balance: U256, - pub(crate) storage_ptr: usize, - pub(crate) code_hash: H256, -} - -pub(crate) fn read_state_trie_value(slice: &[U256]) -> Result { - Ok(AccountTrieRecord { - nonce: slice[0].low_u64(), - balance: slice[1], - storage_ptr: u256_to_usize(slice[2])?, - code_hash: H256::from_uint(&slice[3]), - }) -} - -pub(crate) const fn read_storage_trie_value(slice: &[U256]) -> U256 { - slice[0] -} - -pub(crate) fn read_trie( - memory: &MemoryState, - ptr: usize, - read_value: fn(&[U256]) -> Result, -) -> Result, ProgramError> { - let mut res = HashMap::new(); - let empty_nibbles = Nibbles { - count: 0, - packed: U512::zero(), - }; - read_trie_helper::(memory, ptr, read_value, empty_nibbles, &mut res)?; - Ok(res) -} - -pub(crate) fn read_trie_helper( - memory: &MemoryState, - ptr: usize, - read_value: fn(&[U256]) -> Result, - prefix: Nibbles, - res: &mut HashMap, -) -> Result<(), ProgramError> { - let load = |offset| memory.get(MemoryAddress::new(0, Segment::TrieData, offset)); - let load_slice_from = |init_offset| { - &memory.contexts[0].segments[Segment::TrieData.unscale()].content[init_offset..] - }; - - let trie_type = PartialTrieType::all()[u256_to_usize(load(ptr))?]; - match trie_type { - PartialTrieType::Empty => Ok(()), - PartialTrieType::Hash => Ok(()), - PartialTrieType::Branch => { - let ptr_payload = ptr + 1; - for i in 0u8..16 { - let child_ptr = u256_to_usize(load(ptr_payload + i as usize))?; - read_trie_helper::(memory, child_ptr, read_value, prefix.merge_nibble(i), res)?; - } - let value_ptr = u256_to_usize(load(ptr_payload + 16))?; - if value_ptr != 0 { - res.insert(prefix, read_value(load_slice_from(value_ptr))?); - }; - - Ok(()) - } - PartialTrieType::Extension => { - let count = u256_to_usize(load(ptr + 1))?; - let packed = load(ptr + 2); - let nibbles = Nibbles { - count, - packed: packed.into(), - }; - let child_ptr = u256_to_usize(load(ptr + 3))?; - read_trie_helper::( - memory, - child_ptr, - read_value, - prefix.merge_nibbles(&nibbles), - res, - ) - } - PartialTrieType::Leaf => { - let count = u256_to_usize(load(ptr + 1))?; - let packed = load(ptr + 2); - let nibbles = Nibbles { - count, - packed: packed.into(), - }; - let value_ptr = u256_to_usize(load(ptr + 3))?; - res.insert( - prefix.merge_nibbles(&nibbles), - read_value(load_slice_from(value_ptr))?, - ); - - Ok(()) - } - } -} - -pub(crate) fn read_receipt_trie_value( - slice: &[U256], -) -> Result<(Option, LegacyReceiptRlp), ProgramError> { - let first_value = slice[0]; - // Skip two elements for non-legacy Receipts, and only one otherwise. - let (first_byte, slice) = if first_value == U256::one() || first_value == U256::from(2u8) { - (Some(first_value.as_u32() as u8), &slice[2..]) - } else { - (None, &slice[1..]) - }; - - let status = u256_to_bool(slice[0])?; - let cum_gas_used = slice[1]; - let bloom = slice[2..2 + 256] - .iter() - .map(|&x| u256_to_u8(x)) - .collect::>()?; - // We read the number of logs at position `2 + 256 + 1`, and skip over the next element before parsing the logs. - let logs = read_logs(u256_to_usize(slice[2 + 256 + 1])?, &slice[2 + 256 + 3..])?; - - Ok(( - first_byte, - LegacyReceiptRlp { - status, - cum_gas_used, - bloom, - logs, - }, - )) -} - -pub(crate) fn read_logs(num_logs: usize, slice: &[U256]) -> Result, ProgramError> { - let mut offset = 0; - (0..num_logs) - .map(|_| { - let address = u256_to_h160(slice[offset])?; - let num_topics = u256_to_usize(slice[offset + 1])?; - - let topics = (0..num_topics) - .map(|i| H256::from_uint(&slice[offset + 2 + i])) - .collect(); - - let data_len = u256_to_usize(slice[offset + 2 + num_topics])?; - let log = LogRlp { - address, - topics, - data: slice[offset + 2 + num_topics + 1..offset + 2 + num_topics + 1 + data_len] - .iter() - .map(|&x| u256_to_u8(x)) - .collect::>()?, - }; - offset += 2 + num_topics + 1 + data_len; - Ok(log) - }) - .collect() -} - -pub(crate) fn read_state_rlp_value( - memory: &MemoryState, - slice: &[U256], -) -> Result, ProgramError> { - let storage_trie: HashedPartialTrie = get_trie(memory, slice[2].as_usize(), |_, x| { - Ok(rlp::encode(&read_storage_trie_value(x)).to_vec()) - })?; - let account = AccountRlp { - nonce: slice[0], - balance: slice[1], - storage_root: storage_trie.hash(), - code_hash: H256::from_uint(&slice[3]), - }; - Ok(rlp::encode(&account).to_vec()) -} - -pub(crate) fn read_txn_rlp_value( - _memory: &MemoryState, - slice: &[U256], -) -> Result, ProgramError> { - let txn_rlp_len = u256_to_usize(slice[0])?; - slice[1..txn_rlp_len + 1] - .iter() - .map(|&x| u256_to_u8(x)) - .collect::>() -} - -pub(crate) fn read_receipt_rlp_value( - _memory: &MemoryState, - slice: &[U256], -) -> Result, ProgramError> { - let (first_byte, receipt) = read_receipt_trie_value(slice)?; - let mut bytes = rlp::encode(&receipt).to_vec(); - if let Some(txn_byte) = first_byte { - bytes.insert(0, txn_byte); - } - - Ok(bytes) -} - -pub(crate) fn get_state_trie( - memory: &MemoryState, - ptr: usize, -) -> Result { - get_trie(memory, ptr, read_state_rlp_value) -} - -pub(crate) fn get_txn_trie( - memory: &MemoryState, - ptr: usize, -) -> Result { - get_trie(memory, ptr, read_txn_rlp_value) -} - -pub(crate) fn get_receipt_trie( - memory: &MemoryState, - ptr: usize, -) -> Result { - get_trie(memory, ptr, read_receipt_rlp_value) -} - -pub(crate) fn get_trie( - memory: &MemoryState, - ptr: usize, - read_rlp_value: fn(&MemoryState, &[U256]) -> Result, ProgramError>, -) -> Result { - let empty_nibbles = Nibbles { - count: 0, - packed: U512::zero(), - }; - Ok(N::new(get_trie_helper( - memory, - ptr, - read_rlp_value, - empty_nibbles, - )?)) -} - -pub(crate) fn get_trie_helper( - memory: &MemoryState, - ptr: usize, - read_value: fn(&MemoryState, &[U256]) -> Result, ProgramError>, - prefix: Nibbles, -) -> Result, ProgramError> { - let load = |offset| memory.get(MemoryAddress::new(0, Segment::TrieData, offset)); - let load_slice_from = |init_offset| { - &memory.contexts[0].segments[Segment::TrieData.unscale()].content[init_offset..] - }; - - let trie_type = PartialTrieType::all()[u256_to_usize(load(ptr))?]; - match trie_type { - PartialTrieType::Empty => Ok(Node::Empty), - PartialTrieType::Hash => { - let ptr_payload = ptr + 1; - let hash = H256::from_uint(&load(ptr_payload)); - Ok(Node::Hash(hash)) - } - PartialTrieType::Branch => { - let ptr_payload = ptr + 1; - let children = (0..16) - .map(|i| { - let child_ptr = u256_to_usize(load(ptr_payload + i as usize))?; - get_trie_helper(memory, child_ptr, read_value, prefix.merge_nibble(i as u8)) - }) - .collect::, _>>()?; - let children = core::array::from_fn(|i| WrappedNode::from(children[i].clone())); - let value_ptr = u256_to_usize(load(ptr_payload + 16))?; - let mut value: Vec = vec![]; - if value_ptr != 0 { - value = read_value(memory, load_slice_from(value_ptr))?; - }; - Ok(Node::Branch { children, value }) - } - PartialTrieType::Extension => { - let count = u256_to_usize(load(ptr + 1))?; - let packed = load(ptr + 2); - let nibbles = Nibbles { - count, - packed: packed.into(), - }; - let child_ptr = u256_to_usize(load(ptr + 3))?; - let child = WrappedNode::from(get_trie_helper( - memory, - child_ptr, - read_value, - prefix.merge_nibbles(&nibbles), - )?); - Ok(Node::Extension { nibbles, child }) - } - PartialTrieType::Leaf => { - let count = u256_to_usize(load(ptr + 1))?; - let packed = load(ptr + 2); - let nibbles = Nibbles { - count, - packed: packed.into(), - }; - let value_ptr = u256_to_usize(load(ptr + 3))?; - let value = read_value(memory, load_slice_from(value_ptr))?; - Ok(Node::Leaf { nibbles, value }) - } - } -} diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs deleted file mode 100644 index 2a783b940b..0000000000 --- a/evm/src/get_challenges.rs +++ /dev/null @@ -1,223 +0,0 @@ -use ethereum_types::{BigEndianHash, H256, U256}; -use plonky2::field::extension::Extendable; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; -use starky::config::StarkConfig; -use starky::lookup::get_grand_product_challenge_set; - -use crate::proof::*; -use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; -use crate::witness::errors::ProgramError; - -fn observe_root, C: GenericConfig, const D: usize>( - challenger: &mut Challenger, - root: H256, -) { - for limb in root.into_uint().0.into_iter() { - challenger.observe_element(F::from_canonical_u32(limb as u32)); - challenger.observe_element(F::from_canonical_u32((limb >> 32) as u32)); - } -} - -fn observe_trie_roots, C: GenericConfig, const D: usize>( - challenger: &mut Challenger, - trie_roots: &TrieRoots, -) { - observe_root::(challenger, trie_roots.state_root); - observe_root::(challenger, trie_roots.transactions_root); - observe_root::(challenger, trie_roots.receipts_root); -} - -fn observe_trie_roots_target< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut RecursiveChallenger, - trie_roots: &TrieRootsTarget, -) where - C::Hasher: AlgebraicHasher, -{ - challenger.observe_elements(&trie_roots.state_root); - challenger.observe_elements(&trie_roots.transactions_root); - challenger.observe_elements(&trie_roots.receipts_root); -} - -fn observe_block_metadata< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut Challenger, - block_metadata: &BlockMetadata, -) -> Result<(), ProgramError> { - challenger.observe_elements( - &u256_limbs::(U256::from_big_endian(&block_metadata.block_beneficiary.0))[..5], - ); - challenger.observe_element(u256_to_u32(block_metadata.block_timestamp)?); - challenger.observe_element(u256_to_u32(block_metadata.block_number)?); - challenger.observe_element(u256_to_u32(block_metadata.block_difficulty)?); - challenger.observe_elements(&h256_limbs::(block_metadata.block_random)); - challenger.observe_element(u256_to_u32(block_metadata.block_gaslimit)?); - challenger.observe_element(u256_to_u32(block_metadata.block_chain_id)?); - let basefee = u256_to_u64(block_metadata.block_base_fee)?; - challenger.observe_element(basefee.0); - challenger.observe_element(basefee.1); - challenger.observe_element(u256_to_u32(block_metadata.block_gas_used)?); - for i in 0..8 { - challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i])); - } - - Ok(()) -} - -fn observe_block_metadata_target< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut RecursiveChallenger, - block_metadata: &BlockMetadataTarget, -) where - C::Hasher: AlgebraicHasher, -{ - challenger.observe_elements(&block_metadata.block_beneficiary); - challenger.observe_element(block_metadata.block_timestamp); - challenger.observe_element(block_metadata.block_number); - challenger.observe_element(block_metadata.block_difficulty); - challenger.observe_elements(&block_metadata.block_random); - challenger.observe_element(block_metadata.block_gaslimit); - challenger.observe_element(block_metadata.block_chain_id); - challenger.observe_elements(&block_metadata.block_base_fee); - challenger.observe_element(block_metadata.block_gas_used); - challenger.observe_elements(&block_metadata.block_bloom); -} - -fn observe_extra_block_data< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut Challenger, - extra_data: &ExtraBlockData, -) -> Result<(), ProgramError> { - challenger.observe_elements(&h256_limbs(extra_data.checkpoint_state_trie_root)); - challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?); - challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?); - challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?); - challenger.observe_element(u256_to_u32(extra_data.gas_used_after)?); - - Ok(()) -} - -fn observe_extra_block_data_target< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut RecursiveChallenger, - extra_data: &ExtraBlockDataTarget, -) where - C::Hasher: AlgebraicHasher, -{ - challenger.observe_elements(&extra_data.checkpoint_state_trie_root); - challenger.observe_element(extra_data.txn_number_before); - challenger.observe_element(extra_data.txn_number_after); - challenger.observe_element(extra_data.gas_used_before); - challenger.observe_element(extra_data.gas_used_after); -} - -fn observe_block_hashes< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut Challenger, - block_hashes: &BlockHashes, -) { - for i in 0..256 { - challenger.observe_elements(&h256_limbs::(block_hashes.prev_hashes[i])); - } - challenger.observe_elements(&h256_limbs::(block_hashes.cur_hash)); -} - -fn observe_block_hashes_target< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut RecursiveChallenger, - block_hashes: &BlockHashesTarget, -) where - C::Hasher: AlgebraicHasher, -{ - challenger.observe_elements(&block_hashes.prev_hashes); - challenger.observe_elements(&block_hashes.cur_hash); -} - -pub(crate) fn observe_public_values< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut Challenger, - public_values: &PublicValues, -) -> Result<(), ProgramError> { - observe_trie_roots::(challenger, &public_values.trie_roots_before); - observe_trie_roots::(challenger, &public_values.trie_roots_after); - observe_block_metadata::(challenger, &public_values.block_metadata)?; - observe_block_hashes::(challenger, &public_values.block_hashes); - observe_extra_block_data::(challenger, &public_values.extra_block_data) -} - -pub(crate) fn observe_public_values_target< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( - challenger: &mut RecursiveChallenger, - public_values: &PublicValuesTarget, -) where - C::Hasher: AlgebraicHasher, -{ - observe_trie_roots_target::(challenger, &public_values.trie_roots_before); - observe_trie_roots_target::(challenger, &public_values.trie_roots_after); - observe_block_metadata_target::(challenger, &public_values.block_metadata); - observe_block_hashes_target::(challenger, &public_values.block_hashes); - observe_extra_block_data_target::(challenger, &public_values.extra_block_data); -} - -impl, C: GenericConfig, const D: usize> AllProof { - /// Computes all Fiat-Shamir challenges used in the STARK proof. - pub(crate) fn get_challenges( - &self, - config: &StarkConfig, - ) -> Result, ProgramError> { - let mut challenger = Challenger::::new(); - - let stark_proofs = &self.multi_proof.stark_proofs; - - for proof in stark_proofs { - challenger.observe_cap(&proof.proof.trace_cap); - } - - observe_public_values::(&mut challenger, &self.public_values)?; - - let ctl_challenges = - get_grand_product_challenge_set(&mut challenger, config.num_challenges); - - Ok(AllProofChallenges { - stark_challenges: core::array::from_fn(|i| { - challenger.compact(); - stark_proofs[i].proof.get_challenges( - &mut challenger, - Some(&ctl_challenges), - true, - config, - ) - }), - ctl_challenges, - }) - } -} diff --git a/evm/src/keccak/columns.rs b/evm/src/keccak/columns.rs deleted file mode 100644 index bbd96a7426..0000000000 --- a/evm/src/keccak/columns.rs +++ /dev/null @@ -1,134 +0,0 @@ -use plonky2::field::types::Field; -use starky::lookup::Column; - -use crate::keccak::keccak_stark::{NUM_INPUTS, NUM_ROUNDS}; - -/// A register which is set to 1 if we are in the `i`th round, otherwise 0. -pub(crate) const fn reg_step(i: usize) -> usize { - debug_assert!(i < NUM_ROUNDS); - i -} - -/// Registers to hold permutation inputs. -/// `reg_input_limb(2*i) -> input[i] as u32` -/// `reg_input_limb(2*i+1) -> input[i] >> 32` -pub(crate) fn reg_input_limb(i: usize) -> Column { - debug_assert!(i < 2 * NUM_INPUTS); - let i_u64 = i / 2; // The index of the 64-bit chunk. - - // The 5x5 state is treated as y-major, as per the Keccak spec. - let y = i_u64 / 5; - let x = i_u64 % 5; - - let reg_low_limb = reg_a(x, y); - let is_high_limb = i % 2; - Column::single(reg_low_limb + is_high_limb) -} - -/// Registers to hold permutation outputs. -/// `reg_output_limb(2*i) -> output[i] as u32` -/// `reg_output_limb(2*i+1) -> output[i] >> 32` -pub(crate) const fn reg_output_limb(i: usize) -> usize { - debug_assert!(i < 2 * NUM_INPUTS); - let i_u64 = i / 2; // The index of the 64-bit chunk. - - // The 5x5 state is treated as y-major, as per the Keccak spec. - let y = i_u64 / 5; - let x = i_u64 % 5; - - let is_high_limb = i % 2; - reg_a_prime_prime_prime(x, y) + is_high_limb -} - -const R: [[u8; 5]; 5] = [ - [0, 36, 3, 41, 18], - [1, 44, 10, 45, 2], - [62, 6, 43, 15, 61], - [28, 55, 25, 21, 56], - [27, 20, 39, 8, 14], -]; - -/// Column holding the timestamp, used to link inputs and outputs -/// in the `KeccakSpongeStark`. -pub(crate) const TIMESTAMP: usize = NUM_ROUNDS; - -const START_A: usize = TIMESTAMP + 1; -pub(crate) const fn reg_a(x: usize, y: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(y < 5); - START_A + (x * 5 + y) * 2 -} - -// C[x] = xor(A[x, 0], A[x, 1], A[x, 2], A[x, 3], A[x, 4]) -const START_C: usize = START_A + 5 * 5 * 2; -pub(crate) const fn reg_c(x: usize, z: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(z < 64); - START_C + x * 64 + z -} - -// C'[x, z] = xor(C[x, z], C[x - 1, z], C[x + 1, z - 1]) -const START_C_PRIME: usize = START_C + 5 * 64; -pub(crate) const fn reg_c_prime(x: usize, z: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(z < 64); - START_C_PRIME + x * 64 + z -} - -// Note: D is inlined, not stored in the witness. - -// A'[x, y] = xor(A[x, y], D[x]) -// = xor(A[x, y], C[x - 1], ROT(C[x + 1], 1)) -const START_A_PRIME: usize = START_C_PRIME + 5 * 64; -pub(crate) const fn reg_a_prime(x: usize, y: usize, z: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(y < 5); - debug_assert!(z < 64); - START_A_PRIME + x * 64 * 5 + y * 64 + z -} - -pub(crate) const fn reg_b(x: usize, y: usize, z: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(y < 5); - debug_assert!(z < 64); - // B is just a rotation of A', so these are aliases for A' registers. - // From the spec, - // B[y, (2x + 3y) % 5] = ROT(A'[x, y], r[x, y]) - // So, - // B[x, y] = f((x + 3y) % 5, x) - // where f(a, b) = ROT(A'[a, b], r[a, b]) - let a = (x + 3 * y) % 5; - let b = x; - let rot = R[a][b] as usize; - reg_a_prime(a, b, (z + 64 - rot) % 64) -} - -// A''[x, y] = xor(B[x, y], andn(B[x + 1, y], B[x + 2, y])). -const START_A_PRIME_PRIME: usize = START_A_PRIME + 5 * 5 * 64; -pub(crate) const fn reg_a_prime_prime(x: usize, y: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(y < 5); - START_A_PRIME_PRIME + x * 2 * 5 + y * 2 -} - -const START_A_PRIME_PRIME_0_0_BITS: usize = START_A_PRIME_PRIME + 5 * 5 * 2; -pub(crate) const fn reg_a_prime_prime_0_0_bit(i: usize) -> usize { - debug_assert!(i < 64); - START_A_PRIME_PRIME_0_0_BITS + i -} - -const REG_A_PRIME_PRIME_PRIME_0_0_LO: usize = START_A_PRIME_PRIME_0_0_BITS + 64; -const REG_A_PRIME_PRIME_PRIME_0_0_HI: usize = REG_A_PRIME_PRIME_PRIME_0_0_LO + 1; - -// A'''[0, 0] is additionally xor'd with RC. -pub(crate) const fn reg_a_prime_prime_prime(x: usize, y: usize) -> usize { - debug_assert!(x < 5); - debug_assert!(y < 5); - if x == 0 && y == 0 { - REG_A_PRIME_PRIME_PRIME_0_0_LO - } else { - reg_a_prime_prime(x, y) - } -} - -pub(crate) const NUM_COLUMNS: usize = REG_A_PRIME_PRIME_PRIME_0_0_HI + 1; diff --git a/evm/src/keccak/constants.rs b/evm/src/keccak/constants.rs deleted file mode 100644 index 72286237c8..0000000000 --- a/evm/src/keccak/constants.rs +++ /dev/null @@ -1,157 +0,0 @@ -const RC: [u64; 24] = [ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, -]; - -const RC_BITS: [[u8; 64]; 24] = [ - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - [ - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ], -]; - -pub(crate) const fn rc_value_bit(round: usize, bit_index: usize) -> u8 { - RC_BITS[round][bit_index] -} - -pub(crate) const fn rc_value(round: usize) -> u64 { - RC[round] -} diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs deleted file mode 100644 index fc27086ae5..0000000000 --- a/evm/src/keccak/keccak_stark.rs +++ /dev/null @@ -1,777 +0,0 @@ -use core::marker::PhantomData; - -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::plonk_common::reduce_with_powers_ext_circuit; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter}; -use starky::stark::Stark; -use starky::util::trace_rows_to_poly_values; - -use super::columns::reg_input_limb; -use crate::all_stark::EvmStarkFrame; -use crate::keccak::columns::{ - reg_a, reg_a_prime, reg_a_prime_prime, reg_a_prime_prime_0_0_bit, reg_a_prime_prime_prime, - reg_b, reg_c, reg_c_prime, reg_output_limb, reg_step, NUM_COLUMNS, TIMESTAMP, -}; -use crate::keccak::constants::{rc_value, rc_value_bit}; -use crate::keccak::logic::{ - andn, andn_gen, andn_gen_circuit, xor, xor3_gen, xor3_gen_circuit, xor_gen, xor_gen_circuit, -}; -use crate::keccak::round_flags::{eval_round_flags, eval_round_flags_recursively}; - -/// Number of rounds in a Keccak permutation. -pub(crate) const NUM_ROUNDS: usize = 24; - -/// Number of 64-bit elements in the Keccak permutation input. -pub(crate) const NUM_INPUTS: usize = 25; - -/// Create vector of `Columns` corresponding to the permutation input limbs. -pub(crate) fn ctl_data_inputs() -> Vec> { - let mut res: Vec<_> = (0..2 * NUM_INPUTS).map(reg_input_limb).collect(); - res.push(Column::single(TIMESTAMP)); - res -} - -/// Create vector of `Columns` corresponding to the permutation output limbs. -pub(crate) fn ctl_data_outputs() -> Vec> { - let mut res: Vec<_> = Column::singles((0..2 * NUM_INPUTS).map(reg_output_limb)).collect(); - res.push(Column::single(TIMESTAMP)); - res -} - -/// CTL filter for the first round of the Keccak permutation. -pub(crate) fn ctl_filter_inputs() -> Filter { - Filter::new_simple(Column::single(reg_step(0))) -} - -/// CTL filter for the final round of the Keccak permutation. -pub(crate) fn ctl_filter_outputs() -> Filter { - Filter::new_simple(Column::single(reg_step(NUM_ROUNDS - 1))) -} - -#[derive(Copy, Clone, Default)] -pub(crate) struct KeccakStark { - pub(crate) f: PhantomData, -} - -impl, const D: usize> KeccakStark { - /// Generate the rows of the trace. Note that this does not generate the permuted columns used - /// in our lookup arguments, as those are computed after transposing to column-wise form. - fn generate_trace_rows( - &self, - inputs_and_timestamps: Vec<([u64; NUM_INPUTS], usize)>, - min_rows: usize, - ) -> Vec<[F; NUM_COLUMNS]> { - let num_rows = (inputs_and_timestamps.len() * NUM_ROUNDS) - .max(min_rows) - .next_power_of_two(); - - let mut rows = Vec::with_capacity(num_rows); - for input_and_timestamp in inputs_and_timestamps.iter() { - let rows_for_perm = self.generate_trace_rows_for_perm(*input_and_timestamp); - rows.extend(rows_for_perm); - } - - while rows.len() < num_rows { - rows.push([F::ZERO; NUM_COLUMNS]); - } - rows - } - - fn generate_trace_rows_for_perm( - &self, - input_and_timestamp: ([u64; NUM_INPUTS], usize), - ) -> Vec<[F; NUM_COLUMNS]> { - let mut rows = vec![[F::ZERO; NUM_COLUMNS]; NUM_ROUNDS]; - let input = input_and_timestamp.0; - let timestamp = input_and_timestamp.1; - // Set the timestamp of the current input. - // It will be checked against the value in `KeccakSponge`. - // The timestamp is used to link the input and output of - // the same permutation together. - for round in 0..24 { - rows[round][TIMESTAMP] = F::from_canonical_usize(timestamp); - } - - // Populate the round input for the first round. - for x in 0..5 { - for y in 0..5 { - let input_xy = input[y * 5 + x]; - let reg_lo = reg_a(x, y); - let reg_hi = reg_lo + 1; - rows[0][reg_lo] = F::from_canonical_u64(input_xy & 0xFFFFFFFF); - rows[0][reg_hi] = F::from_canonical_u64(input_xy >> 32); - } - } - - self.generate_trace_row_for_round(&mut rows[0], 0); - for round in 1..24 { - self.copy_output_to_input(rows[round - 1], &mut rows[round]); - self.generate_trace_row_for_round(&mut rows[round], round); - } - - rows - } - - fn copy_output_to_input(&self, prev_row: [F; NUM_COLUMNS], next_row: &mut [F; NUM_COLUMNS]) { - for x in 0..5 { - for y in 0..5 { - let in_lo = reg_a(x, y); - let in_hi = in_lo + 1; - let out_lo = reg_a_prime_prime_prime(x, y); - let out_hi = out_lo + 1; - next_row[in_lo] = prev_row[out_lo]; - next_row[in_hi] = prev_row[out_hi]; - } - } - } - - fn generate_trace_row_for_round(&self, row: &mut [F; NUM_COLUMNS], round: usize) { - row[reg_step(round)] = F::ONE; - - // Populate C[x] = xor(A[x, 0], A[x, 1], A[x, 2], A[x, 3], A[x, 4]). - for x in 0..5 { - for z in 0..64 { - let is_high_limb = z / 32; - let bit_in_limb = z % 32; - let a = [0, 1, 2, 3, 4].map(|i| { - let reg_a_limb = reg_a(x, i) + is_high_limb; - let a_limb = row[reg_a_limb].to_canonical_u64() as u32; - F::from_bool(((a_limb >> bit_in_limb) & 1) != 0) - }); - row[reg_c(x, z)] = xor(a); - } - } - - // Populate C'[x, z] = xor(C[x, z], C[x - 1, z], C[x + 1, z - 1]). - for x in 0..5 { - for z in 0..64 { - row[reg_c_prime(x, z)] = xor([ - row[reg_c(x, z)], - row[reg_c((x + 4) % 5, z)], - row[reg_c((x + 1) % 5, (z + 63) % 64)], - ]); - } - } - - // Populate A'. To avoid shifting indices, we rewrite - // A'[x, y, z] = xor(A[x, y, z], C[x - 1, z], C[x + 1, z - 1]) - // as - // A'[x, y, z] = xor(A[x, y, z], C[x, z], C'[x, z]). - for x in 0..5 { - for y in 0..5 { - for z in 0..64 { - let is_high_limb = z / 32; - let bit_in_limb = z % 32; - let reg_a_limb = reg_a(x, y) + is_high_limb; - let a_limb = row[reg_a_limb].to_canonical_u64() as u32; - let a_bit = F::from_bool(((a_limb >> bit_in_limb) & 1) != 0); - row[reg_a_prime(x, y, z)] = - xor([a_bit, row[reg_c(x, z)], row[reg_c_prime(x, z)]]); - } - } - } - - // Populate A''. - // A''[x, y] = xor(B[x, y], andn(B[x + 1, y], B[x + 2, y])). - for x in 0..5 { - for y in 0..5 { - let get_bit = |z| { - xor([ - row[reg_b(x, y, z)], - andn(row[reg_b((x + 1) % 5, y, z)], row[reg_b((x + 2) % 5, y, z)]), - ]) - }; - - let lo = (0..32) - .rev() - .fold(F::ZERO, |acc, z| acc.double() + get_bit(z)); - let hi = (32..64) - .rev() - .fold(F::ZERO, |acc, z| acc.double() + get_bit(z)); - - let reg_lo = reg_a_prime_prime(x, y); - let reg_hi = reg_lo + 1; - row[reg_lo] = lo; - row[reg_hi] = hi; - } - } - - // For the XOR, we split A''[0, 0] to bits. - let val_lo = row[reg_a_prime_prime(0, 0)].to_canonical_u64(); - let val_hi = row[reg_a_prime_prime(0, 0) + 1].to_canonical_u64(); - let val = val_lo | (val_hi << 32); - let bit_values: Vec = (0..64) - .scan(val, |acc, _| { - let tmp = *acc & 1; - *acc >>= 1; - Some(tmp) - }) - .collect(); - for i in 0..64 { - row[reg_a_prime_prime_0_0_bit(i)] = F::from_canonical_u64(bit_values[i]); - } - - // A''[0, 0] is additionally xor'd with RC. - let in_reg_lo = reg_a_prime_prime(0, 0); - let in_reg_hi = in_reg_lo + 1; - let out_reg_lo = reg_a_prime_prime_prime(0, 0); - let out_reg_hi = out_reg_lo + 1; - let rc_lo = rc_value(round) & ((1 << 32) - 1); - let rc_hi = rc_value(round) >> 32; - row[out_reg_lo] = F::from_canonical_u64(row[in_reg_lo].to_canonical_u64() ^ rc_lo); - row[out_reg_hi] = F::from_canonical_u64(row[in_reg_hi].to_canonical_u64() ^ rc_hi); - } - - pub(crate) fn generate_trace( - &self, - inputs: Vec<([u64; NUM_INPUTS], usize)>, - min_rows: usize, - timing: &mut TimingTree, - ) -> Vec> { - // Generate the witness, except for permuted columns in the lookup argument. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_rows(inputs, min_rows) - ); - let trace_polys = timed!( - timing, - "convert to PolynomialValues", - trace_rows_to_poly_values(trace_rows) - ); - trace_polys - } -} - -impl, const D: usize> Stark for KeccakStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - eval_round_flags(vars, yield_constr); - - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - // The filter must be 0 or 1. - let filter = local_values[reg_step(NUM_ROUNDS - 1)]; - yield_constr.constraint(filter * (filter - P::ONES)); - - // If this is not the final step, the filter must be off. - let final_step = local_values[reg_step(NUM_ROUNDS - 1)]; - let not_final_step = P::ONES - final_step; - yield_constr.constraint(not_final_step * filter); - - // If this is not the final step or a padding row, - // the local and next timestamps must match. - let sum_round_flags = (0..NUM_ROUNDS) - .map(|i| local_values[reg_step(i)]) - .sum::

(); - yield_constr.constraint( - sum_round_flags * not_final_step * (next_values[TIMESTAMP] - local_values[TIMESTAMP]), - ); - - // C'[x, z] = xor(C[x, z], C[x - 1, z], C[x + 1, z - 1]). - for x in 0..5 { - for z in 0..64 { - let xor = xor3_gen( - local_values[reg_c(x, z)], - local_values[reg_c((x + 4) % 5, z)], - local_values[reg_c((x + 1) % 5, (z + 63) % 64)], - ); - let c_prime = local_values[reg_c_prime(x, z)]; - yield_constr.constraint(c_prime - xor); - } - } - - // Check that the input limbs are consistent with A' and D. - // A[x, y, z] = xor(A'[x, y, z], D[x, y, z]) - // = xor(A'[x, y, z], C[x - 1, z], C[x + 1, z - 1]) - // = xor(A'[x, y, z], C[x, z], C'[x, z]). - // The last step is valid based on the identity we checked above. - // It isn't required, but makes this check a bit cleaner. - for x in 0..5 { - for y in 0..5 { - let a_lo = local_values[reg_a(x, y)]; - let a_hi = local_values[reg_a(x, y) + 1]; - let get_bit = |z| { - let a_prime = local_values[reg_a_prime(x, y, z)]; - let c = local_values[reg_c(x, z)]; - let c_prime = local_values[reg_c_prime(x, z)]; - xor3_gen(a_prime, c, c_prime) - }; - let computed_lo = (0..32) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_bit(z)); - let computed_hi = (32..64) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_bit(z)); - yield_constr.constraint(computed_lo - a_lo); - yield_constr.constraint(computed_hi - a_hi); - } - } - - // xor_{i=0}^4 A'[x, i, z] = C'[x, z], so for each x, z, - // diff * (diff - 2) * (diff - 4) = 0, where - // diff = sum_{i=0}^4 A'[x, i, z] - C'[x, z] - for x in 0..5 { - for z in 0..64 { - let sum: P = [0, 1, 2, 3, 4] - .map(|i| local_values[reg_a_prime(x, i, z)]) - .into_iter() - .sum(); - let diff = sum - local_values[reg_c_prime(x, z)]; - yield_constr - .constraint(diff * (diff - FE::TWO) * (diff - FE::from_canonical_u8(4))); - } - } - - // A''[x, y] = xor(B[x, y], andn(B[x + 1, y], B[x + 2, y])). - for x in 0..5 { - for y in 0..5 { - let get_bit = |z| { - xor_gen( - local_values[reg_b(x, y, z)], - andn_gen( - local_values[reg_b((x + 1) % 5, y, z)], - local_values[reg_b((x + 2) % 5, y, z)], - ), - ) - }; - - let reg_lo = reg_a_prime_prime(x, y); - let reg_hi = reg_lo + 1; - let lo = local_values[reg_lo]; - let hi = local_values[reg_hi]; - let computed_lo = (0..32) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_bit(z)); - let computed_hi = (32..64) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_bit(z)); - - yield_constr.constraint(computed_lo - lo); - yield_constr.constraint(computed_hi - hi); - } - } - - // A'''[0, 0] = A''[0, 0] XOR RC - let a_prime_prime_0_0_bits = (0..64) - .map(|i| local_values[reg_a_prime_prime_0_0_bit(i)]) - .collect_vec(); - let computed_a_prime_prime_0_0_lo = (0..32) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + a_prime_prime_0_0_bits[z]); - let computed_a_prime_prime_0_0_hi = (32..64) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + a_prime_prime_0_0_bits[z]); - let a_prime_prime_0_0_lo = local_values[reg_a_prime_prime(0, 0)]; - let a_prime_prime_0_0_hi = local_values[reg_a_prime_prime(0, 0) + 1]; - yield_constr.constraint(computed_a_prime_prime_0_0_lo - a_prime_prime_0_0_lo); - yield_constr.constraint(computed_a_prime_prime_0_0_hi - a_prime_prime_0_0_hi); - - let get_xored_bit = |i| { - let mut rc_bit_i = P::ZEROS; - for r in 0..NUM_ROUNDS { - let this_round = local_values[reg_step(r)]; - let this_round_constant = - P::from(FE::from_canonical_u32(rc_value_bit(r, i) as u32)); - rc_bit_i += this_round * this_round_constant; - } - - xor_gen(a_prime_prime_0_0_bits[i], rc_bit_i) - }; - - let a_prime_prime_prime_0_0_lo = local_values[reg_a_prime_prime_prime(0, 0)]; - let a_prime_prime_prime_0_0_hi = local_values[reg_a_prime_prime_prime(0, 0) + 1]; - let computed_a_prime_prime_prime_0_0_lo = (0..32) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_xored_bit(z)); - let computed_a_prime_prime_prime_0_0_hi = (32..64) - .rev() - .fold(P::ZEROS, |acc, z| acc.doubles() + get_xored_bit(z)); - yield_constr.constraint(computed_a_prime_prime_prime_0_0_lo - a_prime_prime_prime_0_0_lo); - yield_constr.constraint(computed_a_prime_prime_prime_0_0_hi - a_prime_prime_prime_0_0_hi); - - // Enforce that this round's output equals the next round's input. - for x in 0..5 { - for y in 0..5 { - let output_lo = local_values[reg_a_prime_prime_prime(x, y)]; - let output_hi = local_values[reg_a_prime_prime_prime(x, y) + 1]; - let input_lo = next_values[reg_a(x, y)]; - let input_hi = next_values[reg_a(x, y) + 1]; - let is_last_round = local_values[reg_step(NUM_ROUNDS - 1)]; - let not_last_round = P::ONES - is_last_round; - yield_constr.constraint_transition(not_last_round * (output_lo - input_lo)); - yield_constr.constraint_transition(not_last_round * (output_hi - input_hi)); - } - } - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let one_ext = builder.one_extension(); - let two = builder.two(); - let two_ext = builder.two_extension(); - let four_ext = builder.constant_extension(F::Extension::from_canonical_u8(4)); - - eval_round_flags_recursively(builder, vars, yield_constr); - - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - // The filter must be 0 or 1. - let filter = local_values[reg_step(NUM_ROUNDS - 1)]; - let constraint = builder.mul_sub_extension(filter, filter, filter); - yield_constr.constraint(builder, constraint); - - // If this is not the final step, the filter must be off. - let final_step = local_values[reg_step(NUM_ROUNDS - 1)]; - let not_final_step = builder.sub_extension(one_ext, final_step); - let constraint = builder.mul_extension(not_final_step, filter); - yield_constr.constraint(builder, constraint); - - // If this is not the final step or a padding row, - // the local and next timestamps must match. - let sum_round_flags = - builder.add_many_extension((0..NUM_ROUNDS).map(|i| local_values[reg_step(i)])); - let diff = builder.sub_extension(next_values[TIMESTAMP], local_values[TIMESTAMP]); - let constr = builder.mul_many_extension([sum_round_flags, not_final_step, diff]); - yield_constr.constraint(builder, constr); - - // C'[x, z] = xor(C[x, z], C[x - 1, z], C[x + 1, z - 1]). - for x in 0..5 { - for z in 0..64 { - let xor = xor3_gen_circuit( - builder, - local_values[reg_c(x, z)], - local_values[reg_c((x + 4) % 5, z)], - local_values[reg_c((x + 1) % 5, (z + 63) % 64)], - ); - let c_prime = local_values[reg_c_prime(x, z)]; - let diff = builder.sub_extension(c_prime, xor); - yield_constr.constraint(builder, diff); - } - } - - // Check that the input limbs are consistent with A' and D. - // A[x, y, z] = xor(A'[x, y, z], D[x, y, z]) - // = xor(A'[x, y, z], C[x - 1, z], C[x + 1, z - 1]) - // = xor(A'[x, y, z], C[x, z], C'[x, z]). - // The last step is valid based on the identity we checked above. - // It isn't required, but makes this check a bit cleaner. - for x in 0..5 { - for y in 0..5 { - let a_lo = local_values[reg_a(x, y)]; - let a_hi = local_values[reg_a(x, y) + 1]; - let mut get_bit = |z| { - let a_prime = local_values[reg_a_prime(x, y, z)]; - let c = local_values[reg_c(x, z)]; - let c_prime = local_values[reg_c_prime(x, z)]; - xor3_gen_circuit(builder, a_prime, c, c_prime) - }; - let bits_lo = (0..32).map(&mut get_bit).collect_vec(); - let bits_hi = (32..64).map(get_bit).collect_vec(); - let computed_lo = reduce_with_powers_ext_circuit(builder, &bits_lo, two); - let computed_hi = reduce_with_powers_ext_circuit(builder, &bits_hi, two); - let diff = builder.sub_extension(computed_lo, a_lo); - yield_constr.constraint(builder, diff); - let diff = builder.sub_extension(computed_hi, a_hi); - yield_constr.constraint(builder, diff); - } - } - - // xor_{i=0}^4 A'[x, i, z] = C'[x, z], so for each x, z, - // diff * (diff - 2) * (diff - 4) = 0, where - // diff = sum_{i=0}^4 A'[x, i, z] - C'[x, z] - for x in 0..5 { - for z in 0..64 { - let sum = builder.add_many_extension( - [0, 1, 2, 3, 4].map(|i| local_values[reg_a_prime(x, i, z)]), - ); - let diff = builder.sub_extension(sum, local_values[reg_c_prime(x, z)]); - let diff_minus_two = builder.sub_extension(diff, two_ext); - let diff_minus_four = builder.sub_extension(diff, four_ext); - let constraint = - builder.mul_many_extension([diff, diff_minus_two, diff_minus_four]); - yield_constr.constraint(builder, constraint); - } - } - - // A''[x, y] = xor(B[x, y], andn(B[x + 1, y], B[x + 2, y])). - for x in 0..5 { - for y in 0..5 { - let mut get_bit = |z| { - let andn = andn_gen_circuit( - builder, - local_values[reg_b((x + 1) % 5, y, z)], - local_values[reg_b((x + 2) % 5, y, z)], - ); - xor_gen_circuit(builder, local_values[reg_b(x, y, z)], andn) - }; - - let reg_lo = reg_a_prime_prime(x, y); - let reg_hi = reg_lo + 1; - let lo = local_values[reg_lo]; - let hi = local_values[reg_hi]; - let bits_lo = (0..32).map(&mut get_bit).collect_vec(); - let bits_hi = (32..64).map(get_bit).collect_vec(); - let computed_lo = reduce_with_powers_ext_circuit(builder, &bits_lo, two); - let computed_hi = reduce_with_powers_ext_circuit(builder, &bits_hi, two); - let diff = builder.sub_extension(computed_lo, lo); - yield_constr.constraint(builder, diff); - let diff = builder.sub_extension(computed_hi, hi); - yield_constr.constraint(builder, diff); - } - } - - // A'''[0, 0] = A''[0, 0] XOR RC - let a_prime_prime_0_0_bits = (0..64) - .map(|i| local_values[reg_a_prime_prime_0_0_bit(i)]) - .collect_vec(); - let computed_a_prime_prime_0_0_lo = - reduce_with_powers_ext_circuit(builder, &a_prime_prime_0_0_bits[0..32], two); - let computed_a_prime_prime_0_0_hi = - reduce_with_powers_ext_circuit(builder, &a_prime_prime_0_0_bits[32..64], two); - let a_prime_prime_0_0_lo = local_values[reg_a_prime_prime(0, 0)]; - let a_prime_prime_0_0_hi = local_values[reg_a_prime_prime(0, 0) + 1]; - let diff = builder.sub_extension(computed_a_prime_prime_0_0_lo, a_prime_prime_0_0_lo); - yield_constr.constraint(builder, diff); - let diff = builder.sub_extension(computed_a_prime_prime_0_0_hi, a_prime_prime_0_0_hi); - yield_constr.constraint(builder, diff); - - let mut get_xored_bit = |i| { - let mut rc_bit_i = builder.zero_extension(); - for r in 0..NUM_ROUNDS { - let this_round = local_values[reg_step(r)]; - let this_round_constant = builder - .constant_extension(F::from_canonical_u32(rc_value_bit(r, i) as u32).into()); - rc_bit_i = builder.mul_add_extension(this_round, this_round_constant, rc_bit_i); - } - - xor_gen_circuit(builder, a_prime_prime_0_0_bits[i], rc_bit_i) - }; - - let a_prime_prime_prime_0_0_lo = local_values[reg_a_prime_prime_prime(0, 0)]; - let a_prime_prime_prime_0_0_hi = local_values[reg_a_prime_prime_prime(0, 0) + 1]; - let bits_lo = (0..32).map(&mut get_xored_bit).collect_vec(); - let bits_hi = (32..64).map(get_xored_bit).collect_vec(); - let computed_a_prime_prime_prime_0_0_lo = - reduce_with_powers_ext_circuit(builder, &bits_lo, two); - let computed_a_prime_prime_prime_0_0_hi = - reduce_with_powers_ext_circuit(builder, &bits_hi, two); - let diff = builder.sub_extension( - computed_a_prime_prime_prime_0_0_lo, - a_prime_prime_prime_0_0_lo, - ); - yield_constr.constraint(builder, diff); - let diff = builder.sub_extension( - computed_a_prime_prime_prime_0_0_hi, - a_prime_prime_prime_0_0_hi, - ); - yield_constr.constraint(builder, diff); - - // Enforce that this round's output equals the next round's input. - for x in 0..5 { - for y in 0..5 { - let output_lo = local_values[reg_a_prime_prime_prime(x, y)]; - let output_hi = local_values[reg_a_prime_prime_prime(x, y) + 1]; - let input_lo = next_values[reg_a(x, y)]; - let input_hi = next_values[reg_a(x, y) + 1]; - let is_last_round = local_values[reg_step(NUM_ROUNDS - 1)]; - let diff = builder.sub_extension(input_lo, output_lo); - let filtered_diff = builder.mul_sub_extension(is_last_round, diff, diff); - yield_constr.constraint_transition(builder, filtered_diff); - let diff = builder.sub_extension(input_hi, output_hi); - let filtered_diff = builder.mul_sub_extension(is_last_round, diff, diff); - yield_constr.constraint_transition(builder, filtered_diff); - } - } - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; - use plonky2::field::types::PrimeField64; - use plonky2::fri::oracle::PolynomialBatch; - use plonky2::iop::challenger::Challenger; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::config::StarkConfig; - use starky::cross_table_lookup::{CtlData, CtlZData}; - use starky::lookup::{GrandProductChallenge, GrandProductChallengeSet}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - use tiny_keccak::keccakf; - - use super::*; - use crate::prover::prove_single_table; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } - - #[test] - fn keccak_correctness_test() -> Result<()> { - let input: [u64; NUM_INPUTS] = rand::random(); - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakStark; - - let stark = S { - f: Default::default(), - }; - - let rows = stark.generate_trace_rows(vec![(input, 0)], 8); - let last_row = rows[NUM_ROUNDS - 1]; - let output = (0..NUM_INPUTS) - .map(|i| { - let hi = last_row[reg_output_limb(2 * i + 1)].to_canonical_u64(); - let lo = last_row[reg_output_limb(2 * i)].to_canonical_u64(); - (hi << 32) | lo - }) - .collect::>(); - - let expected = { - let mut state = input; - keccakf(&mut state); - state - }; - - assert_eq!(output, expected); - - Ok(()) - } - - #[test] - fn keccak_benchmark() -> Result<()> { - const NUM_PERMS: usize = 85; - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakStark; - let stark = S::default(); - let config = StarkConfig::standard_fast_config(); - - init_logger(); - - let input: Vec<([u64; NUM_INPUTS], usize)> = - (0..NUM_PERMS).map(|_| (rand::random(), 0)).collect(); - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let trace_poly_values = timed!( - timing, - "generate trace", - stark.generate_trace(input, 8, &mut timing) - ); - - let cloned_trace_poly_values = timed!(timing, "clone", trace_poly_values.clone()); - - let trace_commitments = timed!( - timing, - "compute trace commitment", - PolynomialBatch::::from_values( - cloned_trace_poly_values, - config.fri_config.rate_bits, - false, - config.fri_config.cap_height, - &mut timing, - None, - ) - ); - let degree = 1 << trace_commitments.degree_log; - - // Fake CTL data. - let ctl_z_data = CtlZData::new( - vec![PolynomialValues::zero(degree)], - PolynomialValues::zero(degree), - GrandProductChallenge { - beta: F::ZERO, - gamma: F::ZERO, - }, - vec![], - vec![Some(Filter::new_simple(Column::constant(F::ZERO)))], - ); - let ctl_data = CtlData { - zs_columns: vec![ctl_z_data.clone(); config.num_challenges], - }; - - prove_single_table( - &stark, - &config, - &trace_poly_values, - &trace_commitments, - &ctl_data, - &GrandProductChallengeSet { - challenges: vec![ctl_z_data.challenge; config.num_challenges], - }, - &mut Challenger::new(), - &mut timing, - None, - )?; - - timing.print(); - Ok(()) - } - - fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug")); - } -} diff --git a/evm/src/keccak/logic.rs b/evm/src/keccak/logic.rs deleted file mode 100644 index 4e29b93fb8..0000000000 --- a/evm/src/keccak/logic.rs +++ /dev/null @@ -1,65 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::PrimeField64; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -pub(crate) fn xor(xs: [F; N]) -> F { - xs.into_iter().fold(F::ZERO, |acc, x| { - debug_assert!(x.is_zero() || x.is_one()); - F::from_canonical_u64(acc.to_canonical_u64() ^ x.to_canonical_u64()) - }) -} - -/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. -pub(crate) fn xor_gen(x: P, y: P) -> P { - x + y - x * y.doubles() -} - -/// Computes the arithmetic generalization of `xor3(x, y, z)`. -pub(crate) fn xor3_gen(x: P, y: P, z: P) -> P { - xor_gen(x, xor_gen(y, z)) -} - -/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. -pub(crate) fn xor_gen_circuit, const D: usize>( - builder: &mut CircuitBuilder, - x: ExtensionTarget, - y: ExtensionTarget, -) -> ExtensionTarget { - let sum = builder.add_extension(x, y); - builder.arithmetic_extension(-F::TWO, F::ONE, x, y, sum) -} - -/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. -pub(crate) fn xor3_gen_circuit, const D: usize>( - builder: &mut CircuitBuilder, - x: ExtensionTarget, - y: ExtensionTarget, - z: ExtensionTarget, -) -> ExtensionTarget { - let x_xor_y = xor_gen_circuit(builder, x, y); - xor_gen_circuit(builder, x_xor_y, z) -} - -pub(crate) fn andn(x: F, y: F) -> F { - debug_assert!(x.is_zero() || x.is_one()); - debug_assert!(y.is_zero() || y.is_one()); - let x = x.to_canonical_u64(); - let y = y.to_canonical_u64(); - F::from_canonical_u64(!x & y) -} - -pub(crate) fn andn_gen(x: P, y: P) -> P { - (P::ONES - x) * y -} - -pub(crate) fn andn_gen_circuit, const D: usize>( - builder: &mut CircuitBuilder, - x: ExtensionTarget, - y: ExtensionTarget, -) -> ExtensionTarget { - // (1 - x) y = -xy + y - builder.arithmetic_extension(F::NEG_ONE, F::ONE, x, y, y) -} diff --git a/evm/src/keccak/mod.rs b/evm/src/keccak/mod.rs deleted file mode 100644 index d71e9e9cc7..0000000000 --- a/evm/src/keccak/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod columns; -pub mod constants; -pub mod keccak_stark; -pub mod logic; -pub mod round_flags; diff --git a/evm/src/keccak/round_flags.rs b/evm/src/keccak/round_flags.rs deleted file mode 100644 index 5e76b2ec9c..0000000000 --- a/evm/src/keccak/round_flags.rs +++ /dev/null @@ -1,74 +0,0 @@ -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; - -use crate::all_stark::EvmStarkFrame; -use crate::keccak::columns::{reg_step, NUM_COLUMNS}; -use crate::keccak::keccak_stark::NUM_ROUNDS; - -pub(crate) fn eval_round_flags>( - vars: &EvmStarkFrame, - yield_constr: &mut ConstraintConsumer

, -) { - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - // Initially, the first step flag should be 1 while the others should be 0. - yield_constr.constraint_first_row(local_values[reg_step(0)] - F::ONE); - for i in 1..NUM_ROUNDS { - yield_constr.constraint_first_row(local_values[reg_step(i)]); - } - - // Flags should circularly increment, or be all zero for padding rows. - let next_any_flag = (0..NUM_ROUNDS).map(|i| next_values[reg_step(i)]).sum::

(); - for i in 0..NUM_ROUNDS { - let current_round_flag = local_values[reg_step(i)]; - let next_round_flag = next_values[reg_step((i + 1) % NUM_ROUNDS)]; - yield_constr.constraint_transition(next_any_flag * (next_round_flag - current_round_flag)); - } - - // Padding rows should always be followed by padding rows. - let current_any_flag = (0..NUM_ROUNDS) - .map(|i| local_values[reg_step(i)]) - .sum::

(); - yield_constr.constraint_transition(next_any_flag * (current_any_flag - F::ONE)); -} - -pub(crate) fn eval_round_flags_recursively, const D: usize>( - builder: &mut CircuitBuilder, - vars: &EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>, - yield_constr: &mut RecursiveConstraintConsumer, -) { - let one = builder.one_extension(); - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - // Initially, the first step flag should be 1 while the others should be 0. - let step_0_minus_1 = builder.sub_extension(local_values[reg_step(0)], one); - yield_constr.constraint_first_row(builder, step_0_minus_1); - for i in 1..NUM_ROUNDS { - yield_constr.constraint_first_row(builder, local_values[reg_step(i)]); - } - - // Flags should circularly increment, or be all zero for padding rows. - let next_any_flag = - builder.add_many_extension((0..NUM_ROUNDS).map(|i| next_values[reg_step(i)])); - for i in 0..NUM_ROUNDS { - let current_round_flag = local_values[reg_step(i)]; - let next_round_flag = next_values[reg_step((i + 1) % NUM_ROUNDS)]; - let diff = builder.sub_extension(next_round_flag, current_round_flag); - let constraint = builder.mul_extension(next_any_flag, diff); - yield_constr.constraint_transition(builder, constraint); - } - - // Padding rows should always be followed by padding rows. - let current_any_flag = - builder.add_many_extension((0..NUM_ROUNDS).map(|i| local_values[reg_step(i)])); - let constraint = builder.mul_sub_extension(next_any_flag, current_any_flag, next_any_flag); - yield_constr.constraint_transition(builder, constraint); -} diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs deleted file mode 100644 index b35ff1fa10..0000000000 --- a/evm/src/keccak_sponge/columns.rs +++ /dev/null @@ -1,156 +0,0 @@ -use core::borrow::{Borrow, BorrowMut}; -use core::mem::{size_of, transmute}; -use core::ops::Range; - -use crate::util::{indices_arr, transmute_no_compile_time_size_checks}; - -/// Total number of sponge bytes: number of rate bytes + number of capacity bytes. -pub(crate) const KECCAK_WIDTH_BYTES: usize = 200; -/// Total number of 32-bit limbs in the sponge. -pub(crate) const KECCAK_WIDTH_U32S: usize = KECCAK_WIDTH_BYTES / 4; -/// Number of non-digest bytes. -pub(crate) const KECCAK_WIDTH_MINUS_DIGEST_U32S: usize = - (KECCAK_WIDTH_BYTES - KECCAK_DIGEST_BYTES) / 4; -/// Number of rate bytes. -pub(crate) const KECCAK_RATE_BYTES: usize = 136; -/// Number of 32-bit rate limbs. -pub(crate) const KECCAK_RATE_U32S: usize = KECCAK_RATE_BYTES / 4; -/// Number of capacity bytes. -pub(crate) const KECCAK_CAPACITY_BYTES: usize = 64; -/// Number of 32-bit capacity limbs. -pub(crate) const KECCAK_CAPACITY_U32S: usize = KECCAK_CAPACITY_BYTES / 4; -/// Number of output digest bytes used during the squeezing phase. -pub(crate) const KECCAK_DIGEST_BYTES: usize = 32; -/// Number of 32-bit digest limbs. -pub(crate) const KECCAK_DIGEST_U32S: usize = KECCAK_DIGEST_BYTES / 4; - -/// A view of `KeccakSpongeStark`'s columns. -#[repr(C)] -#[derive(Eq, PartialEq, Debug)] -pub(crate) struct KeccakSpongeColumnsView { - /// 1 if this row represents a full input block, i.e. one in which each byte is an input byte, - /// not a padding byte; 0 otherwise. - pub is_full_input_block: T, - - /// The context of the base address at which we will read the input block. - pub context: T, - /// The segment of the base address at which we will read the input block. - pub segment: T, - /// The virtual address at which we will read the input block. - pub virt: T, - - /// The timestamp at which inputs should be read from memory. - pub timestamp: T, - - /// The number of input bytes that have already been absorbed prior to this block. - pub already_absorbed_bytes: T, - - /// If this row represents a final block row, the `i`th entry should be 1 if the final chunk of - /// input has length `i` (in other words if `len - already_absorbed == i`), otherwise 0. - /// - /// If this row represents a full input block, this should contain all 0s. - pub is_final_input_len: [T; KECCAK_RATE_BYTES], - - /// The initial rate part of the sponge, at the start of this step. - pub original_rate_u32s: [T; KECCAK_RATE_U32S], - - /// The capacity part of the sponge, encoded as 32-bit chunks, at the start of this step. - pub original_capacity_u32s: [T; KECCAK_CAPACITY_U32S], - - /// The block being absorbed, which may contain input bytes and/or padding bytes. - pub block_bytes: [T; KECCAK_RATE_BYTES], - - /// The rate part of the sponge, encoded as 32-bit chunks, after the current block is xor'd in, - /// but before the permutation is applied. - pub xored_rate_u32s: [T; KECCAK_RATE_U32S], - - /// The entire state (rate + capacity) of the sponge, encoded as 32-bit chunks, after the - /// permutation is applied, minus the first limbs where the digest is extracted from. - /// Those missing limbs can be recomputed from their corresponding bytes stored in - /// `updated_digest_state_bytes`. - pub partial_updated_state_u32s: [T; KECCAK_WIDTH_MINUS_DIGEST_U32S], - - /// The first part of the state of the sponge, seen as bytes, after the permutation is applied. - /// This also represents the output digest of the Keccak sponge during the squeezing phase. - pub updated_digest_state_bytes: [T; KECCAK_DIGEST_BYTES], - - /// The counter column (used for the range check) starts from 0 and increments. - pub range_counter: T, - /// The frequencies column used in logUp. - pub rc_frequencies: T, -} - -// `u8` is guaranteed to have a `size_of` of 1. -/// Number of columns in `KeccakSpongeStark`. -pub(crate) const NUM_KECCAK_SPONGE_COLUMNS: usize = size_of::>(); - -// Indices for LogUp range-check. -// They are on the last registers of this table. -pub(crate) const RC_FREQUENCIES: usize = NUM_KECCAK_SPONGE_COLUMNS - 1; -pub(crate) const RANGE_COUNTER: usize = RC_FREQUENCIES - 1; - -pub(crate) const BLOCK_BYTES_START: usize = - 6 + KECCAK_RATE_BYTES + KECCAK_RATE_U32S + KECCAK_CAPACITY_U32S; -/// Indices for the range-checked values, i.e. the `block_bytes` section. -// TODO: Find a better way to access those indices -pub(crate) const fn get_block_bytes_range() -> Range { - BLOCK_BYTES_START..BLOCK_BYTES_START + KECCAK_RATE_BYTES -} - -/// Return the index for the targeted `block_bytes` element. -pub(crate) const fn get_single_block_bytes_value(i: usize) -> usize { - debug_assert!(i < KECCAK_RATE_BYTES); - get_block_bytes_range().start + i -} - -impl From<[T; NUM_KECCAK_SPONGE_COLUMNS]> for KeccakSpongeColumnsView { - fn from(value: [T; NUM_KECCAK_SPONGE_COLUMNS]) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl From> for [T; NUM_KECCAK_SPONGE_COLUMNS] { - fn from(value: KeccakSpongeColumnsView) -> Self { - unsafe { transmute_no_compile_time_size_checks(value) } - } -} - -impl Borrow> for [T; NUM_KECCAK_SPONGE_COLUMNS] { - fn borrow(&self) -> &KeccakSpongeColumnsView { - unsafe { transmute(self) } - } -} - -impl BorrowMut> for [T; NUM_KECCAK_SPONGE_COLUMNS] { - fn borrow_mut(&mut self) -> &mut KeccakSpongeColumnsView { - unsafe { transmute(self) } - } -} - -impl Borrow<[T; NUM_KECCAK_SPONGE_COLUMNS]> for KeccakSpongeColumnsView { - fn borrow(&self) -> &[T; NUM_KECCAK_SPONGE_COLUMNS] { - unsafe { transmute(self) } - } -} - -impl BorrowMut<[T; NUM_KECCAK_SPONGE_COLUMNS]> for KeccakSpongeColumnsView { - fn borrow_mut(&mut self) -> &mut [T; NUM_KECCAK_SPONGE_COLUMNS] { - unsafe { transmute(self) } - } -} - -impl Default for KeccakSpongeColumnsView { - fn default() -> Self { - [T::default(); NUM_KECCAK_SPONGE_COLUMNS].into() - } -} - -const fn make_col_map() -> KeccakSpongeColumnsView { - let indices_arr = indices_arr::(); - unsafe { - transmute::<[usize; NUM_KECCAK_SPONGE_COLUMNS], KeccakSpongeColumnsView>(indices_arr) - } -} - -/// Map between the `KeccakSponge` columns and (0..`NUM_KECCAK_SPONGE_COLUMNS`) -pub(crate) const KECCAK_SPONGE_COL_MAP: KeccakSpongeColumnsView = make_col_map(); diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs deleted file mode 100644 index 04b1bca63b..0000000000 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ /dev/null @@ -1,879 +0,0 @@ -use core::borrow::Borrow; -use core::iter::{self, once, repeat}; -use core::marker::PhantomData; -use core::mem::size_of; - -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use plonky2::util::transpose; -use plonky2_util::ceil_div_usize; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter, Lookup}; -use starky::stark::Stark; - -use crate::all_stark::EvmStarkFrame; -use crate::cpu::kernel::keccak_util::keccakf_u32s; -use crate::keccak_sponge::columns::*; -use crate::witness::memory::MemoryAddress; - -/// Strict upper bound for the individual bytes range-check. -const BYTE_RANGE_MAX: usize = 256; - -/// Creates the vector of `Columns` corresponding to: -/// - the address in memory of the inputs, -/// - the length of the inputs, -/// - the timestamp at which the inputs are read from memory, -/// - the output limbs of the Keccak sponge. -pub(crate) fn ctl_looked_data() -> Vec> { - let cols = KECCAK_SPONGE_COL_MAP; - let mut outputs = Vec::with_capacity(8); - for i in (0..8).rev() { - let cur_col = Column::linear_combination( - cols.updated_digest_state_bytes[i * 4..(i + 1) * 4] - .iter() - .enumerate() - .map(|(j, &c)| (c, F::from_canonical_u64(1 << (24 - 8 * j)))), - ); - outputs.push(cur_col); - } - - // The length of the inputs is `already_absorbed_bytes + is_final_input_len`. - let len_col = Column::linear_combination( - iter::once((cols.already_absorbed_bytes, F::ONE)).chain( - cols.is_final_input_len - .iter() - .enumerate() - .map(|(i, &elt)| (elt, F::from_canonical_usize(i))), - ), - ); - - let mut res: Vec> = - Column::singles([cols.context, cols.segment, cols.virt]).collect(); - res.push(len_col); - res.push(Column::single(cols.timestamp)); - res.extend(outputs); - - res -} - -/// Creates the vector of `Columns` corresponding to the inputs of the Keccak sponge. -/// This is used to check that the inputs of the sponge correspond to the inputs -/// given by `KeccakStark`. -pub(crate) fn ctl_looking_keccak_inputs() -> Vec> { - let cols = KECCAK_SPONGE_COL_MAP; - let mut res: Vec<_> = Column::singles( - [ - cols.xored_rate_u32s.as_slice(), - &cols.original_capacity_u32s, - ] - .concat(), - ) - .collect(); - res.push(Column::single(cols.timestamp)); - - res -} - -/// Creates the vector of `Columns` corresponding to the outputs of the Keccak sponge. -/// This is used to check that the outputs of the sponge correspond to the outputs -/// given by `KeccakStark`. -pub(crate) fn ctl_looking_keccak_outputs() -> Vec> { - let cols = KECCAK_SPONGE_COL_MAP; - - // We recover the 32-bit digest limbs from their corresponding bytes, - // and then append them to the rest of the updated state limbs. - let digest_u32s = cols.updated_digest_state_bytes.chunks_exact(4).map(|c| { - Column::linear_combination( - c.iter() - .enumerate() - .map(|(i, &b)| (b, F::from_canonical_usize(1 << (8 * i)))), - ) - }); - - let mut res: Vec<_> = digest_u32s.collect(); - - res.extend(Column::singles(&cols.partial_updated_state_u32s)); - res.push(Column::single(cols.timestamp)); - - res -} - -/// Creates the vector of `Columns` corresponding to the address and value of the byte being read from memory. -pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { - let cols = KECCAK_SPONGE_COL_MAP; - - let mut res = vec![Column::constant(F::ONE)]; // is_read - - res.extend(Column::singles([cols.context, cols.segment])); - - // The address of the byte being read is `virt + already_absorbed_bytes + i`. - res.push(Column::linear_combination_with_constant( - [(cols.virt, F::ONE), (cols.already_absorbed_bytes, F::ONE)], - F::from_canonical_usize(i), - )); - - // The i'th input byte being read. - res.push(Column::single(cols.block_bytes[i])); - - // Since we're reading a single byte, the higher limbs must be zero. - res.extend((1..8).map(|_| Column::zero())); - - res.push(Column::single(cols.timestamp)); - - assert_eq!( - res.len(), - crate::memory::memory_stark::ctl_data::().len() - ); - res -} - -/// Returns the number of `KeccakSponge` tables looking into the `LogicStark`. -pub(crate) const fn num_logic_ctls() -> usize { - const U8S_PER_CTL: usize = 32; - ceil_div_usize(KECCAK_RATE_BYTES, U8S_PER_CTL) -} - -/// Creates the vector of `Columns` required to perform the `i`th logic CTL. -/// It is comprised of the ÌS_XOR` flag, the two inputs and the output -/// of the XOR operation. -/// Since we need to do 136 byte XORs, and the logic CTL can -/// XOR 32 bytes per CTL, there are 5 such CTLs. -pub(crate) fn ctl_looking_logic(i: usize) -> Vec> { - const U32S_PER_CTL: usize = 8; - const U8S_PER_CTL: usize = 32; - - debug_assert!(i < num_logic_ctls()); - let cols = KECCAK_SPONGE_COL_MAP; - - let mut res = vec![ - Column::constant(F::from_canonical_u8(0x18)), // is_xor - ]; - - // Input 0 contains some of the sponge's original rate chunks. If this is the last CTL, we won't - // need to use all of the CTL's inputs, so we will pass some zeros. - res.extend( - Column::singles(&cols.original_rate_u32s[i * U32S_PER_CTL..]) - .chain(repeat(Column::zero())) - .take(U32S_PER_CTL), - ); - - // Input 1 contains some of block's chunks. Again, for the last CTL it will include some zeros. - res.extend( - cols.block_bytes[i * U8S_PER_CTL..] - .chunks(size_of::()) - .map(|chunk| Column::le_bytes(chunk)) - .chain(repeat(Column::zero())) - .take(U32S_PER_CTL), - ); - - // The output contains the XOR'd rate part. - res.extend( - Column::singles(&cols.xored_rate_u32s[i * U32S_PER_CTL..]) - .chain(repeat(Column::zero())) - .take(U32S_PER_CTL), - ); - - res -} - -/// CTL filter for the final block rows of the `KeccakSponge` table. -pub(crate) fn ctl_looked_filter() -> Filter { - // The CPU table is only interested in our final-block rows, since those contain the final - // sponge output. - Filter::new_simple(Column::sum(KECCAK_SPONGE_COL_MAP.is_final_input_len)) -} - -/// CTL filter for reading the `i`th byte of input from memory. -pub(crate) fn ctl_looking_memory_filter(i: usize) -> Filter { - // We perform the `i`th read if either - // - this is a full input block, or - // - this is a final block of length `i` or greater - let cols = KECCAK_SPONGE_COL_MAP; - if i == KECCAK_RATE_BYTES - 1 { - Filter::new_simple(Column::single(cols.is_full_input_block)) - } else { - Filter::new_simple(Column::sum( - once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i + 1..]), - )) - } -} - -/// CTL filter for looking at XORs in the logic table. -pub(crate) fn ctl_looking_logic_filter() -> Filter { - let cols = KECCAK_SPONGE_COL_MAP; - Filter::new_simple(Column::sum( - once(&cols.is_full_input_block).chain(&cols.is_final_input_len), - )) -} - -/// CTL filter for looking at the input and output in the Keccak table. -pub(crate) fn ctl_looking_keccak_filter() -> Filter { - let cols = KECCAK_SPONGE_COL_MAP; - Filter::new_simple(Column::sum( - once(&cols.is_full_input_block).chain(&cols.is_final_input_len), - )) -} - -/// Information about a Keccak sponge operation needed for witness generation. -#[derive(Clone, Debug)] -pub(crate) struct KeccakSpongeOp { - /// The base address at which inputs are read. - pub(crate) base_address: MemoryAddress, - - /// The timestamp at which inputs are read. - pub(crate) timestamp: usize, - - /// The input that was read. - pub(crate) input: Vec, -} - -/// Structure representing the `KeccakSponge` STARK, which carries out the sponge permutation. -#[derive(Copy, Clone, Default)] -pub(crate) struct KeccakSpongeStark { - f: PhantomData, -} - -impl, const D: usize> KeccakSpongeStark { - /// Generates the trace polynomial values for the `KeccakSponge`STARK. - pub(crate) fn generate_trace( - &self, - operations: Vec, - min_rows: usize, - timing: &mut TimingTree, - ) -> Vec> { - // Generate the witness row-wise. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_rows(operations, min_rows) - ); - - let trace_row_vecs: Vec<_> = trace_rows.into_iter().map(|row| row.to_vec()).collect(); - - let mut trace_cols = transpose(&trace_row_vecs); - self.generate_range_checks(&mut trace_cols); - - trace_cols.into_iter().map(PolynomialValues::new).collect() - } - - /// Generates the trace rows given the vector of `KeccakSponge` operations. - /// The trace is padded to a power of two with all-zero rows. - fn generate_trace_rows( - &self, - operations: Vec, - min_rows: usize, - ) -> Vec<[F; NUM_KECCAK_SPONGE_COLUMNS]> { - let base_len: usize = operations - .iter() - .map(|op| op.input.len() / KECCAK_RATE_BYTES + 1) - .sum(); - let mut rows = Vec::with_capacity(base_len.max(min_rows).next_power_of_two()); - // Generate active rows. - for op in operations { - rows.extend(self.generate_rows_for_op(op)); - } - // Pad the trace. - let padded_rows = rows.len().max(min_rows).next_power_of_two(); - for _ in rows.len()..padded_rows { - rows.push(self.generate_padding_row()); - } - rows - } - - /// Generates the rows associated to a given operation: - /// Performs a Keccak sponge permutation and fills the STARK's rows accordingly. - /// The number of rows is the number of input chunks of size `KECCAK_RATE_BYTES`. - fn generate_rows_for_op(&self, op: KeccakSpongeOp) -> Vec<[F; NUM_KECCAK_SPONGE_COLUMNS]> { - let mut rows = Vec::with_capacity(op.input.len() / KECCAK_RATE_BYTES + 1); - - let mut sponge_state = [0u32; KECCAK_WIDTH_U32S]; - - let mut input_blocks = op.input.chunks_exact(KECCAK_RATE_BYTES); - let mut already_absorbed_bytes = 0; - for block in input_blocks.by_ref() { - // We compute the updated state of the sponge. - let row = self.generate_full_input_row( - &op, - already_absorbed_bytes, - sponge_state, - block.try_into().unwrap(), - ); - - // We update the state limbs for the next block absorption. - // The first `KECCAK_DIGEST_U32s` limbs are stored as bytes after the computation, - // so we recompute the corresponding `u32` and update the first state limbs. - sponge_state[..KECCAK_DIGEST_U32S] - .iter_mut() - .zip(row.updated_digest_state_bytes.chunks_exact(4)) - .for_each(|(s, bs)| { - *s = bs - .iter() - .enumerate() - .map(|(i, b)| (b.to_canonical_u64() as u32) << (8 * i)) - .sum(); - }); - - // The rest of the bytes are already stored in the expected form, so we can directly - // update the state with the stored values. - sponge_state[KECCAK_DIGEST_U32S..] - .iter_mut() - .zip(row.partial_updated_state_u32s) - .for_each(|(s, x)| *s = x.to_canonical_u64() as u32); - - rows.push(row.into()); - already_absorbed_bytes += KECCAK_RATE_BYTES; - } - - rows.push( - self.generate_final_row( - &op, - already_absorbed_bytes, - sponge_state, - input_blocks.remainder(), - ) - .into(), - ); - - rows - } - - /// Generates a row where all bytes are input bytes, not padding bytes. - /// This includes updating the state sponge with a single absorption. - fn generate_full_input_row( - &self, - op: &KeccakSpongeOp, - already_absorbed_bytes: usize, - sponge_state: [u32; KECCAK_WIDTH_U32S], - block: [u8; KECCAK_RATE_BYTES], - ) -> KeccakSpongeColumnsView { - let mut row = KeccakSpongeColumnsView { - is_full_input_block: F::ONE, - ..Default::default() - }; - - row.block_bytes = block.map(F::from_canonical_u8); - - Self::generate_common_fields(&mut row, op, already_absorbed_bytes, sponge_state); - row - } - - /// Generates a row containing the last input bytes. - /// On top of computing one absorption and padding the input, - /// we indicate the last non-padding input byte by setting - /// `row.is_final_input_len[final_inputs.len()]` to 1. - fn generate_final_row( - &self, - op: &KeccakSpongeOp, - already_absorbed_bytes: usize, - sponge_state: [u32; KECCAK_WIDTH_U32S], - final_inputs: &[u8], - ) -> KeccakSpongeColumnsView { - assert_eq!(already_absorbed_bytes + final_inputs.len(), op.input.len()); - - let mut row = KeccakSpongeColumnsView::default(); - - for (block_byte, input_byte) in row.block_bytes.iter_mut().zip(final_inputs) { - *block_byte = F::from_canonical_u8(*input_byte); - } - - // pad10*1 rule - if final_inputs.len() == KECCAK_RATE_BYTES - 1 { - // Both 1s are placed in the same byte. - row.block_bytes[final_inputs.len()] = F::from_canonical_u8(0b10000001); - } else { - row.block_bytes[final_inputs.len()] = F::ONE; - row.block_bytes[KECCAK_RATE_BYTES - 1] = F::from_canonical_u8(0b10000000); - } - - row.is_final_input_len[final_inputs.len()] = F::ONE; - - Self::generate_common_fields(&mut row, op, already_absorbed_bytes, sponge_state); - row - } - - /// Generate fields that are common to both full-input-block rows and final-block rows. - /// Also updates the sponge state with a single absorption. - /// Given a state S = R || C and a block input B, - /// - R is updated with R XOR B, - /// - S is replaced by keccakf_u32s(S). - fn generate_common_fields( - row: &mut KeccakSpongeColumnsView, - op: &KeccakSpongeOp, - already_absorbed_bytes: usize, - mut sponge_state: [u32; KECCAK_WIDTH_U32S], - ) { - row.context = F::from_canonical_usize(op.base_address.context); - row.segment = F::from_canonical_usize(op.base_address.segment); - row.virt = F::from_canonical_usize(op.base_address.virt); - row.timestamp = F::from_canonical_usize(op.timestamp); - row.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes); - - row.original_rate_u32s = sponge_state[..KECCAK_RATE_U32S] - .iter() - .map(|x| F::from_canonical_u32(*x)) - .collect_vec() - .try_into() - .unwrap(); - - row.original_capacity_u32s = sponge_state[KECCAK_RATE_U32S..] - .iter() - .map(|x| F::from_canonical_u32(*x)) - .collect_vec() - .try_into() - .unwrap(); - - let block_u32s = (0..KECCAK_RATE_U32S).map(|i| { - u32::from_le_bytes( - row.block_bytes[i * 4..(i + 1) * 4] - .iter() - .map(|x| x.to_canonical_u64() as u8) - .collect_vec() - .try_into() - .unwrap(), - ) - }); - - // xor in the block - for (state_i, block_i) in sponge_state.iter_mut().zip(block_u32s) { - *state_i ^= block_i; - } - let xored_rate_u32s: [u32; KECCAK_RATE_U32S] = sponge_state[..KECCAK_RATE_U32S] - .to_vec() - .try_into() - .unwrap(); - row.xored_rate_u32s = xored_rate_u32s.map(F::from_canonical_u32); - - keccakf_u32s(&mut sponge_state); - // Store all but the first `KECCAK_DIGEST_U32S` limbs in the updated state. - // Those missing limbs will be broken down into bytes and stored separately. - row.partial_updated_state_u32s.copy_from_slice( - &sponge_state[KECCAK_DIGEST_U32S..] - .iter() - .copied() - .map(|i| F::from_canonical_u32(i)) - .collect::>(), - ); - sponge_state[..KECCAK_DIGEST_U32S] - .iter() - .enumerate() - .for_each(|(l, &elt)| { - let mut cur_elt = elt; - (0..4).for_each(|i| { - row.updated_digest_state_bytes[l * 4 + i] = - F::from_canonical_u32(cur_elt & 0xFF); - cur_elt >>= 8; - }); - - // 32-bit limb reconstruction consistency check. - let mut s = row.updated_digest_state_bytes[l * 4].to_canonical_u64(); - for i in 1..4 { - s += row.updated_digest_state_bytes[l * 4 + i].to_canonical_u64() << (8 * i); - } - assert_eq!(elt as u64, s, "not equal"); - }) - } - - fn generate_padding_row(&self) -> [F; NUM_KECCAK_SPONGE_COLUMNS] { - // The default instance has is_full_input_block = is_final_block = 0, - // indicating that it's a dummy/padding row. - KeccakSpongeColumnsView::default().into() - } - - /// Expects input in *column*-major layout - fn generate_range_checks(&self, cols: &mut [Vec]) { - debug_assert!(cols.len() == NUM_KECCAK_SPONGE_COLUMNS); - - let n_rows = cols[0].len(); - debug_assert!(cols.iter().all(|col| col.len() == n_rows)); - - for i in 0..BYTE_RANGE_MAX { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(i); - } - for i in BYTE_RANGE_MAX..n_rows { - cols[RANGE_COUNTER][i] = F::from_canonical_usize(BYTE_RANGE_MAX - 1); - } - - // For each column c in cols, generate the range-check - // permutations and put them in the corresponding range-check - // columns rc_c and rc_c+1. - for col in 0..KECCAK_RATE_BYTES { - let c = get_single_block_bytes_value(col); - for i in 0..n_rows { - let x = cols[c][i].to_canonical_u64() as usize; - assert!( - x < BYTE_RANGE_MAX, - "column value {} exceeds the max range value {}", - x, - BYTE_RANGE_MAX - ); - cols[RC_FREQUENCIES][x] += F::ONE; - } - } - } -} - -impl, const D: usize> Stark for KeccakSpongeStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = - EvmStarkFrame, ExtensionTarget, NUM_KECCAK_SPONGE_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let local_values: &[P; NUM_KECCAK_SPONGE_COLUMNS] = - vars.get_local_values().try_into().unwrap(); - let local_values: &KeccakSpongeColumnsView

= local_values.borrow(); - let next_values: &[P; NUM_KECCAK_SPONGE_COLUMNS] = - vars.get_next_values().try_into().unwrap(); - let next_values: &KeccakSpongeColumnsView

= next_values.borrow(); - - // Check the range column: First value must be 0, last row - // must be 255, and intermediate rows must increment by 0 - // or 1. - let rc1 = local_values.range_counter; - let rc2 = next_values.range_counter; - yield_constr.constraint_first_row(rc1); - let incr = rc2 - rc1; - yield_constr.constraint_transition(incr * incr - incr); - let range_max = P::Scalar::from_canonical_u64((BYTE_RANGE_MAX - 1) as u64); - yield_constr.constraint_last_row(rc1 - range_max); - - // Each flag (full-input block, final block or implied dummy flag) must be boolean. - let is_full_input_block = local_values.is_full_input_block; - yield_constr.constraint(is_full_input_block * (is_full_input_block - P::ONES)); - - let is_final_block: P = local_values.is_final_input_len.iter().copied().sum(); - yield_constr.constraint(is_final_block * (is_final_block - P::ONES)); - - for &is_final_len in local_values.is_final_input_len.iter() { - yield_constr.constraint(is_final_len * (is_final_len - P::ONES)); - } - - // Ensure that full-input block and final block flags are not set to 1 at the same time. - yield_constr.constraint(is_final_block * is_full_input_block); - - // If this is the first row, the original sponge state should be 0 and already_absorbed_bytes = 0. - let already_absorbed_bytes = local_values.already_absorbed_bytes; - yield_constr.constraint_first_row(already_absorbed_bytes); - for &original_rate_elem in local_values.original_rate_u32s.iter() { - yield_constr.constraint_first_row(original_rate_elem); - } - for &original_capacity_elem in local_values.original_capacity_u32s.iter() { - yield_constr.constraint_first_row(original_capacity_elem); - } - - // If this is a final block, the next row's original sponge state should be 0 and already_absorbed_bytes = 0. - yield_constr.constraint_transition(is_final_block * next_values.already_absorbed_bytes); - for &original_rate_elem in next_values.original_rate_u32s.iter() { - yield_constr.constraint_transition(is_final_block * original_rate_elem); - } - for &original_capacity_elem in next_values.original_capacity_u32s.iter() { - yield_constr.constraint_transition(is_final_block * original_capacity_elem); - } - - // If this is a full-input block, the next row's address, time and len must match as well as its timestamp. - yield_constr.constraint_transition( - is_full_input_block * (local_values.context - next_values.context), - ); - yield_constr.constraint_transition( - is_full_input_block * (local_values.segment - next_values.segment), - ); - yield_constr - .constraint_transition(is_full_input_block * (local_values.virt - next_values.virt)); - yield_constr.constraint_transition( - is_full_input_block * (local_values.timestamp - next_values.timestamp), - ); - - // If this is a full-input block, the next row's "before" should match our "after" state. - for (current_bytes_after, next_before) in local_values - .updated_digest_state_bytes - .chunks_exact(4) - .zip(&next_values.original_rate_u32s[..KECCAK_DIGEST_U32S]) - { - let mut current_after = current_bytes_after[0]; - for i in 1..4 { - current_after += - current_bytes_after[i] * P::from(FE::from_canonical_usize(1 << (8 * i))); - } - yield_constr - .constraint_transition(is_full_input_block * (*next_before - current_after)); - } - for (¤t_after, &next_before) in local_values - .partial_updated_state_u32s - .iter() - .zip(next_values.original_rate_u32s[KECCAK_DIGEST_U32S..].iter()) - { - yield_constr.constraint_transition(is_full_input_block * (next_before - current_after)); - } - for (¤t_after, &next_before) in local_values - .partial_updated_state_u32s - .iter() - .skip(KECCAK_RATE_U32S - KECCAK_DIGEST_U32S) - .zip(next_values.original_capacity_u32s.iter()) - { - yield_constr.constraint_transition(is_full_input_block * (next_before - current_after)); - } - - // If this is a full-input block, the next row's already_absorbed_bytes should be ours plus `KECCAK_RATE_BYTES`. - yield_constr.constraint_transition( - is_full_input_block - * (already_absorbed_bytes + P::from(FE::from_canonical_usize(KECCAK_RATE_BYTES)) - - next_values.already_absorbed_bytes), - ); - - // A dummy row is always followed by another dummy row, so the prover can't put dummy rows "in between" to avoid the above checks. - let is_dummy = P::ONES - is_full_input_block - is_final_block; - let next_is_final_block: P = next_values.is_final_input_len.iter().copied().sum(); - yield_constr.constraint_transition( - is_dummy * (next_values.is_full_input_block + next_is_final_block), - ); - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let local_values: &[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS] = - vars.get_local_values().try_into().unwrap(); - let local_values: &KeccakSpongeColumnsView> = local_values.borrow(); - let next_values: &[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS] = - vars.get_next_values().try_into().unwrap(); - let next_values: &KeccakSpongeColumnsView> = next_values.borrow(); - - let one = builder.one_extension(); - - // Check the range column: First value must be 0, last row - // must be 255, and intermediate rows must increment by 0 - // or 1. - let rc1 = local_values.range_counter; - let rc2 = next_values.range_counter; - yield_constr.constraint_first_row(builder, rc1); - let incr = builder.sub_extension(rc2, rc1); - let t = builder.mul_sub_extension(incr, incr, incr); - yield_constr.constraint_transition(builder, t); - let range_max = - builder.constant_extension(F::Extension::from_canonical_usize(BYTE_RANGE_MAX - 1)); - let t = builder.sub_extension(rc1, range_max); - yield_constr.constraint_last_row(builder, t); - - // Each flag (full-input block, final block or implied dummy flag) must be boolean. - let is_full_input_block = local_values.is_full_input_block; - let constraint = builder.mul_sub_extension( - is_full_input_block, - is_full_input_block, - is_full_input_block, - ); - yield_constr.constraint(builder, constraint); - - let is_final_block = builder.add_many_extension(local_values.is_final_input_len); - let constraint = builder.mul_sub_extension(is_final_block, is_final_block, is_final_block); - yield_constr.constraint(builder, constraint); - - for &is_final_len in local_values.is_final_input_len.iter() { - let constraint = builder.mul_sub_extension(is_final_len, is_final_len, is_final_len); - yield_constr.constraint(builder, constraint); - } - - // Ensure that full-input block and final block flags are not set to 1 at the same time. - let constraint = builder.mul_extension(is_final_block, is_full_input_block); - yield_constr.constraint(builder, constraint); - - // If this is the first row, the original sponge state should be 0 and already_absorbed_bytes = 0. - let already_absorbed_bytes = local_values.already_absorbed_bytes; - yield_constr.constraint_first_row(builder, already_absorbed_bytes); - for &original_rate_elem in local_values.original_rate_u32s.iter() { - yield_constr.constraint_first_row(builder, original_rate_elem); - } - for &original_capacity_elem in local_values.original_capacity_u32s.iter() { - yield_constr.constraint_first_row(builder, original_capacity_elem); - } - - // If this is a final block, the next row's original sponge state should be 0 and already_absorbed_bytes = 0. - let constraint = builder.mul_extension(is_final_block, next_values.already_absorbed_bytes); - yield_constr.constraint_transition(builder, constraint); - for &original_rate_elem in next_values.original_rate_u32s.iter() { - let constraint = builder.mul_extension(is_final_block, original_rate_elem); - yield_constr.constraint_transition(builder, constraint); - } - for &original_capacity_elem in next_values.original_capacity_u32s.iter() { - let constraint = builder.mul_extension(is_final_block, original_capacity_elem); - yield_constr.constraint_transition(builder, constraint); - } - - // If this is a full-input block, the next row's address, time and len must match as well as its timestamp. - let context_diff = builder.sub_extension(local_values.context, next_values.context); - let constraint = builder.mul_extension(is_full_input_block, context_diff); - yield_constr.constraint_transition(builder, constraint); - - let segment_diff = builder.sub_extension(local_values.segment, next_values.segment); - let constraint = builder.mul_extension(is_full_input_block, segment_diff); - yield_constr.constraint_transition(builder, constraint); - - let virt_diff = builder.sub_extension(local_values.virt, next_values.virt); - let constraint = builder.mul_extension(is_full_input_block, virt_diff); - yield_constr.constraint_transition(builder, constraint); - - let timestamp_diff = builder.sub_extension(local_values.timestamp, next_values.timestamp); - let constraint = builder.mul_extension(is_full_input_block, timestamp_diff); - yield_constr.constraint_transition(builder, constraint); - - // If this is a full-input block, the next row's "before" should match our "after" state. - for (current_bytes_after, next_before) in local_values - .updated_digest_state_bytes - .chunks_exact(4) - .zip(&next_values.original_rate_u32s[..KECCAK_DIGEST_U32S]) - { - let mut current_after = current_bytes_after[0]; - for i in 1..4 { - current_after = builder.mul_const_add_extension( - F::from_canonical_usize(1 << (8 * i)), - current_bytes_after[i], - current_after, - ); - } - let diff = builder.sub_extension(*next_before, current_after); - let constraint = builder.mul_extension(is_full_input_block, diff); - yield_constr.constraint_transition(builder, constraint); - } - for (¤t_after, &next_before) in local_values - .partial_updated_state_u32s - .iter() - .zip(next_values.original_rate_u32s[KECCAK_DIGEST_U32S..].iter()) - { - let diff = builder.sub_extension(next_before, current_after); - let constraint = builder.mul_extension(is_full_input_block, diff); - yield_constr.constraint_transition(builder, constraint); - } - for (¤t_after, &next_before) in local_values - .partial_updated_state_u32s - .iter() - .skip(KECCAK_RATE_U32S - KECCAK_DIGEST_U32S) - .zip(next_values.original_capacity_u32s.iter()) - { - let diff = builder.sub_extension(next_before, current_after); - let constraint = builder.mul_extension(is_full_input_block, diff); - yield_constr.constraint_transition(builder, constraint); - } - - // If this is a full-input block, the next row's already_absorbed_bytes should be ours plus `KECCAK_RATE_BYTES`. - let absorbed_bytes = builder.add_const_extension( - already_absorbed_bytes, - F::from_canonical_usize(KECCAK_RATE_BYTES), - ); - let absorbed_diff = - builder.sub_extension(absorbed_bytes, next_values.already_absorbed_bytes); - let constraint = builder.mul_extension(is_full_input_block, absorbed_diff); - yield_constr.constraint_transition(builder, constraint); - - // A dummy row is always followed by another dummy row, so the prover can't put dummy rows "in between" to avoid the above checks. - let is_dummy = { - let tmp = builder.sub_extension(one, is_final_block); - builder.sub_extension(tmp, is_full_input_block) - }; - let next_is_final_block = builder.add_many_extension(next_values.is_final_input_len); - let constraint = { - let tmp = builder.add_extension(next_is_final_block, next_values.is_full_input_block); - builder.mul_extension(is_dummy, tmp) - }; - yield_constr.constraint_transition(builder, constraint); - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn lookups(&self) -> Vec> { - vec![Lookup { - columns: Column::singles(get_block_bytes_range()).collect(), - table_column: Column::single(RANGE_COUNTER), - frequencies_column: Column::single(RC_FREQUENCIES), - filter_columns: vec![None; KECCAK_RATE_BYTES], - }] - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use keccak_hash::keccak; - use plonky2::field::goldilocks_field::GoldilocksField; - use plonky2::field::types::PrimeField64; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use super::*; - use crate::memory::segments::Segment; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakSpongeStark; - - let stark = S::default(); - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakSpongeStark; - - let stark = S::default(); - test_stark_circuit_constraints::(stark) - } - - #[test] - fn test_generation() -> Result<()> { - const D: usize = 2; - type F = GoldilocksField; - type S = KeccakSpongeStark; - - let input = vec![1, 2, 3]; - let expected_output = keccak(&input); - - let op = KeccakSpongeOp { - base_address: MemoryAddress::new(0, Segment::Code, 0), - timestamp: 0, - input, - }; - let stark = S::default(); - let rows = stark.generate_rows_for_op(op); - assert_eq!(rows.len(), 1); - let last_row: &KeccakSpongeColumnsView = rows.last().unwrap().borrow(); - let output = last_row - .updated_digest_state_bytes - .iter() - .map(|x| x.to_canonical_u64() as u8) - .collect_vec(); - - assert_eq!(output, expected_output.0); - Ok(()) - } -} diff --git a/evm/src/keccak_sponge/mod.rs b/evm/src/keccak_sponge/mod.rs deleted file mode 100644 index 92b7f0c15e..0000000000 --- a/evm/src/keccak_sponge/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! The Keccak sponge STARK is used to hash a variable amount of data which is read from memory. -//! It connects to the memory STARK to read input data, and to the Keccak-f STARK to evaluate the -//! permutation at each absorption step. - -pub mod columns; -pub mod keccak_sponge_stark; diff --git a/evm/src/lib.rs b/evm/src/lib.rs deleted file mode 100644 index 5741c4419e..0000000000 --- a/evm/src/lib.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! An implementation of a Type 1 zk-EVM by Polygon Zero. -//! -//! Following the [zk-EVM classification of V. Buterin](https://vitalik.eth.limo/general/2022/08/04/zkevm.html), -//! the plonky2_evm crate aims at providing an efficient solution for the problem of generating cryptographic -//! proofs of Ethereum-like transactions with *full Ethereum capability*. -//! -//! To this end, the plonky2 zk-EVM is tailored for an AIR-based STARK system satisfying degree 3 constraints, -//! with support for recursive aggregation leveraging plonky2 circuits with FRI-based plonkish arithmetization. -//! These circuits require a one-time, offline preprocessing phase. -//! See the [`fixed_recursive_verifier`] module for more details on how this works. -//! These preprocessed circuits are gathered within the [`AllRecursiveCircuits`] prover state, -//! and can be generated as such: -//! -//! ```ignore -//! // Specify the base field to use. -//! type F = GoldilocksField; -//! // Specify the extension degree to use. -//! const D: usize = 2; -//! // Specify the recursive configuration to use, here leveraging Poseidon hash -//! // over the Goldilocks field both natively and in-circuit. -//! type C = PoseidonGoldilocksConfig; -//! -//! let all_stark = AllStark::::default(); -//! let config = StarkConfig::standard_fast_config(); -//! -//! // Generate all the recursive circuits needed to generate succinct proofs for blocks. -//! // The ranges correspond to the supported table sizes for each individual STARK component. -//! let prover_state = AllRecursiveCircuits::::new( -//! &all_stark, -//! &[16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30], -//! &config, -//! ); -//! ``` -//! -//! # Inputs type -//! -//! Transactions need to be processed into an Intermediary Representation (IR) format for the prover -//! to be able to generate proofs of valid state transition. This involves passing the encoded transaction, -//! the header of the block in which it was included, some information on the state prior execution -//! of this transaction, etc. -//! This intermediary representation is called [`GenerationInputs`]. -//! -//! -//! # Generating succinct proofs -//! -//! ## Transaction proofs -//! -//! To generate a proof for a transaction, given its [`GenerationInputs`] and an [`AllRecursiveCircuits`] -//! prover state, one can simply call the [prove_root](AllRecursiveCircuits::prove_root) method. -//! -//! ```ignore -//! let mut timing = TimingTree::new("prove", log::Level::Debug); -//! let kill_signal = None; // Useful only with distributed proving to kill hanging jobs. -//! let (proof, public_values) = -//! prover_state.prove_root(all_stark, config, inputs, &mut timing, kill_signal); -//! ``` -//! -//! This outputs a transaction proof and its associated public values. These are necessary during the -//! aggregation levels (see below). If one were to miss the public values, they are also retrievable directly -//! from the proof's encoded public inputs, as such: -//! -//! ```ignore -//! let public_values = PublicValues::from_public_inputs(&proof.public_inputs); -//! ``` -//! -//! ## Aggregation proofs -//! -//! Because the plonky2 zkEVM generates proofs on a transaction basis, we then need to aggregate them for succinct -//! verification. This is done in a binary tree fashion, where each inner node proof verifies two children proofs, -//! through the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method. -//! Note that the tree does *not* need to be complete, as this aggregation process can take as inputs both regular -//! transaction proofs and aggregation proofs. We only need to specify for each child if it is an aggregation proof -//! or a regular one. -//! -//! ```ignore -//! let (proof_1, pv_1) = -//! prover_state.prove_root(all_stark, config, inputs_1, &mut timing, None); -//! let (proof_2, pv_2) = -//! prover_state.prove_root(all_stark, config, inputs_2, &mut timing, None); -//! let (proof_3, pv_3) = -//! prover_state.prove_root(all_stark, config, inputs_3, &mut timing, None); -//! -//! // Now aggregate proofs for txn 1 and 2. -//! let (agg_proof_1_2, pv_1_2) = -//! prover_state.prove_aggregation(false, proof_1, pv_1, false, proof_2, pv_2); -//! -//! // Now aggregate the newly generated aggregation proof with the last regular txn proof. -//! let (agg_proof_1_3, pv_1_3) = -//! prover_state.prove_aggregation(true, agg_proof_1_2, pv_1_2, false, proof_3, pv_3); -//! ``` -//! -//! **Note**: The proofs provided to the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method *MUST* have contiguous states. -//! Trying to combine `proof_1` and `proof_3` from the example above would fail. -//! -//! ## Block proofs -//! -//! Once all transactions of a block have been proven and we are left with a single aggregation proof and its public values, -//! we can then wrap it into a final block proof, attesting validity of the entire block. -//! This [prove_block](AllRecursiveCircuits::prove_block) method accepts an optional previous block proof as argument, -//! which will then try combining the previously proven block with the current one, generating a validity proof for both. -//! Applying this process from genesis would yield a single proof attesting correctness of the entire chain. -//! -//! ```ignore -//! let previous_block_proof = { ... }; -//! let (block_proof, block_public_values) = -//! prover_state.prove_block(Some(&previous_block_proof), &agg_proof, agg_pv)?; -//! ``` -//! -//! ### Checkpoint heights -//! -//! The process of always providing a previous block proof when generating a proof for the current block may yield some -//! undesirable issues. For this reason, the plonky2 zk-EVM supports checkpoint heights. At given block heights, -//! the prover does not have to pass a previous block proof. This would in practice correspond to block heights at which -//! a proof has been generated and sent to L1 for settlement. -//! -//! The only requirement when generating a block proof without passing a previous one as argument is to have the -//! `checkpoint_state_trie_root` metadata in the `PublicValues` of the final aggregation proof be matching the state -//! trie before applying all the included transactions. If this condition is not met, the prover will fail to generate -//! a valid proof. -//! -//! -//! ```ignore -//! let (block_proof, block_public_values) = -//! prover_state.prove_block(None, &agg_proof, agg_pv)?; -//! ``` -//! -//! # Prover state serialization -//! -//! Because the recursive circuits only need to be generated once, they can be saved to disk once the preprocessing phase -//! completed successfully, and deserialized on-demand. -//! The plonky2 zk-EVM provides serialization methods to convert the entire prover state to a vector of bytes, and vice-versa. -//! This requires the use of custom serializers for gates and generators for proper recursive circuit encoding. This crate provides -//! default serializers supporting all custom gates and associated generators defined within the [`plonky2`] crate. -//! -//! ```ignore -//! let prover_state = AllRecursiveCircuits::::new(...); -//! -//! // Default serializers -//! let gate_serializer = DefaultGateSerializer; -//! let generator_serializer = DefaultGeneratorSerializer:: { -//! _phantom: PhantomData::, -//! }; -//! -//! // Serialize the prover state to a sequence of bytes -//! let bytes = prover_state.to_bytes(false, &gate_serializer, &generator_serializer).unwrap(); -//! -//! // Deserialize the bytes into a prover state -//! let recovered_prover_state = AllRecursiveCircuits::::from_bytes( -//! &all_circuits_bytes, -//! false, -//! &gate_serializer, -//! &generator_serializer, -//! ).unwrap(); -//! -//! assert_eq!(prover_state, recovered_prover_state); -//! ``` -//! -//! Note that an entire prover state built with wide ranges may be particularly large (up to ~25 GB), hence serialization methods, -//! while faster than doing another preprocessing, may take some non-negligible time. - -#![cfg_attr(docsrs, feature(doc_cfg))] -#![allow(clippy::needless_range_loop)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::field_reassign_with_default)] -#![allow(unused)] -#![feature(let_chains)] - -// Individual STARK processing units -pub mod arithmetic; -pub mod byte_packing; -pub mod cpu; -pub mod keccak; -pub mod keccak_sponge; -pub mod logic; -pub mod memory; - -// Proving system components -pub mod all_stark; -pub mod fixed_recursive_verifier; -mod get_challenges; -pub mod proof; -pub mod prover; -pub mod recursive_verifier; -pub mod verifier; - -// Witness generation -pub mod generation; -pub mod witness; - -// Utility modules -pub mod curve_pairings; -pub mod extension_tower; -pub mod util; - -use eth_trie_utils::partial_trie::HashedPartialTrie; -// Set up Jemalloc -#[cfg(not(target_env = "msvc"))] -use jemallocator::Jemalloc; - -#[cfg(not(target_env = "msvc"))] -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; - -// Public definitions and re-exports - -pub type Node = eth_trie_utils::partial_trie::Node; - -pub use all_stark::AllStark; -pub use fixed_recursive_verifier::AllRecursiveCircuits; -pub use generation::GenerationInputs; -pub use starky::config::StarkConfig; diff --git a/evm/src/logic.rs b/evm/src/logic.rs deleted file mode 100644 index d07a6e3d18..0000000000 --- a/evm/src/logic.rs +++ /dev/null @@ -1,398 +0,0 @@ -use core::marker::PhantomData; - -use ethereum_types::U256; -use itertools::izip; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use plonky2_util::ceil_div_usize; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter}; -use starky::stark::Stark; -use starky::util::trace_rows_to_poly_values; - -use crate::all_stark::EvmStarkFrame; -use crate::logic::columns::NUM_COLUMNS; -use crate::util::{limb_from_bits_le, limb_from_bits_le_recursive}; - -/// Total number of bits per input/output. -const VAL_BITS: usize = 256; -/// Number of bits stored per field element. Ensure that this fits; it is not checked. -pub(crate) const PACKED_LIMB_BITS: usize = 32; -/// Number of field elements needed to store each input/output at the specified packing. -const PACKED_LEN: usize = ceil_div_usize(VAL_BITS, PACKED_LIMB_BITS); - -/// `LogicStark` columns. -pub(crate) mod columns { - use core::cmp::min; - use core::ops::Range; - - use super::{PACKED_LEN, PACKED_LIMB_BITS, VAL_BITS}; - - /// 1 if this is an AND operation, 0 otherwise. - pub(crate) const IS_AND: usize = 0; - /// 1 if this is an OR operation, 0 otherwise. - pub(crate) const IS_OR: usize = IS_AND + 1; - /// 1 if this is a XOR operation, 0 otherwise. - pub(crate) const IS_XOR: usize = IS_OR + 1; - /// First input, decomposed into bits. - pub(crate) const INPUT0: Range = (IS_XOR + 1)..(IS_XOR + 1) + VAL_BITS; - /// Second input, decomposed into bits. - pub(crate) const INPUT1: Range = INPUT0.end..INPUT0.end + VAL_BITS; - /// The result is packed in limbs of `PACKED_LIMB_BITS` bits. - pub(crate) const RESULT: Range = INPUT1.end..INPUT1.end + PACKED_LEN; - - /// Returns the column range for each 32 bit chunk in the input. - pub(crate) fn limb_bit_cols_for_input( - input_bits: Range, - ) -> impl Iterator> { - (0..PACKED_LEN).map(move |i| { - let start = input_bits.start + i * PACKED_LIMB_BITS; - let end = min(start + PACKED_LIMB_BITS, input_bits.end); - start..end - }) - } - - /// Number of columns in `LogicStark`. - pub(crate) const NUM_COLUMNS: usize = RESULT.end; -} - -/// Creates the vector of `Columns` corresponding to the opcode, the two inputs and the output of the logic operation. -pub(crate) fn ctl_data() -> Vec> { - // We scale each filter flag with the associated opcode value. - // If a logic operation is happening on the CPU side, the CTL - // will enforce that the reconstructed opcode value from the - // opcode bits matches. - let mut res = vec![Column::linear_combination([ - (columns::IS_AND, F::from_canonical_u8(0x16)), - (columns::IS_OR, F::from_canonical_u8(0x17)), - (columns::IS_XOR, F::from_canonical_u8(0x18)), - ])]; - res.extend(columns::limb_bit_cols_for_input(columns::INPUT0).map(Column::le_bits)); - res.extend(columns::limb_bit_cols_for_input(columns::INPUT1).map(Column::le_bits)); - res.extend(columns::RESULT.map(Column::single)); - res -} - -/// CTL filter for logic operations. -pub(crate) fn ctl_filter() -> Filter { - Filter::new_simple(Column::sum([ - columns::IS_AND, - columns::IS_OR, - columns::IS_XOR, - ])) -} - -/// Structure representing the Logic STARK, which computes all logic operations. -#[derive(Copy, Clone, Default)] -pub(crate) struct LogicStark { - pub f: PhantomData, -} - -/// Logic operations. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub(crate) enum Op { - And, - Or, - Xor, -} - -impl Op { - /// Returns the output of the current Logic operation. - pub(crate) fn result(&self, a: U256, b: U256) -> U256 { - match self { - Op::And => a & b, - Op::Or => a | b, - Op::Xor => a ^ b, - } - } -} - -/// A logic operation over `U256`` words. It contains an operator, -/// either `AND`, `OR` or `XOR`, two inputs and its expected result. -#[derive(Debug)] -pub(crate) struct Operation { - operator: Op, - input0: U256, - input1: U256, - pub(crate) result: U256, -} - -impl Operation { - /// Computes the expected result of an operator with the two provided inputs, - /// and returns the associated logic `Operation`. - pub(crate) fn new(operator: Op, input0: U256, input1: U256) -> Self { - let result = operator.result(input0, input1); - Operation { - operator, - input0, - input1, - result, - } - } - - /// Given an `Operation`, fills a row with the corresponding flag, inputs and output. - fn into_row(self) -> [F; NUM_COLUMNS] { - let Operation { - operator, - input0, - input1, - result, - } = self; - let mut row = [F::ZERO; NUM_COLUMNS]; - row[match operator { - Op::And => columns::IS_AND, - Op::Or => columns::IS_OR, - Op::Xor => columns::IS_XOR, - }] = F::ONE; - for i in 0..256 { - row[columns::INPUT0.start + i] = F::from_bool(input0.bit(i)); - row[columns::INPUT1.start + i] = F::from_bool(input1.bit(i)); - } - let result_limbs: &[u64] = result.as_ref(); - for (i, &limb) in result_limbs.iter().enumerate() { - row[columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32); - row[columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - row - } -} - -impl LogicStark { - /// Generates the trace polynomials for `LogicStark`. - pub(crate) fn generate_trace( - &self, - operations: Vec, - min_rows: usize, - timing: &mut TimingTree, - ) -> Vec> { - // First, turn all provided operations into rows in `LogicStark`, and pad if necessary. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_rows(operations, min_rows) - ); - // Generate the trace polynomials from the trace values. - let trace_polys = timed!( - timing, - "convert to PolynomialValues", - trace_rows_to_poly_values(trace_rows) - ); - trace_polys - } - - /// Generate the `LogicStark` traces based on the provided vector of operations. - /// The trace is padded to a power of two with all-zero rows. - fn generate_trace_rows( - &self, - operations: Vec, - min_rows: usize, - ) -> Vec<[F; NUM_COLUMNS]> { - let len = operations.len(); - let padded_len = len.max(min_rows).next_power_of_two(); - - let mut rows = Vec::with_capacity(padded_len); - for op in operations { - rows.push(op.into_row()); - } - - // Pad to a power of two. - for _ in len..padded_len { - rows.push([F::ZERO; NUM_COLUMNS]); - } - - rows - } -} - -impl, const D: usize> Stark for LogicStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let lv = vars.get_local_values(); - - let is_and = lv[columns::IS_AND]; - let is_or = lv[columns::IS_OR]; - let is_xor = lv[columns::IS_XOR]; - - // Flags must be boolean. - for &flag in &[is_and, is_or, is_xor] { - yield_constr.constraint(flag * (flag - P::ONES)); - } - - // Only a single flag must be activated at once. - let all_flags = is_and + is_or + is_xor; - yield_constr.constraint(all_flags * (all_flags - P::ONES)); - - // The result will be `in0 OP in1 = sum_coeff * (in0 + in1) + and_coeff * (in0 AND in1)`. - // `AND => sum_coeff = 0, and_coeff = 1` - // `OR => sum_coeff = 1, and_coeff = -1` - // `XOR => sum_coeff = 1, and_coeff = -2` - let sum_coeff = is_or + is_xor; - let and_coeff = is_and - is_or - is_xor * FE::TWO; - - // Ensure that all bits are indeed bits. - for input_bits_cols in [columns::INPUT0, columns::INPUT1] { - for i in input_bits_cols { - let bit = lv[i]; - yield_constr.constraint(bit * (bit - P::ONES)); - } - } - - // Form the result - for (result_col, x_bits_cols, y_bits_cols) in izip!( - columns::RESULT, - columns::limb_bit_cols_for_input(columns::INPUT0), - columns::limb_bit_cols_for_input(columns::INPUT1), - ) { - let x: P = limb_from_bits_le(x_bits_cols.clone().map(|col| lv[col])); - let y: P = limb_from_bits_le(y_bits_cols.clone().map(|col| lv[col])); - - let x_bits = x_bits_cols.map(|i| lv[i]); - let y_bits = y_bits_cols.map(|i| lv[i]); - - let x_land_y: P = izip!(0.., x_bits, y_bits) - .map(|(i, x_bit, y_bit)| x_bit * y_bit * FE::from_canonical_u64(1 << i)) - .sum(); - let x_op_y = sum_coeff * (x + y) + and_coeff * x_land_y; - - yield_constr.constraint(lv[result_col] - x_op_y); - } - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let lv = vars.get_local_values(); - - let is_and = lv[columns::IS_AND]; - let is_or = lv[columns::IS_OR]; - let is_xor = lv[columns::IS_XOR]; - - // Flags must be boolean. - for &flag in &[is_and, is_or, is_xor] { - let constraint = builder.mul_sub_extension(flag, flag, flag); - yield_constr.constraint(builder, constraint); - } - - // Only a single flag must be activated at once. - let all_flags = builder.add_many_extension([is_and, is_or, is_xor]); - let constraint = builder.mul_sub_extension(all_flags, all_flags, all_flags); - yield_constr.constraint(builder, constraint); - - // The result will be `in0 OP in1 = sum_coeff * (in0 + in1) + and_coeff * (in0 AND in1)`. - // `AND => sum_coeff = 0, and_coeff = 1` - // `OR => sum_coeff = 1, and_coeff = -1` - // `XOR => sum_coeff = 1, and_coeff = -2` - let sum_coeff = builder.add_extension(is_or, is_xor); - let and_coeff = { - let and_coeff = builder.sub_extension(is_and, is_or); - builder.mul_const_add_extension(-F::TWO, is_xor, and_coeff) - }; - - // Ensure that all bits are indeed bits. - for input_bits_cols in [columns::INPUT0, columns::INPUT1] { - for i in input_bits_cols { - let bit = lv[i]; - let constr = builder.mul_sub_extension(bit, bit, bit); - yield_constr.constraint(builder, constr); - } - } - - // Form the result - for (result_col, x_bits_cols, y_bits_cols) in izip!( - columns::RESULT, - columns::limb_bit_cols_for_input(columns::INPUT0), - columns::limb_bit_cols_for_input(columns::INPUT1), - ) { - let x = limb_from_bits_le_recursive(builder, x_bits_cols.clone().map(|i| lv[i])); - let y = limb_from_bits_le_recursive(builder, y_bits_cols.clone().map(|i| lv[i])); - let x_bits = x_bits_cols.map(|i| lv[i]); - let y_bits = y_bits_cols.map(|i| lv[i]); - - let x_land_y = izip!(0usize.., x_bits, y_bits).fold( - builder.zero_extension(), - |acc, (i, x_bit, y_bit)| { - builder.arithmetic_extension( - F::from_canonical_u64(1 << i), - F::ONE, - x_bit, - y_bit, - acc, - ) - }, - ); - let x_op_y = { - let x_op_y = builder.mul_extension(sum_coeff, x); - let x_op_y = builder.mul_add_extension(sum_coeff, y, x_op_y); - builder.mul_add_extension(and_coeff, x_land_y, x_op_y) - }; - let constr = builder.sub_extension(lv[result_col], x_op_y); - yield_constr.constraint(builder, constr); - } - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use crate::logic::LogicStark; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = LogicStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = LogicStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } -} diff --git a/evm/src/memory/columns.rs b/evm/src/memory/columns.rs deleted file mode 100644 index 2010bf33ec..0000000000 --- a/evm/src/memory/columns.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Memory registers. - -use crate::memory::VALUE_LIMBS; - -// Columns for memory operations, ordered by (addr, timestamp). -/// 1 if this is an actual memory operation, or 0 if it's a padding row. -pub(crate) const FILTER: usize = 0; -/// Each memory operation is associated to a unique timestamp. -/// For a given memory operation `op_i`, its timestamp is computed as `C * N + i` -/// where `C` is the CPU clock at that time, `N` is the number of general memory channels, -/// and `i` is the index of the memory channel at which the memory operation is performed. -pub(crate) const TIMESTAMP: usize = FILTER + 1; -/// 1 if this is a read operation, 0 if it is a write one. -pub(crate) const IS_READ: usize = TIMESTAMP + 1; -/// The execution context of this address. -pub(crate) const ADDR_CONTEXT: usize = IS_READ + 1; -/// The segment section of this address. -pub(crate) const ADDR_SEGMENT: usize = ADDR_CONTEXT + 1; -/// The virtual address within the given context and segment. -pub(crate) const ADDR_VIRTUAL: usize = ADDR_SEGMENT + 1; - -// Eight 32-bit limbs hold a total of 256 bits. -// If a value represents an integer, it is little-endian encoded. -const VALUE_START: usize = ADDR_VIRTUAL + 1; -pub(crate) const fn value_limb(i: usize) -> usize { - debug_assert!(i < VALUE_LIMBS); - VALUE_START + i -} - -// Flags to indicate whether this part of the address differs from the next row, -// and the previous parts do not differ. -// That is, e.g., `SEGMENT_FIRST_CHANGE` is `F::ONE` iff `ADDR_CONTEXT` is the same in this -// row and the next, but `ADDR_SEGMENT` is not. -pub(crate) const CONTEXT_FIRST_CHANGE: usize = VALUE_START + VALUE_LIMBS; -pub(crate) const SEGMENT_FIRST_CHANGE: usize = CONTEXT_FIRST_CHANGE + 1; -pub(crate) const VIRTUAL_FIRST_CHANGE: usize = SEGMENT_FIRST_CHANGE + 1; - -// Used to lower the degree of the zero-initializing constraints. -// Contains `next_segment * addr_changed * next_is_read`. -pub(crate) const INITIALIZE_AUX: usize = VIRTUAL_FIRST_CHANGE + 1; - -// We use a range check to enforce the ordering. -pub(crate) const RANGE_CHECK: usize = INITIALIZE_AUX + 1; -/// The counter column (used for the range check) starts from 0 and increments. -pub(crate) const COUNTER: usize = RANGE_CHECK + 1; -/// The frequencies column used in logUp. -pub(crate) const FREQUENCIES: usize = COUNTER + 1; - -pub(crate) const NUM_COLUMNS: usize = FREQUENCIES + 1; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs deleted file mode 100644 index d8a818ff83..0000000000 --- a/evm/src/memory/memory_stark.rs +++ /dev/null @@ -1,612 +0,0 @@ -use core::marker::PhantomData; - -use ethereum_types::U256; -use itertools::Itertools; -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use plonky2::util::transpose; -use plonky2_maybe_rayon::*; -use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use starky::evaluation_frame::StarkEvaluationFrame; -use starky::lookup::{Column, Filter, Lookup}; -use starky::stark::Stark; - -use super::segments::Segment; -use crate::all_stark::EvmStarkFrame; -use crate::memory::columns::{ - value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, FILTER, - FREQUENCIES, INITIALIZE_AUX, IS_READ, NUM_COLUMNS, RANGE_CHECK, SEGMENT_FIRST_CHANGE, - TIMESTAMP, VIRTUAL_FIRST_CHANGE, -}; -use crate::memory::VALUE_LIMBS; -use crate::witness::memory::MemoryOpKind::Read; -use crate::witness::memory::{MemoryAddress, MemoryOp}; - -/// Creates the vector of `Columns` corresponding to: -/// - the memory operation type, -/// - the address in memory of the element being read/written, -/// - the value being read/written, -/// - the timestamp at which the element is read/written. -pub(crate) fn ctl_data() -> Vec> { - let mut res = - Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]).collect_vec(); - res.extend(Column::singles((0..8).map(value_limb))); - res.push(Column::single(TIMESTAMP)); - res -} - -/// CTL filter for memory operations. -pub(crate) fn ctl_filter() -> Filter { - Filter::new_simple(Column::single(FILTER)) -} - -#[derive(Copy, Clone, Default)] -pub(crate) struct MemoryStark { - pub(crate) f: PhantomData, -} - -impl MemoryOp { - /// Generate a row for a given memory operation. Note that this does not generate columns which - /// depend on the next operation, such as `CONTEXT_FIRST_CHANGE`; those are generated later. - /// It also does not generate columns such as `COUNTER`, which are generated later, after the - /// trace has been transposed into column-major form. - fn into_row(self) -> [F; NUM_COLUMNS] { - let mut row = [F::ZERO; NUM_COLUMNS]; - row[FILTER] = F::from_bool(self.filter); - row[TIMESTAMP] = F::from_canonical_usize(self.timestamp); - row[IS_READ] = F::from_bool(self.kind == Read); - let MemoryAddress { - context, - segment, - virt, - } = self.address; - row[ADDR_CONTEXT] = F::from_canonical_usize(context); - row[ADDR_SEGMENT] = F::from_canonical_usize(segment); - row[ADDR_VIRTUAL] = F::from_canonical_usize(virt); - for j in 0..VALUE_LIMBS { - row[value_limb(j)] = F::from_canonical_u32((self.value >> (j * 32)).low_u32()); - } - row - } -} - -/// Generates the `_FIRST_CHANGE` columns and the `RANGE_CHECK` column in the trace. -pub(crate) fn generate_first_change_flags_and_rc( - trace_rows: &mut [[F; NUM_COLUMNS]], -) { - let num_ops = trace_rows.len(); - for idx in 0..num_ops - 1 { - let row = trace_rows[idx].as_slice(); - let next_row = trace_rows[idx + 1].as_slice(); - - let context = row[ADDR_CONTEXT]; - let segment = row[ADDR_SEGMENT]; - let virt = row[ADDR_VIRTUAL]; - let timestamp = row[TIMESTAMP]; - let next_context = next_row[ADDR_CONTEXT]; - let next_segment = next_row[ADDR_SEGMENT]; - let next_virt = next_row[ADDR_VIRTUAL]; - let next_timestamp = next_row[TIMESTAMP]; - let next_is_read = next_row[IS_READ]; - - let context_changed = context != next_context; - let segment_changed = segment != next_segment; - let virtual_changed = virt != next_virt; - - let context_first_change = context_changed; - let segment_first_change = segment_changed && !context_first_change; - let virtual_first_change = - virtual_changed && !segment_first_change && !context_first_change; - - let row = trace_rows[idx].as_mut_slice(); - row[CONTEXT_FIRST_CHANGE] = F::from_bool(context_first_change); - row[SEGMENT_FIRST_CHANGE] = F::from_bool(segment_first_change); - row[VIRTUAL_FIRST_CHANGE] = F::from_bool(virtual_first_change); - - row[RANGE_CHECK] = if context_first_change { - next_context - context - F::ONE - } else if segment_first_change { - next_segment - segment - F::ONE - } else if virtual_first_change { - next_virt - virt - F::ONE - } else { - next_timestamp - timestamp - }; - - assert!( - row[RANGE_CHECK].to_canonical_u64() < num_ops as u64, - "Range check of {} is too large. Bug in fill_gaps?", - row[RANGE_CHECK] - ); - - let address_changed = - row[CONTEXT_FIRST_CHANGE] + row[SEGMENT_FIRST_CHANGE] + row[VIRTUAL_FIRST_CHANGE]; - row[INITIALIZE_AUX] = next_segment * address_changed * next_is_read; - } -} - -impl, const D: usize> MemoryStark { - /// Generate most of the trace rows. Excludes a few columns like `COUNTER`, which are generated - /// later, after transposing to column-major form. - fn generate_trace_row_major(&self, mut memory_ops: Vec) -> Vec<[F; NUM_COLUMNS]> { - // fill_gaps expects an ordered list of operations. - memory_ops.sort_by_key(MemoryOp::sorting_key); - Self::fill_gaps(&mut memory_ops); - - Self::pad_memory_ops(&mut memory_ops); - - // fill_gaps may have added operations at the end which break the order, so sort again. - memory_ops.sort_by_key(MemoryOp::sorting_key); - - let mut trace_rows = memory_ops - .into_par_iter() - .map(|op| op.into_row()) - .collect::>(); - generate_first_change_flags_and_rc(trace_rows.as_mut_slice()); - trace_rows - } - - /// Generates the `COUNTER`, `RANGE_CHECK` and `FREQUENCIES` columns, given a - /// trace in column-major form. - fn generate_trace_col_major(trace_col_vecs: &mut [Vec]) { - let height = trace_col_vecs[0].len(); - trace_col_vecs[COUNTER] = (0..height).map(|i| F::from_canonical_usize(i)).collect(); - - for i in 0..height { - let x_rc = trace_col_vecs[RANGE_CHECK][i].to_canonical_u64() as usize; - trace_col_vecs[FREQUENCIES][x_rc] += F::ONE; - if (trace_col_vecs[CONTEXT_FIRST_CHANGE][i] == F::ONE) - || (trace_col_vecs[SEGMENT_FIRST_CHANGE][i] == F::ONE) - { - // CONTEXT_FIRST_CHANGE and SEGMENT_FIRST_CHANGE should be 0 at the last row, so the index - // should never be out of bounds. - let x_fo = trace_col_vecs[ADDR_VIRTUAL][i + 1].to_canonical_u64() as usize; - trace_col_vecs[FREQUENCIES][x_fo] += F::ONE; - } - } - } - - /// This memory STARK orders rows by `(context, segment, virt, timestamp)`. To enforce the - /// ordering, it range checks the delta of the first field that changed. - /// - /// This method adds some dummy operations to ensure that none of these range checks will be too - /// large, i.e. that they will all be smaller than the number of rows, allowing them to be - /// checked easily with a single lookup. - /// - /// For example, say there are 32 memory operations, and a particular address is accessed at - /// timestamps 20 and 100. 80 would fail the range check, so this method would add two dummy - /// reads to the same address, say at timestamps 50 and 80. - fn fill_gaps(memory_ops: &mut Vec) { - let max_rc = memory_ops.len().next_power_of_two() - 1; - for (mut curr, mut next) in memory_ops.clone().into_iter().tuple_windows() { - if curr.address.context != next.address.context - || curr.address.segment != next.address.segment - { - // We won't bother to check if there's a large context gap, because there can't be - // more than 500 contexts or so, as explained here: - // https://notes.ethereum.org/@vbuterin/proposals_to_adjust_memory_gas_costs - // Similarly, the number of possible segments is a small constant, so any gap must - // be small. max_rc will always be much larger, as just bootloading the kernel will - // trigger thousands of memory operations. - // However, we do check that the first address accessed is range-checkable. If not, - // we could start at a negative address and cheat. - while next.address.virt > max_rc { - let mut dummy_address = next.address; - dummy_address.virt -= max_rc; - let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0, U256::zero()); - memory_ops.push(dummy_read); - next = dummy_read; - } - } else if curr.address.virt != next.address.virt { - while next.address.virt - curr.address.virt - 1 > max_rc { - let mut dummy_address = curr.address; - dummy_address.virt += max_rc + 1; - let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0, U256::zero()); - memory_ops.push(dummy_read); - curr = dummy_read; - } - } else { - while next.timestamp - curr.timestamp > max_rc { - let dummy_read = - MemoryOp::new_dummy_read(curr.address, curr.timestamp + max_rc, curr.value); - memory_ops.push(dummy_read); - curr = dummy_read; - } - } - } - } - - fn pad_memory_ops(memory_ops: &mut Vec) { - let last_op = *memory_ops.last().expect("No memory ops?"); - - // We essentially repeat the last operation until our operation list has the desired size, - // with a few changes: - // - We change its filter to 0 to indicate that this is a dummy operation. - // - We make sure it's a read, since dummy operations must be reads. - let padding_op = MemoryOp { - filter: false, - kind: Read, - ..last_op - }; - - let num_ops = memory_ops.len(); - let num_ops_padded = num_ops.next_power_of_two(); - for _ in num_ops..num_ops_padded { - memory_ops.push(padding_op); - } - } - - pub(crate) fn generate_trace( - &self, - memory_ops: Vec, - timing: &mut TimingTree, - ) -> Vec> { - // Generate most of the trace in row-major form. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_row_major(memory_ops) - ); - let trace_row_vecs: Vec<_> = trace_rows.into_iter().map(|row| row.to_vec()).collect(); - - // Transpose to column-major form. - let mut trace_col_vecs = transpose(&trace_row_vecs); - - // A few final generation steps, which work better in column-major form. - Self::generate_trace_col_major(&mut trace_col_vecs); - - trace_col_vecs - .into_iter() - .map(|column| PolynomialValues::new(column)) - .collect() - } -} - -impl, const D: usize> Stark for MemoryStark { - type EvaluationFrame = EvmStarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let one = P::from(FE::ONE); - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - let timestamp = local_values[TIMESTAMP]; - let addr_context = local_values[ADDR_CONTEXT]; - let addr_segment = local_values[ADDR_SEGMENT]; - let addr_virtual = local_values[ADDR_VIRTUAL]; - let value_limbs: Vec<_> = (0..8).map(|i| local_values[value_limb(i)]).collect(); - - let next_timestamp = next_values[TIMESTAMP]; - let next_is_read = next_values[IS_READ]; - let next_addr_context = next_values[ADDR_CONTEXT]; - let next_addr_segment = next_values[ADDR_SEGMENT]; - let next_addr_virtual = next_values[ADDR_VIRTUAL]; - let next_values_limbs: Vec<_> = (0..8).map(|i| next_values[value_limb(i)]).collect(); - - // The filter must be 0 or 1. - let filter = local_values[FILTER]; - yield_constr.constraint(filter * (filter - P::ONES)); - - // IS_READ must be 0 or 1. - // This is implied by the MemoryStark CTL, where corresponding values are either - // hardcoded to 0/1, or boolean-constrained in their respective STARK modules. - - // If this is a dummy row (filter is off), it must be a read. This means the prover can - // insert reads which never appear in the CPU trace (which are harmless), but not writes. - let is_dummy = P::ONES - filter; - let is_write = P::ONES - local_values[IS_READ]; - yield_constr.constraint(is_dummy * is_write); - - let context_first_change = local_values[CONTEXT_FIRST_CHANGE]; - let segment_first_change = local_values[SEGMENT_FIRST_CHANGE]; - let virtual_first_change = local_values[VIRTUAL_FIRST_CHANGE]; - let address_unchanged = - one - context_first_change - segment_first_change - virtual_first_change; - - let range_check = local_values[RANGE_CHECK]; - - let not_context_first_change = one - context_first_change; - let not_segment_first_change = one - segment_first_change; - let not_virtual_first_change = one - virtual_first_change; - let not_address_unchanged = one - address_unchanged; - - // First set of ordering constraint: first_change flags are boolean. - yield_constr.constraint(context_first_change * not_context_first_change); - yield_constr.constraint(segment_first_change * not_segment_first_change); - yield_constr.constraint(virtual_first_change * not_virtual_first_change); - yield_constr.constraint(address_unchanged * not_address_unchanged); - - // Second set of ordering constraints: no change before the column corresponding to the nonzero first_change flag. - yield_constr - .constraint_transition(segment_first_change * (next_addr_context - addr_context)); - yield_constr - .constraint_transition(virtual_first_change * (next_addr_context - addr_context)); - yield_constr - .constraint_transition(virtual_first_change * (next_addr_segment - addr_segment)); - yield_constr.constraint_transition(address_unchanged * (next_addr_context - addr_context)); - yield_constr.constraint_transition(address_unchanged * (next_addr_segment - addr_segment)); - yield_constr.constraint_transition(address_unchanged * (next_addr_virtual - addr_virtual)); - - // Third set of ordering constraints: range-check difference in the column that should be increasing. - let computed_range_check = context_first_change * (next_addr_context - addr_context - one) - + segment_first_change * (next_addr_segment - addr_segment - one) - + virtual_first_change * (next_addr_virtual - addr_virtual - one) - + address_unchanged * (next_timestamp - timestamp); - yield_constr.constraint_transition(range_check - computed_range_check); - - // Validate initialize_aux. It contains next_segment * addr_changed * next_is_read. - let initialize_aux = local_values[INITIALIZE_AUX]; - yield_constr.constraint_transition( - initialize_aux - next_addr_segment * not_address_unchanged * next_is_read, - ); - - for i in 0..8 { - // Enumerate purportedly-ordered log. - yield_constr.constraint_transition( - next_is_read * address_unchanged * (next_values_limbs[i] - value_limbs[i]), - ); - // By default, memory is initialized with 0. This means that if the first operation of a new address is a read, - // then its value must be 0. - // There are exceptions, though: this constraint zero-initializes everything but the code segment and context 0. - yield_constr - .constraint_transition(next_addr_context * initialize_aux * next_values_limbs[i]); - // We don't want to exclude the entirety of context 0. This constraint zero-initializes all segments except the - // specified ones (segment 0 is already included in initialize_aux). - // There is overlap with the previous constraint, but this is not a problem. - yield_constr.constraint_transition( - (next_addr_segment - P::Scalar::from_canonical_usize(Segment::TrieData.unscale())) - * initialize_aux - * next_values_limbs[i], - ); - } - - // Check the range column: First value must be 0, - // and intermediate rows must increment by 1. - let rc1 = local_values[COUNTER]; - let rc2 = next_values[COUNTER]; - yield_constr.constraint_first_row(rc1); - let incr = rc2 - rc1; - yield_constr.constraint_transition(incr - P::Scalar::ONES); - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let one = builder.one_extension(); - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - - let addr_context = local_values[ADDR_CONTEXT]; - let addr_segment = local_values[ADDR_SEGMENT]; - let addr_virtual = local_values[ADDR_VIRTUAL]; - let value_limbs: Vec<_> = (0..8).map(|i| local_values[value_limb(i)]).collect(); - let timestamp = local_values[TIMESTAMP]; - - let next_addr_context = next_values[ADDR_CONTEXT]; - let next_addr_segment = next_values[ADDR_SEGMENT]; - let next_addr_virtual = next_values[ADDR_VIRTUAL]; - let next_values_limbs: Vec<_> = (0..8).map(|i| next_values[value_limb(i)]).collect(); - let next_is_read = next_values[IS_READ]; - let next_timestamp = next_values[TIMESTAMP]; - - // The filter must be 0 or 1. - let filter = local_values[FILTER]; - let constraint = builder.mul_sub_extension(filter, filter, filter); - yield_constr.constraint(builder, constraint); - - // IS_READ must be 0 or 1. - // This is implied by the MemoryStark CTL, where corresponding values are either - // hardcoded to 0/1, or boolean-constrained in their respective STARK modules. - - // If this is a dummy row (filter is off), it must be a read. This means the prover can - // insert reads which never appear in the CPU trace (which are harmless), but not writes. - let is_dummy = builder.sub_extension(one, filter); - let is_write = builder.sub_extension(one, local_values[IS_READ]); - let is_dummy_write = builder.mul_extension(is_dummy, is_write); - yield_constr.constraint(builder, is_dummy_write); - - let context_first_change = local_values[CONTEXT_FIRST_CHANGE]; - let segment_first_change = local_values[SEGMENT_FIRST_CHANGE]; - let virtual_first_change = local_values[VIRTUAL_FIRST_CHANGE]; - let address_unchanged = { - let mut cur = builder.sub_extension(one, context_first_change); - cur = builder.sub_extension(cur, segment_first_change); - builder.sub_extension(cur, virtual_first_change) - }; - - let range_check = local_values[RANGE_CHECK]; - - let not_context_first_change = builder.sub_extension(one, context_first_change); - let not_segment_first_change = builder.sub_extension(one, segment_first_change); - let not_virtual_first_change = builder.sub_extension(one, virtual_first_change); - let not_address_unchanged = builder.sub_extension(one, address_unchanged); - let addr_context_diff = builder.sub_extension(next_addr_context, addr_context); - let addr_segment_diff = builder.sub_extension(next_addr_segment, addr_segment); - let addr_virtual_diff = builder.sub_extension(next_addr_virtual, addr_virtual); - - // First set of ordering constraint: traces are boolean. - let context_first_change_bool = - builder.mul_extension(context_first_change, not_context_first_change); - yield_constr.constraint(builder, context_first_change_bool); - let segment_first_change_bool = - builder.mul_extension(segment_first_change, not_segment_first_change); - yield_constr.constraint(builder, segment_first_change_bool); - let virtual_first_change_bool = - builder.mul_extension(virtual_first_change, not_virtual_first_change); - yield_constr.constraint(builder, virtual_first_change_bool); - let address_unchanged_bool = - builder.mul_extension(address_unchanged, not_address_unchanged); - yield_constr.constraint(builder, address_unchanged_bool); - - // Second set of ordering constraints: no change before the column corresponding to the nonzero first_change flag. - let segment_first_change_check = - builder.mul_extension(segment_first_change, addr_context_diff); - yield_constr.constraint_transition(builder, segment_first_change_check); - let virtual_first_change_check_1 = - builder.mul_extension(virtual_first_change, addr_context_diff); - yield_constr.constraint_transition(builder, virtual_first_change_check_1); - let virtual_first_change_check_2 = - builder.mul_extension(virtual_first_change, addr_segment_diff); - yield_constr.constraint_transition(builder, virtual_first_change_check_2); - let address_unchanged_check_1 = builder.mul_extension(address_unchanged, addr_context_diff); - yield_constr.constraint_transition(builder, address_unchanged_check_1); - let address_unchanged_check_2 = builder.mul_extension(address_unchanged, addr_segment_diff); - yield_constr.constraint_transition(builder, address_unchanged_check_2); - let address_unchanged_check_3 = builder.mul_extension(address_unchanged, addr_virtual_diff); - yield_constr.constraint_transition(builder, address_unchanged_check_3); - - // Third set of ordering constraints: range-check difference in the column that should be increasing. - let context_diff = { - let diff = builder.sub_extension(next_addr_context, addr_context); - builder.sub_extension(diff, one) - }; - let segment_diff = { - let diff = builder.sub_extension(next_addr_segment, addr_segment); - builder.sub_extension(diff, one) - }; - let segment_range_check = builder.mul_extension(segment_first_change, segment_diff); - let virtual_diff = { - let diff = builder.sub_extension(next_addr_virtual, addr_virtual); - builder.sub_extension(diff, one) - }; - let virtual_range_check = builder.mul_extension(virtual_first_change, virtual_diff); - let timestamp_diff = builder.sub_extension(next_timestamp, timestamp); - let timestamp_range_check = builder.mul_extension(address_unchanged, timestamp_diff); - - let computed_range_check = { - // context_range_check = context_first_change * context_diff - let mut sum = - builder.mul_add_extension(context_first_change, context_diff, segment_range_check); - sum = builder.add_extension(sum, virtual_range_check); - builder.add_extension(sum, timestamp_range_check) - }; - let range_check_diff = builder.sub_extension(range_check, computed_range_check); - yield_constr.constraint_transition(builder, range_check_diff); - - // Validate initialize_aux. It contains next_segment * addr_changed * next_is_read. - let initialize_aux = local_values[INITIALIZE_AUX]; - let computed_initialize_aux = builder.mul_extension(not_address_unchanged, next_is_read); - let computed_initialize_aux = - builder.mul_extension(next_addr_segment, computed_initialize_aux); - let new_first_read_constraint = - builder.sub_extension(initialize_aux, computed_initialize_aux); - yield_constr.constraint_transition(builder, new_first_read_constraint); - - for i in 0..8 { - // Enumerate purportedly-ordered log. - let value_diff = builder.sub_extension(next_values_limbs[i], value_limbs[i]); - let zero_if_read = builder.mul_extension(address_unchanged, value_diff); - let read_constraint = builder.mul_extension(next_is_read, zero_if_read); - yield_constr.constraint_transition(builder, read_constraint); - // By default, memory is initialized with 0. This means that if the first operation of a new address is a read, - // then its value must be 0. - // There are exceptions, though: this constraint zero-initializes everything but the code segment and context 0. - let context_zero_initializing_constraint = - builder.mul_extension(next_values_limbs[i], initialize_aux); - let initializing_constraint = - builder.mul_extension(next_addr_context, context_zero_initializing_constraint); - yield_constr.constraint_transition(builder, initializing_constraint); - // We don't want to exclude the entirety of context 0. This constraint zero-initializes all segments except the - // specified ones (segment 0 is already included in initialize_aux). - // There is overlap with the previous constraint, but this is not a problem. - let segment_trie_data = builder.add_const_extension( - next_addr_segment, - F::NEG_ONE * F::from_canonical_usize(Segment::TrieData.unscale()), - ); - let zero_init_constraint = - builder.mul_extension(segment_trie_data, context_zero_initializing_constraint); - yield_constr.constraint_transition(builder, zero_init_constraint); - } - - // Check the range column: First value must be 0, - // and intermediate rows must increment by 1. - let rc1 = local_values[COUNTER]; - let rc2 = next_values[COUNTER]; - yield_constr.constraint_first_row(builder, rc1); - let incr = builder.sub_extension(rc2, rc1); - let t = builder.sub_extension(incr, one); - yield_constr.constraint_transition(builder, t); - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn lookups(&self) -> Vec> { - vec![Lookup { - columns: vec![ - Column::single(RANGE_CHECK), - Column::single_next_row(ADDR_VIRTUAL), - ], - table_column: Column::single(COUNTER), - frequencies_column: Column::single(FREQUENCIES), - filter_columns: vec![ - None, - Some(Filter::new_simple(Column::sum([ - CONTEXT_FIRST_CHANGE, - SEGMENT_FIRST_CHANGE, - ]))), - ], - }] - } - - fn requires_ctls(&self) -> bool { - true - } -} - -#[cfg(test)] -pub(crate) mod tests { - use anyhow::Result; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - use crate::memory::memory_stark::MemoryStark; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = MemoryStark; - - let stark = S { - f: Default::default(), - }; - test_stark_low_degree(stark) - } - - #[test] - fn test_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = MemoryStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } -} diff --git a/evm/src/memory/mod.rs b/evm/src/memory/mod.rs deleted file mode 100644 index c61119530f..0000000000 --- a/evm/src/memory/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! The Memory STARK is used to handle all memory read and write operations happening when -//! executing the EVM. Each non-dummy row of the table correspond to a single operation, -//! and rows are ordered by the timestamp associated to each memory operation. - -pub mod columns; -pub mod memory_stark; -pub mod segments; - -// TODO: Move to CPU module, now that channels have been removed from the memory table. -pub(crate) const NUM_CHANNELS: usize = crate::cpu::membus::NUM_CHANNELS; -/// The number of limbs holding the value at a memory address. -/// Eight limbs of 32 bits can hold a `U256`. -pub(crate) const VALUE_LIMBS: usize = 8; diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs deleted file mode 100644 index 1b4bcbfbf7..0000000000 --- a/evm/src/memory/segments.rs +++ /dev/null @@ -1,212 +0,0 @@ -use ethereum_types::U256; - -pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; - -/// This contains all the existing memory segments. The values in the enum are shifted by 32 bits -/// to allow for convenient address components (context / segment / virtual) bundling in the kernel. -#[allow(dead_code)] -#[allow(clippy::enum_clike_unportable_variant)] -#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] -pub(crate) enum Segment { - /// Contains EVM bytecode. - // The Kernel has optimizations relying on the Code segment being 0. - // This shouldn't be changed! - Code = 0, - /// The program stack. - Stack = 1 << SEGMENT_SCALING_FACTOR, - /// Main memory, owned by the contract code. - MainMemory = 2 << SEGMENT_SCALING_FACTOR, - /// Data passed to the current context by its caller. - Calldata = 3 << SEGMENT_SCALING_FACTOR, - /// Data returned to the current context by its latest callee. - Returndata = 4 << SEGMENT_SCALING_FACTOR, - /// A segment which contains a few fixed-size metadata fields, such as the caller's context, or the - /// size of `CALLDATA` and `RETURNDATA`. - GlobalMetadata = 5 << SEGMENT_SCALING_FACTOR, - ContextMetadata = 6 << SEGMENT_SCALING_FACTOR, - /// General purpose kernel memory, used by various kernel functions. - /// In general, calling a helper function can result in this memory being clobbered. - KernelGeneral = 7 << SEGMENT_SCALING_FACTOR, - /// Another segment for general purpose kernel use. - KernelGeneral2 = 8 << SEGMENT_SCALING_FACTOR, - /// Segment to hold account code for opcodes like `CODESIZE, CODECOPY,...`. - KernelAccountCode = 9 << SEGMENT_SCALING_FACTOR, - /// Contains normalized transaction fields; see `NormalizedTxnField`. - TxnFields = 10 << SEGMENT_SCALING_FACTOR, - /// Contains the data field of a transaction. - TxnData = 11 << SEGMENT_SCALING_FACTOR, - /// A buffer used to hold raw RLP data. - RlpRaw = 12 << SEGMENT_SCALING_FACTOR, - /// Contains all trie data. It is owned by the kernel, so it only lives on context 0. - TrieData = 13 << SEGMENT_SCALING_FACTOR, - ShiftTable = 14 << SEGMENT_SCALING_FACTOR, - JumpdestBits = 15 << SEGMENT_SCALING_FACTOR, - EcdsaTable = 16 << SEGMENT_SCALING_FACTOR, - BnWnafA = 17 << SEGMENT_SCALING_FACTOR, - BnWnafB = 18 << SEGMENT_SCALING_FACTOR, - BnTableQ = 19 << SEGMENT_SCALING_FACTOR, - BnPairing = 20 << SEGMENT_SCALING_FACTOR, - /// List of addresses that have been accessed in the current transaction. - AccessedAddresses = 21 << SEGMENT_SCALING_FACTOR, - /// List of storage keys that have been accessed in the current transaction. - AccessedStorageKeys = 22 << SEGMENT_SCALING_FACTOR, - /// List of addresses that have called SELFDESTRUCT in the current transaction. - SelfDestructList = 23 << SEGMENT_SCALING_FACTOR, - /// Contains the bloom filter of a transaction. - TxnBloom = 24 << SEGMENT_SCALING_FACTOR, - /// Contains the bloom filter present in the block header. - GlobalBlockBloom = 25 << SEGMENT_SCALING_FACTOR, - /// List of log pointers pointing to the LogsData segment. - Logs = 26 << SEGMENT_SCALING_FACTOR, - LogsData = 27 << SEGMENT_SCALING_FACTOR, - /// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`. - Journal = 28 << SEGMENT_SCALING_FACTOR, - JournalData = 29 << SEGMENT_SCALING_FACTOR, - JournalCheckpoints = 30 << SEGMENT_SCALING_FACTOR, - /// List of addresses that have been touched in the current transaction. - TouchedAddresses = 31 << SEGMENT_SCALING_FACTOR, - /// List of checkpoints for the current context. Length in `ContextMetadata`. - ContextCheckpoints = 32 << SEGMENT_SCALING_FACTOR, - /// List of 256 previous block hashes. - BlockHashes = 33 << SEGMENT_SCALING_FACTOR, -} - -impl Segment { - pub(crate) const COUNT: usize = 34; - - /// Unscales this segment by `SEGMENT_SCALING_FACTOR`. - pub(crate) const fn unscale(&self) -> usize { - *self as usize >> SEGMENT_SCALING_FACTOR - } - - pub(crate) const fn all() -> [Self; Self::COUNT] { - [ - Self::Code, - Self::Stack, - Self::MainMemory, - Self::Calldata, - Self::Returndata, - Self::GlobalMetadata, - Self::ContextMetadata, - Self::KernelGeneral, - Self::KernelGeneral2, - Self::KernelAccountCode, - Self::TxnFields, - Self::TxnData, - Self::RlpRaw, - Self::TrieData, - Self::ShiftTable, - Self::JumpdestBits, - Self::EcdsaTable, - Self::BnWnafA, - Self::BnWnafB, - Self::BnTableQ, - Self::BnPairing, - Self::AccessedAddresses, - Self::AccessedStorageKeys, - Self::SelfDestructList, - Self::TxnBloom, - Self::GlobalBlockBloom, - Self::Logs, - Self::LogsData, - Self::Journal, - Self::JournalData, - Self::JournalCheckpoints, - Self::TouchedAddresses, - Self::ContextCheckpoints, - Self::BlockHashes, - ] - } - - /// The variable name that gets passed into kernel assembly code. - pub(crate) const fn var_name(&self) -> &'static str { - match self { - Segment::Code => "SEGMENT_CODE", - Segment::Stack => "SEGMENT_STACK", - Segment::MainMemory => "SEGMENT_MAIN_MEMORY", - Segment::Calldata => "SEGMENT_CALLDATA", - Segment::Returndata => "SEGMENT_RETURNDATA", - Segment::GlobalMetadata => "SEGMENT_GLOBAL_METADATA", - Segment::ContextMetadata => "SEGMENT_CONTEXT_METADATA", - Segment::KernelGeneral => "SEGMENT_KERNEL_GENERAL", - Segment::KernelGeneral2 => "SEGMENT_KERNEL_GENERAL_2", - Segment::KernelAccountCode => "SEGMENT_KERNEL_ACCOUNT_CODE", - Segment::TxnFields => "SEGMENT_NORMALIZED_TXN", - Segment::TxnData => "SEGMENT_TXN_DATA", - Segment::RlpRaw => "SEGMENT_RLP_RAW", - Segment::TrieData => "SEGMENT_TRIE_DATA", - Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", - Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", - Segment::EcdsaTable => "SEGMENT_ECDSA_TABLE", - Segment::BnWnafA => "SEGMENT_BN_WNAF_A", - Segment::BnWnafB => "SEGMENT_BN_WNAF_B", - Segment::BnTableQ => "SEGMENT_BN_TABLE_Q", - Segment::BnPairing => "SEGMENT_BN_PAIRING", - Segment::AccessedAddresses => "SEGMENT_ACCESSED_ADDRESSES", - Segment::AccessedStorageKeys => "SEGMENT_ACCESSED_STORAGE_KEYS", - Segment::SelfDestructList => "SEGMENT_SELFDESTRUCT_LIST", - Segment::TxnBloom => "SEGMENT_TXN_BLOOM", - Segment::GlobalBlockBloom => "SEGMENT_GLOBAL_BLOCK_BLOOM", - Segment::Logs => "SEGMENT_LOGS", - Segment::LogsData => "SEGMENT_LOGS_DATA", - Segment::Journal => "SEGMENT_JOURNAL", - Segment::JournalData => "SEGMENT_JOURNAL_DATA", - Segment::JournalCheckpoints => "SEGMENT_JOURNAL_CHECKPOINTS", - Segment::TouchedAddresses => "SEGMENT_TOUCHED_ADDRESSES", - Segment::ContextCheckpoints => "SEGMENT_CONTEXT_CHECKPOINTS", - Segment::BlockHashes => "SEGMENT_BLOCK_HASHES", - } - } - - pub(crate) const fn bit_range(&self) -> usize { - match self { - Segment::Code => 8, - Segment::Stack => 256, - Segment::MainMemory => 8, - Segment::Calldata => 8, - Segment::Returndata => 8, - Segment::GlobalMetadata => 256, - Segment::ContextMetadata => 256, - Segment::KernelGeneral => 256, - Segment::KernelGeneral2 => 256, - Segment::KernelAccountCode => 8, - Segment::TxnFields => 256, - Segment::TxnData => 8, - Segment::RlpRaw => 8, - Segment::TrieData => 256, - Segment::ShiftTable => 256, - Segment::JumpdestBits => 1, - Segment::EcdsaTable => 256, - Segment::BnWnafA => 8, - Segment::BnWnafB => 8, - Segment::BnTableQ => 256, - Segment::BnPairing => 256, - Segment::AccessedAddresses => 256, - Segment::AccessedStorageKeys => 256, - Segment::SelfDestructList => 256, - Segment::TxnBloom => 8, - Segment::GlobalBlockBloom => 256, - Segment::Logs => 256, - Segment::LogsData => 256, - Segment::Journal => 256, - Segment::JournalData => 256, - Segment::JournalCheckpoints => 256, - Segment::TouchedAddresses => 256, - Segment::ContextCheckpoints => 256, - Segment::BlockHashes => 256, - } - } - - pub(crate) fn constant(&self, virt: usize) -> Option { - match self { - Segment::RlpRaw => { - if virt == 0xFFFFFFFF { - Some(U256::from(0x80)) - } else { - None - } - } - _ => None, - } - } -} diff --git a/evm/src/proof.rs b/evm/src/proof.rs deleted file mode 100644 index bc70dbb857..0000000000 --- a/evm/src/proof.rs +++ /dev/null @@ -1,814 +0,0 @@ -use ethereum_types::{Address, H256, U256}; -use plonky2::field::extension::Extendable; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::target::{BoolTarget, Target}; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::GenericConfig; -use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; -use serde::{Deserialize, Serialize}; -use starky::config::StarkConfig; -use starky::lookup::GrandProductChallengeSet; -use starky::proof::{MultiProof, StarkProofChallenges}; - -use crate::all_stark::NUM_TABLES; -use crate::util::{get_h160, get_h256, h2u}; - -/// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs. -#[derive(Debug, Clone)] -pub struct AllProof, C: GenericConfig, const D: usize> { - /// A multi-proof containing all proofs for the different STARK modules and their - /// cross-table lookup challenges. - pub multi_proof: MultiProof, - /// Public memory values used for the recursive proofs. - pub public_values: PublicValues, -} - -impl, C: GenericConfig, const D: usize> AllProof { - /// Returns the degree (i.e. the trace length) of each STARK. - pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] { - self.multi_proof.recover_degree_bits(config) - } -} - -/// Randomness for all STARKs. -pub(crate) struct AllProofChallenges, const D: usize> { - /// Randomness used in each STARK proof. - pub stark_challenges: [StarkProofChallenges; NUM_TABLES], - /// Randomness used for cross-table lookups. It is shared by all STARKs. - pub ctl_challenges: GrandProductChallengeSet, -} - -/// Memory values which are public. -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct PublicValues { - /// Trie hashes before the execution of the local state transition - pub trie_roots_before: TrieRoots, - /// Trie hashes after the execution of the local state transition. - pub trie_roots_after: TrieRoots, - /// Block metadata: it remains unchanged within a block. - pub block_metadata: BlockMetadata, - /// 256 previous block hashes and current block's hash. - pub block_hashes: BlockHashes, - /// Extra block data that is specific to the current proof. - pub extra_block_data: ExtraBlockData, -} - -impl PublicValues { - /// Extracts public values from the given public inputs of a proof. - /// Public values are always the first public inputs added to the circuit, - /// so we can start extracting at index 0. - pub fn from_public_inputs(pis: &[F]) -> Self { - assert!( - pis.len() - > TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + ExtraBlockDataTarget::SIZE - - 1 - ); - - let trie_roots_before = TrieRoots::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]); - let trie_roots_after = - TrieRoots::from_public_inputs(&pis[TrieRootsTarget::SIZE..TrieRootsTarget::SIZE * 2]); - let block_metadata = BlockMetadata::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE], - ); - let block_hashes = BlockHashes::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE], - ); - let extra_block_data = ExtraBlockData::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - ..TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + ExtraBlockDataTarget::SIZE], - ); - - Self { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, - } - } -} - -/// Trie hashes. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct TrieRoots { - /// State trie hash. - pub state_root: H256, - /// Transaction trie hash. - pub transactions_root: H256, - /// Receipts trie hash. - pub receipts_root: H256, -} - -impl TrieRoots { - pub fn from_public_inputs(pis: &[F]) -> Self { - assert!(pis.len() == TrieRootsTarget::SIZE); - - let state_root = get_h256(&pis[0..8]); - let transactions_root = get_h256(&pis[8..16]); - let receipts_root = get_h256(&pis[16..24]); - - Self { - state_root, - transactions_root, - receipts_root, - } - } -} - -// There should be 256 previous hashes stored, so the default should also contain 256 values. -impl Default for BlockHashes { - fn default() -> Self { - Self { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - } - } -} - -/// User-provided helper values to compute the `BLOCKHASH` opcode. -/// The proofs across consecutive blocks ensure that these values -/// are consistent (i.e. shifted by one to the left). -/// -/// When the block number is less than 256, dummy values, i.e. `H256::default()`, -/// should be used for the additional block hashes. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct BlockHashes { - /// The previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0]`, - /// is the oldest, and the rightmost, i.e. `prev_hashes[255]` is the hash of the parent block. - pub prev_hashes: Vec, - // The hash of the current block. - pub cur_hash: H256, -} - -impl BlockHashes { - pub fn from_public_inputs(pis: &[F]) -> Self { - assert!(pis.len() == BlockHashesTarget::SIZE); - - let prev_hashes: [H256; 256] = core::array::from_fn(|i| get_h256(&pis[8 * i..8 + 8 * i])); - let cur_hash = get_h256(&pis[2048..2056]); - - Self { - prev_hashes: prev_hashes.to_vec(), - cur_hash, - } - } -} - -/// Metadata contained in a block header. Those are identical between -/// all state transition proofs within the same block. -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct BlockMetadata { - /// The address of this block's producer. - pub block_beneficiary: Address, - /// The timestamp of this block. - pub block_timestamp: U256, - /// The index of this block. - pub block_number: U256, - /// The difficulty (before PoS transition) of this block. - pub block_difficulty: U256, - pub block_random: H256, - /// The gas limit of this block. It must fit in a `u32`. - pub block_gaslimit: U256, - /// The chain id of this block. - pub block_chain_id: U256, - /// The base fee of this block. - pub block_base_fee: U256, - /// The total gas used in this block. It must fit in a `u32`. - pub block_gas_used: U256, - /// The block bloom of this block, represented as the consecutive - /// 32-byte chunks of a block's final bloom filter string. - pub block_bloom: [U256; 8], -} - -impl BlockMetadata { - pub fn from_public_inputs(pis: &[F]) -> Self { - assert!(pis.len() == BlockMetadataTarget::SIZE); - - let block_beneficiary = get_h160(&pis[0..5]); - let block_timestamp = pis[5].to_canonical_u64().into(); - let block_number = pis[6].to_canonical_u64().into(); - let block_difficulty = pis[7].to_canonical_u64().into(); - let block_random = get_h256(&pis[8..16]); - let block_gaslimit = pis[16].to_canonical_u64().into(); - let block_chain_id = pis[17].to_canonical_u64().into(); - let block_base_fee = - (pis[18].to_canonical_u64() + (pis[19].to_canonical_u64() << 32)).into(); - let block_gas_used = pis[20].to_canonical_u64().into(); - let block_bloom = core::array::from_fn(|i| h2u(get_h256(&pis[21 + 8 * i..29 + 8 * i]))); - - Self { - block_beneficiary, - block_timestamp, - block_number, - block_difficulty, - block_random, - block_gaslimit, - block_chain_id, - block_base_fee, - block_gas_used, - block_bloom, - } - } -} - -/// Additional block data that are specific to the local transaction being proven, -/// unlike `BlockMetadata`. -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct ExtraBlockData { - /// The state trie digest of the checkpoint block. - pub checkpoint_state_trie_root: H256, - /// The transaction count prior execution of the local state transition, starting - /// at 0 for the initial transaction of a block. - pub txn_number_before: U256, - /// The transaction count after execution of the local state transition. - pub txn_number_after: U256, - /// The accumulated gas used prior execution of the local state transition, starting - /// at 0 for the initial transaction of a block. - pub gas_used_before: U256, - /// The accumulated gas used after execution of the local state transition. It should - /// match the `block_gas_used` value after execution of the last transaction in a block. - pub gas_used_after: U256, -} - -impl ExtraBlockData { - pub fn from_public_inputs(pis: &[F]) -> Self { - assert!(pis.len() == ExtraBlockDataTarget::SIZE); - - let checkpoint_state_trie_root = get_h256(&pis[0..8]); - let txn_number_before = pis[8].to_canonical_u64().into(); - let txn_number_after = pis[9].to_canonical_u64().into(); - let gas_used_before = pis[10].to_canonical_u64().into(); - let gas_used_after = pis[11].to_canonical_u64().into(); - - Self { - checkpoint_state_trie_root, - txn_number_before, - txn_number_after, - gas_used_before, - gas_used_after, - } - } -} - -/// Memory values which are public. -/// Note: All the larger integers are encoded with 32-bit limbs in little-endian order. -#[derive(Eq, PartialEq, Debug)] -pub struct PublicValuesTarget { - /// Trie hashes before the execution of the local state transition. - pub trie_roots_before: TrieRootsTarget, - /// Trie hashes after the execution of the local state transition. - pub trie_roots_after: TrieRootsTarget, - /// Block metadata: it remains unchanged within a block. - pub block_metadata: BlockMetadataTarget, - /// 256 previous block hashes and current block's hash. - pub block_hashes: BlockHashesTarget, - /// Extra block data that is specific to the current proof. - pub extra_block_data: ExtraBlockDataTarget, -} - -impl PublicValuesTarget { - /// Serializes public value targets. - pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - let TrieRootsTarget { - state_root: state_root_before, - transactions_root: transactions_root_before, - receipts_root: receipts_root_before, - } = self.trie_roots_before; - - buffer.write_target_array(&state_root_before)?; - buffer.write_target_array(&transactions_root_before)?; - buffer.write_target_array(&receipts_root_before)?; - - let TrieRootsTarget { - state_root: state_root_after, - transactions_root: transactions_root_after, - receipts_root: receipts_root_after, - } = self.trie_roots_after; - - buffer.write_target_array(&state_root_after)?; - buffer.write_target_array(&transactions_root_after)?; - buffer.write_target_array(&receipts_root_after)?; - - let BlockMetadataTarget { - block_beneficiary, - block_timestamp, - block_number, - block_difficulty, - block_random, - block_gaslimit, - block_chain_id, - block_base_fee, - block_gas_used, - block_bloom, - } = self.block_metadata; - - buffer.write_target_array(&block_beneficiary)?; - buffer.write_target(block_timestamp)?; - buffer.write_target(block_number)?; - buffer.write_target(block_difficulty)?; - buffer.write_target_array(&block_random)?; - buffer.write_target(block_gaslimit)?; - buffer.write_target(block_chain_id)?; - buffer.write_target_array(&block_base_fee)?; - buffer.write_target(block_gas_used)?; - buffer.write_target_array(&block_bloom)?; - - let BlockHashesTarget { - prev_hashes, - cur_hash, - } = self.block_hashes; - buffer.write_target_array(&prev_hashes)?; - buffer.write_target_array(&cur_hash)?; - - let ExtraBlockDataTarget { - checkpoint_state_trie_root, - txn_number_before, - txn_number_after, - gas_used_before, - gas_used_after, - } = self.extra_block_data; - buffer.write_target_array(&checkpoint_state_trie_root)?; - buffer.write_target(txn_number_before)?; - buffer.write_target(txn_number_after)?; - buffer.write_target(gas_used_before)?; - buffer.write_target(gas_used_after)?; - - Ok(()) - } - - /// Deserializes public value targets. - pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { - let trie_roots_before = TrieRootsTarget { - state_root: buffer.read_target_array()?, - transactions_root: buffer.read_target_array()?, - receipts_root: buffer.read_target_array()?, - }; - - let trie_roots_after = TrieRootsTarget { - state_root: buffer.read_target_array()?, - transactions_root: buffer.read_target_array()?, - receipts_root: buffer.read_target_array()?, - }; - - let block_metadata = BlockMetadataTarget { - block_beneficiary: buffer.read_target_array()?, - block_timestamp: buffer.read_target()?, - block_number: buffer.read_target()?, - block_difficulty: buffer.read_target()?, - block_random: buffer.read_target_array()?, - block_gaslimit: buffer.read_target()?, - block_chain_id: buffer.read_target()?, - block_base_fee: buffer.read_target_array()?, - block_gas_used: buffer.read_target()?, - block_bloom: buffer.read_target_array()?, - }; - - let block_hashes = BlockHashesTarget { - prev_hashes: buffer.read_target_array()?, - cur_hash: buffer.read_target_array()?, - }; - - let extra_block_data = ExtraBlockDataTarget { - checkpoint_state_trie_root: buffer.read_target_array()?, - txn_number_before: buffer.read_target()?, - txn_number_after: buffer.read_target()?, - gas_used_before: buffer.read_target()?, - gas_used_after: buffer.read_target()?, - }; - - Ok(Self { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, - }) - } - - /// Extracts public value `Target`s from the given public input `Target`s. - /// Public values are always the first public inputs added to the circuit, - /// so we can start extracting at index 0. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - assert!( - pis.len() - > TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + ExtraBlockDataTarget::SIZE - - 1 - ); - - Self { - trie_roots_before: TrieRootsTarget::from_public_inputs(&pis[0..TrieRootsTarget::SIZE]), - trie_roots_after: TrieRootsTarget::from_public_inputs( - &pis[TrieRootsTarget::SIZE..TrieRootsTarget::SIZE * 2], - ), - block_metadata: BlockMetadataTarget::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 - ..TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE], - ), - block_hashes: BlockHashesTarget::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE - ..TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE], - ), - extra_block_data: ExtraBlockDataTarget::from_public_inputs( - &pis[TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - ..TrieRootsTarget::SIZE * 2 - + BlockMetadataTarget::SIZE - + BlockHashesTarget::SIZE - + ExtraBlockDataTarget::SIZE], - ), - } - } - - /// Returns the public values in `pv0` or `pv1` depening on `condition`. - pub(crate) fn select, const D: usize>( - builder: &mut CircuitBuilder, - condition: BoolTarget, - pv0: Self, - pv1: Self, - ) -> Self { - Self { - trie_roots_before: TrieRootsTarget::select( - builder, - condition, - pv0.trie_roots_before, - pv1.trie_roots_before, - ), - trie_roots_after: TrieRootsTarget::select( - builder, - condition, - pv0.trie_roots_after, - pv1.trie_roots_after, - ), - block_metadata: BlockMetadataTarget::select( - builder, - condition, - pv0.block_metadata, - pv1.block_metadata, - ), - block_hashes: BlockHashesTarget::select( - builder, - condition, - pv0.block_hashes, - pv1.block_hashes, - ), - extra_block_data: ExtraBlockDataTarget::select( - builder, - condition, - pv0.extra_block_data, - pv1.extra_block_data, - ), - } - } -} - -/// Circuit version of `TrieRoots`. -/// `Target`s for trie hashes. Since a `Target` holds a 32-bit limb, each hash requires 8 `Target`s. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct TrieRootsTarget { - /// Targets for the state trie hash. - pub(crate) state_root: [Target; 8], - /// Targets for the transactions trie hash. - pub(crate) transactions_root: [Target; 8], - /// Targets for the receipts trie hash. - pub(crate) receipts_root: [Target; 8], -} - -impl TrieRootsTarget { - /// Number of `Target`s required for all trie hashes. - pub(crate) const HASH_SIZE: usize = 8; - pub(crate) const SIZE: usize = Self::HASH_SIZE * 3; - - /// Extracts trie hash `Target`s for all tries from the provided public input `Target`s. - /// The provided `pis` should start with the trie hashes. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - let state_root = pis[0..8].try_into().unwrap(); - let transactions_root = pis[8..16].try_into().unwrap(); - let receipts_root = pis[16..24].try_into().unwrap(); - - Self { - state_root, - transactions_root, - receipts_root, - } - } - - /// If `condition`, returns the trie hashes in `tr0`, - /// otherwise returns the trie hashes in `tr1`. - pub(crate) fn select, const D: usize>( - builder: &mut CircuitBuilder, - condition: BoolTarget, - tr0: Self, - tr1: Self, - ) -> Self { - Self { - state_root: core::array::from_fn(|i| { - builder.select(condition, tr0.state_root[i], tr1.state_root[i]) - }), - transactions_root: core::array::from_fn(|i| { - builder.select( - condition, - tr0.transactions_root[i], - tr1.transactions_root[i], - ) - }), - receipts_root: core::array::from_fn(|i| { - builder.select(condition, tr0.receipts_root[i], tr1.receipts_root[i]) - }), - } - } - - /// Connects the trie hashes in `tr0` and in `tr1`. - pub(crate) fn connect, const D: usize>( - builder: &mut CircuitBuilder, - tr0: Self, - tr1: Self, - ) { - for i in 0..8 { - builder.connect(tr0.state_root[i], tr1.state_root[i]); - builder.connect(tr0.transactions_root[i], tr1.transactions_root[i]); - builder.connect(tr0.receipts_root[i], tr1.receipts_root[i]); - } - } -} - -/// Circuit version of `BlockMetadata`. -/// Metadata contained in a block header. Those are identical between -/// all state transition proofs within the same block. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct BlockMetadataTarget { - /// `Target`s for the address of this block's producer. - pub(crate) block_beneficiary: [Target; 5], - /// `Target` for the timestamp of this block. - pub(crate) block_timestamp: Target, - /// `Target` for the index of this block. - pub(crate) block_number: Target, - /// `Target` for the difficulty (before PoS transition) of this block. - pub(crate) block_difficulty: Target, - /// `Target`s for the `mix_hash` value of this block. - pub(crate) block_random: [Target; 8], - /// `Target`s for the gas limit of this block. - pub(crate) block_gaslimit: Target, - /// `Target` for the chain id of this block. - pub(crate) block_chain_id: Target, - /// `Target`s for the base fee of this block. - pub(crate) block_base_fee: [Target; 2], - /// `Target`s for the gas used of this block. - pub(crate) block_gas_used: Target, - /// `Target`s for the block bloom of this block. - pub(crate) block_bloom: [Target; 64], -} - -impl BlockMetadataTarget { - /// Number of `Target`s required for the block metadata. - pub(crate) const SIZE: usize = 85; - - /// Extracts block metadata `Target`s from the provided public input `Target`s. - /// The provided `pis` should start with the block metadata. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - let block_beneficiary = pis[0..5].try_into().unwrap(); - let block_timestamp = pis[5]; - let block_number = pis[6]; - let block_difficulty = pis[7]; - let block_random = pis[8..16].try_into().unwrap(); - let block_gaslimit = pis[16]; - let block_chain_id = pis[17]; - let block_base_fee = pis[18..20].try_into().unwrap(); - let block_gas_used = pis[20]; - let block_bloom = pis[21..85].try_into().unwrap(); - - Self { - block_beneficiary, - block_timestamp, - block_number, - block_difficulty, - block_random, - block_gaslimit, - block_chain_id, - block_base_fee, - block_gas_used, - block_bloom, - } - } - - /// If `condition`, returns the block metadata in `bm0`, - /// otherwise returns the block metadata in `bm1`. - pub(crate) fn select, const D: usize>( - builder: &mut CircuitBuilder, - condition: BoolTarget, - bm0: Self, - bm1: Self, - ) -> Self { - Self { - block_beneficiary: core::array::from_fn(|i| { - builder.select( - condition, - bm0.block_beneficiary[i], - bm1.block_beneficiary[i], - ) - }), - block_timestamp: builder.select(condition, bm0.block_timestamp, bm1.block_timestamp), - block_number: builder.select(condition, bm0.block_number, bm1.block_number), - block_difficulty: builder.select(condition, bm0.block_difficulty, bm1.block_difficulty), - block_random: core::array::from_fn(|i| { - builder.select(condition, bm0.block_random[i], bm1.block_random[i]) - }), - block_gaslimit: builder.select(condition, bm0.block_gaslimit, bm1.block_gaslimit), - block_chain_id: builder.select(condition, bm0.block_chain_id, bm1.block_chain_id), - block_base_fee: core::array::from_fn(|i| { - builder.select(condition, bm0.block_base_fee[i], bm1.block_base_fee[i]) - }), - block_gas_used: builder.select(condition, bm0.block_gas_used, bm1.block_gas_used), - block_bloom: core::array::from_fn(|i| { - builder.select(condition, bm0.block_bloom[i], bm1.block_bloom[i]) - }), - } - } - - /// Connects the block metadata in `bm0` to the block metadata in `bm1`. - pub(crate) fn connect, const D: usize>( - builder: &mut CircuitBuilder, - bm0: Self, - bm1: Self, - ) { - for i in 0..5 { - builder.connect(bm0.block_beneficiary[i], bm1.block_beneficiary[i]); - } - builder.connect(bm0.block_timestamp, bm1.block_timestamp); - builder.connect(bm0.block_number, bm1.block_number); - builder.connect(bm0.block_difficulty, bm1.block_difficulty); - for i in 0..8 { - builder.connect(bm0.block_random[i], bm1.block_random[i]); - } - builder.connect(bm0.block_gaslimit, bm1.block_gaslimit); - builder.connect(bm0.block_chain_id, bm1.block_chain_id); - for i in 0..2 { - builder.connect(bm0.block_base_fee[i], bm1.block_base_fee[i]) - } - builder.connect(bm0.block_gas_used, bm1.block_gas_used); - for i in 0..64 { - builder.connect(bm0.block_bloom[i], bm1.block_bloom[i]) - } - } -} - -/// Circuit version of `BlockHashes`. -/// `Target`s for the user-provided previous 256 block hashes and current block hash. -/// Each block hash requires 8 `Target`s. -/// The proofs across consecutive blocks ensure that these values -/// are consistent (i.e. shifted by eight `Target`s to the left). -/// -/// When the block number is less than 256, dummy values, i.e. `H256::default()`, -/// should be used for the additional block hashes. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct BlockHashesTarget { - /// `Target`s for the previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0..8]`, - /// is the oldest, and the rightmost, i.e. `prev_hashes[255 * 7..255 * 8]` is the hash of the parent block. - pub(crate) prev_hashes: [Target; 2048], - // `Target` for the hash of the current block. - pub(crate) cur_hash: [Target; 8], -} - -impl BlockHashesTarget { - /// Number of `Target`s required for previous and current block hashes. - pub(crate) const SIZE: usize = 2056; - - /// Extracts the previous and current block hash `Target`s from the public input `Target`s. - /// The provided `pis` should start with the block hashes. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - Self { - prev_hashes: pis[0..2048].try_into().unwrap(), - cur_hash: pis[2048..2056].try_into().unwrap(), - } - } - - /// If `condition`, returns the block hashes in `bm0`, - /// otherwise returns the block hashes in `bm1`. - pub(crate) fn select, const D: usize>( - builder: &mut CircuitBuilder, - condition: BoolTarget, - bm0: Self, - bm1: Self, - ) -> Self { - Self { - prev_hashes: core::array::from_fn(|i| { - builder.select(condition, bm0.prev_hashes[i], bm1.prev_hashes[i]) - }), - cur_hash: core::array::from_fn(|i| { - builder.select(condition, bm0.cur_hash[i], bm1.cur_hash[i]) - }), - } - } - - /// Connects the block hashes in `bm0` to the block hashes in `bm1`. - pub(crate) fn connect, const D: usize>( - builder: &mut CircuitBuilder, - bm0: Self, - bm1: Self, - ) { - for i in 0..2048 { - builder.connect(bm0.prev_hashes[i], bm1.prev_hashes[i]); - } - for i in 0..8 { - builder.connect(bm0.cur_hash[i], bm1.cur_hash[i]); - } - } -} - -/// Circuit version of `ExtraBlockData`. -/// Additional block data that are specific to the local transaction being proven, -/// unlike `BlockMetadata`. -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct ExtraBlockDataTarget { - /// `Target`s for the state trie digest of the checkpoint block. - pub checkpoint_state_trie_root: [Target; 8], - /// `Target` for the transaction count prior execution of the local state transition, starting - /// at 0 for the initial trnasaction of a block. - pub txn_number_before: Target, - /// `Target` for the transaction count after execution of the local state transition. - pub txn_number_after: Target, - /// `Target` for the accumulated gas used prior execution of the local state transition, starting - /// at 0 for the initial transaction of a block. - pub gas_used_before: Target, - /// `Target` for the accumulated gas used after execution of the local state transition. It should - /// match the `block_gas_used` value after execution of the last transaction in a block. - pub gas_used_after: Target, -} - -impl ExtraBlockDataTarget { - /// Number of `Target`s required for the extra block data. - const SIZE: usize = 12; - - /// Extracts the extra block data `Target`s from the public input `Target`s. - /// The provided `pis` should start with the extra vblock data. - pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { - let checkpoint_state_trie_root = pis[0..8].try_into().unwrap(); - let txn_number_before = pis[8]; - let txn_number_after = pis[9]; - let gas_used_before = pis[10]; - let gas_used_after = pis[11]; - - Self { - checkpoint_state_trie_root, - txn_number_before, - txn_number_after, - gas_used_before, - gas_used_after, - } - } - - /// If `condition`, returns the extra block data in `ed0`, - /// otherwise returns the extra block data in `ed1`. - pub(crate) fn select, const D: usize>( - builder: &mut CircuitBuilder, - condition: BoolTarget, - ed0: Self, - ed1: Self, - ) -> Self { - Self { - checkpoint_state_trie_root: core::array::from_fn(|i| { - builder.select( - condition, - ed0.checkpoint_state_trie_root[i], - ed1.checkpoint_state_trie_root[i], - ) - }), - txn_number_before: builder.select( - condition, - ed0.txn_number_before, - ed1.txn_number_before, - ), - txn_number_after: builder.select(condition, ed0.txn_number_after, ed1.txn_number_after), - gas_used_before: builder.select(condition, ed0.gas_used_before, ed1.gas_used_before), - gas_used_after: builder.select(condition, ed0.gas_used_after, ed1.gas_used_after), - } - } - - /// Connects the extra block data in `ed0` with the extra block data in `ed1`. - pub(crate) fn connect, const D: usize>( - builder: &mut CircuitBuilder, - ed0: Self, - ed1: Self, - ) { - for i in 0..8 { - builder.connect( - ed0.checkpoint_state_trie_root[i], - ed1.checkpoint_state_trie_root[i], - ); - } - builder.connect(ed0.txn_number_before, ed1.txn_number_before); - builder.connect(ed0.txn_number_after, ed1.txn_number_after); - builder.connect(ed0.gas_used_before, ed1.gas_used_before); - builder.connect(ed0.gas_used_after, ed1.gas_used_after); - } -} diff --git a/evm/src/prover.rs b/evm/src/prover.rs deleted file mode 100644 index 8f11c112b1..0000000000 --- a/evm/src/prover.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; - -use anyhow::{anyhow, Result}; -use hashbrown::HashMap; -use itertools::Itertools; -use once_cell::sync::Lazy; -use plonky2::field::extension::Extendable; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::fri::oracle::PolynomialBatch; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::challenger::Challenger; -use plonky2::plonk::config::GenericConfig; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use starky::config::StarkConfig; -#[cfg(debug_assertions)] -use starky::cross_table_lookup::debug_utils::check_ctls; -use starky::cross_table_lookup::{get_ctl_data, CtlData}; -use starky::lookup::GrandProductChallengeSet; -use starky::proof::{MultiProof, StarkProofWithMetadata}; -use starky::prover::prove_with_commitment; -use starky::stark::Stark; - -use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::generation::{generate_traces, GenerationInputs}; -use crate::get_challenges::observe_public_values; -use crate::proof::{AllProof, PublicValues}; -#[cfg(debug_assertions)] -use crate::verifier::debug_utils::get_memory_extra_looking_values; - -/// Generate traces, then create all STARK proofs. -pub fn prove( - all_stark: &AllStark, - config: &StarkConfig, - inputs: GenerationInputs, - timing: &mut TimingTree, - abort_signal: Option>, -) -> Result> -where - F: RichField + Extendable, - C: GenericConfig, -{ - timed!(timing, "build kernel", Lazy::force(&KERNEL)); - let (traces, public_values) = timed!( - timing, - "generate all traces", - generate_traces(all_stark, inputs, config, timing)? - ); - check_abort_signal(abort_signal.clone())?; - - let proof = prove_with_traces( - all_stark, - config, - traces, - public_values, - timing, - abort_signal, - )?; - Ok(proof) -} - -/// Compute all STARK proofs. -pub(crate) fn prove_with_traces( - all_stark: &AllStark, - config: &StarkConfig, - trace_poly_values: [Vec>; NUM_TABLES], - public_values: PublicValues, - timing: &mut TimingTree, - abort_signal: Option>, -) -> Result> -where - F: RichField + Extendable, - C: GenericConfig, -{ - let rate_bits = config.fri_config.rate_bits; - let cap_height = config.fri_config.cap_height; - - // For each STARK, we compute the polynomial commitments for the polynomials interpolating its trace. - let trace_commitments = timed!( - timing, - "compute all trace commitments", - trace_poly_values - .iter() - .zip_eq(Table::all()) - .map(|(trace, table)| { - timed!( - timing, - &format!("compute trace commitment for {:?}", table), - PolynomialBatch::::from_values( - trace.clone(), - rate_bits, - false, - cap_height, - timing, - None, - ) - ) - }) - .collect::>() - ); - - // Get the Merkle caps for all trace commitments and observe them. - let trace_caps = trace_commitments - .iter() - .map(|c| c.merkle_tree.cap.clone()) - .collect::>(); - let mut challenger = Challenger::::new(); - for cap in &trace_caps { - challenger.observe_cap(cap); - } - - observe_public_values::(&mut challenger, &public_values) - .map_err(|_| anyhow::Error::msg("Invalid conversion of public values."))?; - - // For each STARK, compute its cross-table lookup Z polynomials and get the associated `CtlData`. - let (ctl_challenges, ctl_data_per_table) = timed!( - timing, - "compute CTL data", - get_ctl_data::( - config, - &trace_poly_values, - &all_stark.cross_table_lookups, - &mut challenger, - all_stark.arithmetic_stark.constraint_degree() - ) - ); - - let stark_proofs = timed!( - timing, - "compute all proofs given commitments", - prove_with_commitments( - all_stark, - config, - &trace_poly_values, - trace_commitments, - ctl_data_per_table, - &mut challenger, - &ctl_challenges, - timing, - abort_signal, - )? - ); - - // This is an expensive check, hence is only run when `debug_assertions` are enabled. - #[cfg(debug_assertions)] - { - let mut extra_values = HashMap::new(); - extra_values.insert( - *Table::Memory, - get_memory_extra_looking_values(&public_values), - ); - check_ctls( - &trace_poly_values, - &all_stark.cross_table_lookups, - &extra_values, - ); - } - - Ok(AllProof { - multi_proof: MultiProof { - stark_proofs, - ctl_challenges, - }, - public_values, - }) -} - -/// Generates a proof for each STARK. -/// At this stage, we have computed the trace polynomials commitments for the various STARKs, -/// and we have the cross-table lookup data for each table, including the associated challenges. -/// - `trace_poly_values` are the trace values for each STARK. -/// - `trace_commitments` are the trace polynomials commitments for each STARK. -/// - `ctl_data_per_table` group all the cross-table lookup data for each STARK. -/// Each STARK uses its associated data to generate a proof. -fn prove_with_commitments( - all_stark: &AllStark, - config: &StarkConfig, - trace_poly_values: &[Vec>; NUM_TABLES], - trace_commitments: Vec>, - ctl_data_per_table: [CtlData; NUM_TABLES], - challenger: &mut Challenger, - ctl_challenges: &GrandProductChallengeSet, - timing: &mut TimingTree, - abort_signal: Option>, -) -> Result<[StarkProofWithMetadata; NUM_TABLES]> -where - F: RichField + Extendable, - C: GenericConfig, -{ - let arithmetic_proof = timed!( - timing, - "prove Arithmetic STARK", - prove_single_table( - &all_stark.arithmetic_stark, - config, - &trace_poly_values[Table::Arithmetic as usize], - &trace_commitments[Table::Arithmetic as usize], - &ctl_data_per_table[Table::Arithmetic as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let byte_packing_proof = timed!( - timing, - "prove byte packing STARK", - prove_single_table( - &all_stark.byte_packing_stark, - config, - &trace_poly_values[Table::BytePacking as usize], - &trace_commitments[Table::BytePacking as usize], - &ctl_data_per_table[Table::BytePacking as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let cpu_proof = timed!( - timing, - "prove CPU STARK", - prove_single_table( - &all_stark.cpu_stark, - config, - &trace_poly_values[Table::Cpu as usize], - &trace_commitments[Table::Cpu as usize], - &ctl_data_per_table[Table::Cpu as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let keccak_proof = timed!( - timing, - "prove Keccak STARK", - prove_single_table( - &all_stark.keccak_stark, - config, - &trace_poly_values[Table::Keccak as usize], - &trace_commitments[Table::Keccak as usize], - &ctl_data_per_table[Table::Keccak as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let keccak_sponge_proof = timed!( - timing, - "prove Keccak sponge STARK", - prove_single_table( - &all_stark.keccak_sponge_stark, - config, - &trace_poly_values[Table::KeccakSponge as usize], - &trace_commitments[Table::KeccakSponge as usize], - &ctl_data_per_table[Table::KeccakSponge as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let logic_proof = timed!( - timing, - "prove logic STARK", - prove_single_table( - &all_stark.logic_stark, - config, - &trace_poly_values[Table::Logic as usize], - &trace_commitments[Table::Logic as usize], - &ctl_data_per_table[Table::Logic as usize], - ctl_challenges, - challenger, - timing, - abort_signal.clone(), - )? - ); - let memory_proof = timed!( - timing, - "prove memory STARK", - prove_single_table( - &all_stark.memory_stark, - config, - &trace_poly_values[Table::Memory as usize], - &trace_commitments[Table::Memory as usize], - &ctl_data_per_table[Table::Memory as usize], - ctl_challenges, - challenger, - timing, - abort_signal, - )? - ); - - Ok([ - arithmetic_proof, - byte_packing_proof, - cpu_proof, - keccak_proof, - keccak_sponge_proof, - logic_proof, - memory_proof, - ]) -} - -/// Computes a proof for a single STARK table, including: -/// - the initial state of the challenger, -/// - all the requires Merkle caps, -/// - all the required polynomial and FRI argument openings. -pub(crate) fn prove_single_table( - stark: &S, - config: &StarkConfig, - trace_poly_values: &[PolynomialValues], - trace_commitment: &PolynomialBatch, - ctl_data: &CtlData, - ctl_challenges: &GrandProductChallengeSet, - challenger: &mut Challenger, - timing: &mut TimingTree, - abort_signal: Option>, -) -> Result> -where - F: RichField + Extendable, - C: GenericConfig, - S: Stark, -{ - check_abort_signal(abort_signal.clone())?; - - // Clear buffered outputs. - let init_challenger_state = challenger.compact(); - - prove_with_commitment( - stark, - config, - trace_poly_values, - trace_commitment, - Some(ctl_data), - Some(ctl_challenges), - challenger, - &[], - timing, - ) - .map(|proof_with_pis| StarkProofWithMetadata { - proof: proof_with_pis.proof, - init_challenger_state, - }) -} - -/// Utility method that checks whether a kill signal has been emitted by one of the workers, -/// which will result in an early abort for all the other processes involved in the same set -/// of transactions. -pub fn check_abort_signal(abort_signal: Option>) -> Result<()> { - if let Some(signal) = abort_signal { - if signal.load(Ordering::Relaxed) { - return Err(anyhow!("Stopping job from abort signal.")); - } - } - - Ok(()) -} diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs deleted file mode 100644 index f3a8e1db4a..0000000000 --- a/evm/src/recursive_verifier.rs +++ /dev/null @@ -1,828 +0,0 @@ -use core::array::from_fn; -use core::fmt::Debug; - -use anyhow::Result; -use ethereum_types::{BigEndianHash, U256}; -use plonky2::field::extension::Extendable; -use plonky2::gates::exponentiation::ExponentiationGate; -use plonky2::gates::gate::GateRef; -use plonky2::gates::noop::NoopGate; -use plonky2::hash::hash_types::RichField; -use plonky2::hash::hashing::PlonkyPermutation; -use plonky2::iop::challenger::RecursiveChallenger; -use plonky2::iop::target::Target; -use plonky2::iop::witness::{PartialWitness, Witness, WitnessWrite}; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; -use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; -use plonky2::util::serialization::{ - Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, -}; -use plonky2_util::log2_ceil; -use starky::config::StarkConfig; -use starky::cross_table_lookup::{CrossTableLookup, CtlCheckVarsTarget}; -use starky::lookup::{GrandProductChallenge, GrandProductChallengeSet}; -use starky::proof::{StarkProofTarget, StarkProofWithMetadata}; -use starky::recursive_verifier::{ - add_virtual_stark_proof, set_stark_proof_target, verify_stark_proof_with_challenges_circuit, -}; -use starky::stark::Stark; - -use crate::all_stark::Table; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::memory::segments::Segment; -use crate::memory::VALUE_LIMBS; -use crate::proof::{ - BlockHashes, BlockHashesTarget, BlockMetadata, BlockMetadataTarget, ExtraBlockData, - ExtraBlockDataTarget, PublicValues, PublicValuesTarget, TrieRoots, TrieRootsTarget, -}; -use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; -use crate::witness::errors::ProgramError; - -pub(crate) struct PublicInputs> -{ - pub(crate) trace_cap: Vec>, - pub(crate) ctl_zs_first: Vec, - pub(crate) ctl_challenges: GrandProductChallengeSet, - pub(crate) challenger_state_before: P, - pub(crate) challenger_state_after: P, -} - -impl> PublicInputs { - pub(crate) fn from_vec(v: &[T], config: &StarkConfig) -> Self { - // TODO: Document magic number 4; probably comes from - // Ethereum 256 bits = 4 * Goldilocks 64 bits - let nelts = config.fri_config.num_cap_elements(); - let mut trace_cap = Vec::with_capacity(nelts); - for i in 0..nelts { - trace_cap.push(v[4 * i..4 * (i + 1)].to_vec()); - } - let mut iter = v.iter().copied().skip(4 * nelts); - let ctl_challenges = GrandProductChallengeSet { - challenges: (0..config.num_challenges) - .map(|_| GrandProductChallenge { - beta: iter.next().unwrap(), - gamma: iter.next().unwrap(), - }) - .collect(), - }; - let challenger_state_before = P::new(&mut iter); - let challenger_state_after = P::new(&mut iter); - let ctl_zs_first: Vec<_> = iter.collect(); - - Self { - trace_cap, - ctl_zs_first, - ctl_challenges, - challenger_state_before, - challenger_state_after, - } - } -} - -/// Represents a circuit which recursively verifies a STARK proof. -#[derive(Eq, PartialEq, Debug)] -pub(crate) struct StarkWrapperCircuit -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - pub(crate) circuit: CircuitData, - pub(crate) stark_proof_target: StarkProofTarget, - pub(crate) ctl_challenges_target: GrandProductChallengeSet, - pub(crate) init_challenger_state_target: - >::AlgebraicPermutation, - pub(crate) zero_target: Target, -} - -impl StarkWrapperCircuit -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - pub(crate) fn to_buffer( - &self, - buffer: &mut Vec, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult<()> { - buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; - buffer.write_target_vec(self.init_challenger_state_target.as_ref())?; - buffer.write_target(self.zero_target)?; - self.stark_proof_target.to_buffer(buffer)?; - self.ctl_challenges_target.to_buffer(buffer)?; - Ok(()) - } - - pub(crate) fn from_buffer( - buffer: &mut Buffer, - gate_serializer: &dyn GateSerializer, - generator_serializer: &dyn WitnessGeneratorSerializer, - ) -> IoResult { - let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; - let target_vec = buffer.read_target_vec()?; - let init_challenger_state_target = - >::AlgebraicPermutation::new(target_vec); - let zero_target = buffer.read_target()?; - let stark_proof_target = StarkProofTarget::from_buffer(buffer)?; - let ctl_challenges_target = GrandProductChallengeSet::from_buffer(buffer)?; - Ok(Self { - circuit, - stark_proof_target, - ctl_challenges_target, - init_challenger_state_target, - zero_target, - }) - } - - pub(crate) fn prove( - &self, - proof_with_metadata: &StarkProofWithMetadata, - ctl_challenges: &GrandProductChallengeSet, - ) -> Result> { - let mut inputs = PartialWitness::new(); - - set_stark_proof_target( - &mut inputs, - &self.stark_proof_target, - &proof_with_metadata.proof, - self.zero_target, - ); - - for (challenge_target, challenge) in self - .ctl_challenges_target - .challenges - .iter() - .zip(&ctl_challenges.challenges) - { - inputs.set_target(challenge_target.beta, challenge.beta); - inputs.set_target(challenge_target.gamma, challenge.gamma); - } - - inputs.set_target_arr( - self.init_challenger_state_target.as_ref(), - proof_with_metadata.init_challenger_state.as_ref(), - ); - - self.circuit.prove(inputs) - } -} - -/// Represents a circuit which recursively verifies a PLONK proof. -#[derive(Eq, PartialEq, Debug)] -pub(crate) struct PlonkWrapperCircuit -where - F: RichField + Extendable, - C: GenericConfig, -{ - pub(crate) circuit: CircuitData, - pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget, -} - -impl PlonkWrapperCircuit -where - F: RichField + Extendable, - C: GenericConfig, - C::Hasher: AlgebraicHasher, -{ - pub(crate) fn prove( - &self, - proof: &ProofWithPublicInputs, - ) -> Result> { - let mut inputs = PartialWitness::new(); - inputs.set_proof_with_pis_target(&self.proof_with_pis_target, proof); - self.circuit.prove(inputs) - } -} - -/// Returns the recursive STARK circuit. -pub(crate) fn recursive_stark_circuit< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - table: Table, - stark: &S, - degree_bits: usize, - cross_table_lookups: &[CrossTableLookup], - inner_config: &StarkConfig, - circuit_config: &CircuitConfig, - min_degree_bits: usize, -) -> StarkWrapperCircuit -where - C::Hasher: AlgebraicHasher, -{ - let mut builder = CircuitBuilder::::new(circuit_config.clone()); - let zero_target = builder.zero(); - - let num_lookup_columns = stark.num_lookup_helper_columns(inner_config); - let (total_num_helpers, num_ctl_zs, num_helpers_by_ctl) = - CrossTableLookup::num_ctl_helpers_zs_all( - cross_table_lookups, - *table, - inner_config.num_challenges, - stark.constraint_degree(), - ); - let num_ctl_helper_zs = num_ctl_zs + total_num_helpers; - - let stark_proof_target = add_virtual_stark_proof( - &mut builder, - stark, - inner_config, - degree_bits, - num_ctl_helper_zs, - num_ctl_zs, - ); - - builder.register_public_inputs( - &stark_proof_target - .trace_cap - .0 - .iter() - .flat_map(|h| h.elements) - .collect::>(), - ); - - let ctl_challenges_target = GrandProductChallengeSet { - challenges: (0..inner_config.num_challenges) - .map(|_| GrandProductChallenge { - beta: builder.add_virtual_public_input(), - gamma: builder.add_virtual_public_input(), - }) - .collect(), - }; - - let ctl_vars = CtlCheckVarsTarget::from_proof( - *table, - &stark_proof_target, - cross_table_lookups, - &ctl_challenges_target, - num_lookup_columns, - total_num_helpers, - &num_helpers_by_ctl, - ); - - let init_challenger_state_target = - >::AlgebraicPermutation::new(std::iter::from_fn(|| { - Some(builder.add_virtual_public_input()) - })); - let mut challenger = - RecursiveChallenger::::from_state(init_challenger_state_target); - let challenges = stark_proof_target.get_challenges::( - &mut builder, - &mut challenger, - Some(&ctl_challenges_target), - true, - inner_config, - ); - let challenger_state = challenger.compact(&mut builder); - builder.register_public_inputs(challenger_state.as_ref()); - - builder.register_public_inputs(stark_proof_target.openings.ctl_zs_first.as_ref().unwrap()); - - verify_stark_proof_with_challenges_circuit::( - &mut builder, - stark, - &stark_proof_target, - &[], // public inputs - challenges, - Some(&ctl_vars), - inner_config, - ); - - add_common_recursion_gates(&mut builder); - - // Pad to the minimum degree. - while log2_ceil(builder.num_gates()) < min_degree_bits { - builder.add_gate(NoopGate, vec![]); - } - - let circuit = builder.build::(); - StarkWrapperCircuit { - circuit, - stark_proof_target, - ctl_challenges_target, - init_challenger_state_target, - zero_target, - } -} - -/// Add gates that are sometimes used by recursive circuits, even if it's not actually used by this -/// particular recursive circuit. This is done for uniformity. We sometimes want all recursion -/// circuits to have the same gate set, so that we can do 1-of-n conditional recursion efficiently. -pub(crate) fn add_common_recursion_gates, const D: usize>( - builder: &mut CircuitBuilder, -) { - builder.add_gate_to_gate_set(GateRef::new(ExponentiationGate::new_from_config( - &builder.config, - ))); -} - -/// Recursive version of `get_memory_extra_looking_sum`. -pub(crate) fn get_memory_extra_looking_sum_circuit, const D: usize>( - builder: &mut CircuitBuilder, - public_values: &PublicValuesTarget, - challenge: GrandProductChallenge, -) -> Target { - let mut sum = builder.zero(); - - // Add metadata writes. - let block_fields_scalars = [ - ( - GlobalMetadata::BlockTimestamp, - public_values.block_metadata.block_timestamp, - ), - ( - GlobalMetadata::BlockNumber, - public_values.block_metadata.block_number, - ), - ( - GlobalMetadata::BlockDifficulty, - public_values.block_metadata.block_difficulty, - ), - ( - GlobalMetadata::BlockGasLimit, - public_values.block_metadata.block_gaslimit, - ), - ( - GlobalMetadata::BlockChainId, - public_values.block_metadata.block_chain_id, - ), - ( - GlobalMetadata::BlockGasUsed, - public_values.block_metadata.block_gas_used, - ), - ( - GlobalMetadata::BlockGasUsedBefore, - public_values.extra_block_data.gas_used_before, - ), - ( - GlobalMetadata::BlockGasUsedAfter, - public_values.extra_block_data.gas_used_after, - ), - ( - GlobalMetadata::TxnNumberBefore, - public_values.extra_block_data.txn_number_before, - ), - ( - GlobalMetadata::TxnNumberAfter, - public_values.extra_block_data.txn_number_after, - ), - ]; - - let beneficiary_random_base_fee_cur_hash_fields: [(GlobalMetadata, &[Target]); 4] = [ - ( - GlobalMetadata::BlockBeneficiary, - &public_values.block_metadata.block_beneficiary, - ), - ( - GlobalMetadata::BlockRandom, - &public_values.block_metadata.block_random, - ), - ( - GlobalMetadata::BlockBaseFee, - &public_values.block_metadata.block_base_fee, - ), - ( - GlobalMetadata::BlockCurrentHash, - &public_values.block_hashes.cur_hash, - ), - ]; - - let metadata_segment = - builder.constant(F::from_canonical_usize(Segment::GlobalMetadata.unscale())); - block_fields_scalars.map(|(field, target)| { - // Each of those fields fit in 32 bits, hence in a single Target. - sum = add_data_write( - builder, - challenge, - sum, - metadata_segment, - field.unscale(), - &[target], - ); - }); - - beneficiary_random_base_fee_cur_hash_fields.map(|(field, targets)| { - sum = add_data_write( - builder, - challenge, - sum, - metadata_segment, - field.unscale(), - targets, - ); - }); - - // Add block hashes writes. - let block_hashes_segment = - builder.constant(F::from_canonical_usize(Segment::BlockHashes.unscale())); - for i in 0..256 { - sum = add_data_write( - builder, - challenge, - sum, - block_hashes_segment, - i, - &public_values.block_hashes.prev_hashes[8 * i..8 * (i + 1)], - ); - } - - // Add block bloom filters writes. - let bloom_segment = - builder.constant(F::from_canonical_usize(Segment::GlobalBlockBloom.unscale())); - for i in 0..8 { - sum = add_data_write( - builder, - challenge, - sum, - bloom_segment, - i, - &public_values.block_metadata.block_bloom[i * 8..(i + 1) * 8], - ); - } - - // Add trie roots writes. - let trie_fields = [ - ( - GlobalMetadata::StateTrieRootDigestBefore, - public_values.trie_roots_before.state_root, - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - public_values.trie_roots_before.transactions_root, - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - public_values.trie_roots_before.receipts_root, - ), - ( - GlobalMetadata::StateTrieRootDigestAfter, - public_values.trie_roots_after.state_root, - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - public_values.trie_roots_after.transactions_root, - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - public_values.trie_roots_after.receipts_root, - ), - ]; - - trie_fields.map(|(field, targets)| { - sum = add_data_write( - builder, - challenge, - sum, - metadata_segment, - field.unscale(), - &targets, - ); - }); - - // Add kernel hash and kernel length. - let kernel_hash_limbs = h256_limbs::(KERNEL.code_hash); - let kernel_hash_targets: [Target; 8] = from_fn(|i| builder.constant(kernel_hash_limbs[i])); - sum = add_data_write( - builder, - challenge, - sum, - metadata_segment, - GlobalMetadata::KernelHash.unscale(), - &kernel_hash_targets, - ); - let kernel_len_target = builder.constant(F::from_canonical_usize(KERNEL.code.len())); - sum = add_data_write( - builder, - challenge, - sum, - metadata_segment, - GlobalMetadata::KernelLen.unscale(), - &[kernel_len_target], - ); - - sum -} - -fn add_data_write, const D: usize>( - builder: &mut CircuitBuilder, - challenge: GrandProductChallenge, - running_sum: Target, - segment: Target, - idx: usize, - val: &[Target], -) -> Target { - debug_assert!(val.len() <= VALUE_LIMBS); - let len = core::cmp::min(val.len(), VALUE_LIMBS); - - let row = builder.add_virtual_targets(13); - // is_read = false - builder.assert_zero(row[0]); - // context = 0 - builder.assert_zero(row[1]); - // segment - builder.connect(row[2], segment); - // virtual - let field_target = builder.constant(F::from_canonical_usize(idx)); - builder.connect(row[3], field_target); - - // values - for j in 0..len { - // connect the actual value limbs - builder.connect(row[4 + j], val[j]); - } - for j in len..VALUE_LIMBS { - // assert that the remaining limbs are 0 - builder.assert_zero(row[4 + j]); - } - - // timestamp = 1 - builder.assert_one(row[12]); - - let combined = challenge.combine_base_circuit(builder, &row); - let inverse = builder.inverse(combined); - builder.add(running_sum, inverse) -} - -pub(crate) fn add_virtual_public_values, const D: usize>( - builder: &mut CircuitBuilder, -) -> PublicValuesTarget { - let trie_roots_before = add_virtual_trie_roots(builder); - let trie_roots_after = add_virtual_trie_roots(builder); - let block_metadata = add_virtual_block_metadata(builder); - let block_hashes = add_virtual_block_hashes(builder); - let extra_block_data = add_virtual_extra_block_data(builder); - PublicValuesTarget { - trie_roots_before, - trie_roots_after, - block_metadata, - block_hashes, - extra_block_data, - } -} - -pub(crate) fn add_virtual_trie_roots, const D: usize>( - builder: &mut CircuitBuilder, -) -> TrieRootsTarget { - let state_root = builder.add_virtual_public_input_arr(); - let transactions_root = builder.add_virtual_public_input_arr(); - let receipts_root = builder.add_virtual_public_input_arr(); - TrieRootsTarget { - state_root, - transactions_root, - receipts_root, - } -} - -pub(crate) fn add_virtual_block_metadata, const D: usize>( - builder: &mut CircuitBuilder, -) -> BlockMetadataTarget { - let block_beneficiary = builder.add_virtual_public_input_arr(); - let block_timestamp = builder.add_virtual_public_input(); - let block_number = builder.add_virtual_public_input(); - let block_difficulty = builder.add_virtual_public_input(); - let block_random = builder.add_virtual_public_input_arr(); - let block_gaslimit = builder.add_virtual_public_input(); - let block_chain_id = builder.add_virtual_public_input(); - let block_base_fee = builder.add_virtual_public_input_arr(); - let block_gas_used = builder.add_virtual_public_input(); - let block_bloom = builder.add_virtual_public_input_arr(); - BlockMetadataTarget { - block_beneficiary, - block_timestamp, - block_number, - block_difficulty, - block_random, - block_gaslimit, - block_chain_id, - block_base_fee, - block_gas_used, - block_bloom, - } -} - -pub(crate) fn add_virtual_block_hashes, const D: usize>( - builder: &mut CircuitBuilder, -) -> BlockHashesTarget { - let prev_hashes = builder.add_virtual_public_input_arr(); - let cur_hash = builder.add_virtual_public_input_arr(); - BlockHashesTarget { - prev_hashes, - cur_hash, - } -} -pub(crate) fn add_virtual_extra_block_data, const D: usize>( - builder: &mut CircuitBuilder, -) -> ExtraBlockDataTarget { - let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); - let txn_number_before = builder.add_virtual_public_input(); - let txn_number_after = builder.add_virtual_public_input(); - let gas_used_before = builder.add_virtual_public_input(); - let gas_used_after = builder.add_virtual_public_input(); - ExtraBlockDataTarget { - checkpoint_state_trie_root, - txn_number_before, - txn_number_after, - gas_used_before, - gas_used_after, - } -} - -pub fn set_public_value_targets( - witness: &mut W, - public_values_target: &PublicValuesTarget, - public_values: &PublicValues, -) -> Result<(), ProgramError> -where - F: RichField + Extendable, - W: Witness, -{ - set_trie_roots_target( - witness, - &public_values_target.trie_roots_before, - &public_values.trie_roots_before, - ); - set_trie_roots_target( - witness, - &public_values_target.trie_roots_after, - &public_values.trie_roots_after, - ); - set_block_metadata_target( - witness, - &public_values_target.block_metadata, - &public_values.block_metadata, - )?; - set_block_hashes_target( - witness, - &public_values_target.block_hashes, - &public_values.block_hashes, - ); - set_extra_public_values_target( - witness, - &public_values_target.extra_block_data, - &public_values.extra_block_data, - )?; - - Ok(()) -} - -pub(crate) fn set_trie_roots_target( - witness: &mut W, - trie_roots_target: &TrieRootsTarget, - trie_roots: &TrieRoots, -) where - F: RichField + Extendable, - W: Witness, -{ - for (i, limb) in trie_roots.state_root.into_uint().0.into_iter().enumerate() { - witness.set_target( - trie_roots_target.state_root[2 * i], - F::from_canonical_u32(limb as u32), - ); - witness.set_target( - trie_roots_target.state_root[2 * i + 1], - F::from_canonical_u32((limb >> 32) as u32), - ); - } - - for (i, limb) in trie_roots - .transactions_root - .into_uint() - .0 - .into_iter() - .enumerate() - { - witness.set_target( - trie_roots_target.transactions_root[2 * i], - F::from_canonical_u32(limb as u32), - ); - witness.set_target( - trie_roots_target.transactions_root[2 * i + 1], - F::from_canonical_u32((limb >> 32) as u32), - ); - } - - for (i, limb) in trie_roots - .receipts_root - .into_uint() - .0 - .into_iter() - .enumerate() - { - witness.set_target( - trie_roots_target.receipts_root[2 * i], - F::from_canonical_u32(limb as u32), - ); - witness.set_target( - trie_roots_target.receipts_root[2 * i + 1], - F::from_canonical_u32((limb >> 32) as u32), - ); - } -} - -pub(crate) fn set_block_metadata_target( - witness: &mut W, - block_metadata_target: &BlockMetadataTarget, - block_metadata: &BlockMetadata, -) -> Result<(), ProgramError> -where - F: RichField + Extendable, - W: Witness, -{ - let beneficiary_limbs: [F; 5] = - u256_limbs::(U256::from_big_endian(&block_metadata.block_beneficiary.0))[..5] - .try_into() - .unwrap(); - witness.set_target_arr(&block_metadata_target.block_beneficiary, &beneficiary_limbs); - witness.set_target( - block_metadata_target.block_timestamp, - u256_to_u32(block_metadata.block_timestamp)?, - ); - witness.set_target( - block_metadata_target.block_number, - u256_to_u32(block_metadata.block_number)?, - ); - witness.set_target( - block_metadata_target.block_difficulty, - u256_to_u32(block_metadata.block_difficulty)?, - ); - witness.set_target_arr( - &block_metadata_target.block_random, - &h256_limbs(block_metadata.block_random), - ); - witness.set_target( - block_metadata_target.block_gaslimit, - u256_to_u32(block_metadata.block_gaslimit)?, - ); - witness.set_target( - block_metadata_target.block_chain_id, - u256_to_u32(block_metadata.block_chain_id)?, - ); - // Basefee fits in 2 limbs - let basefee = u256_to_u64(block_metadata.block_base_fee)?; - witness.set_target(block_metadata_target.block_base_fee[0], basefee.0); - witness.set_target(block_metadata_target.block_base_fee[1], basefee.1); - witness.set_target( - block_metadata_target.block_gas_used, - u256_to_u32(block_metadata.block_gas_used)?, - ); - let mut block_bloom_limbs = [F::ZERO; 64]; - for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { - limbs.copy_from_slice(&u256_limbs(block_metadata.block_bloom[i])); - } - witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs); - - Ok(()) -} - -pub(crate) fn set_block_hashes_target( - witness: &mut W, - block_hashes_target: &BlockHashesTarget, - block_hashes: &BlockHashes, -) where - F: RichField + Extendable, - W: Witness, -{ - for i in 0..256 { - let block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.prev_hashes[i]); - witness.set_target_arr( - &block_hashes_target.prev_hashes[8 * i..8 * (i + 1)], - &block_hash_limbs, - ); - } - let cur_block_hash_limbs: [F; 8] = h256_limbs::(block_hashes.cur_hash); - witness.set_target_arr(&block_hashes_target.cur_hash, &cur_block_hash_limbs); -} - -pub(crate) fn set_extra_public_values_target( - witness: &mut W, - ed_target: &ExtraBlockDataTarget, - ed: &ExtraBlockData, -) -> Result<(), ProgramError> -where - F: RichField + Extendable, - W: Witness, -{ - witness.set_target_arr( - &ed_target.checkpoint_state_trie_root, - &h256_limbs::(ed.checkpoint_state_trie_root), - ); - witness.set_target( - ed_target.txn_number_before, - u256_to_u32(ed.txn_number_before)?, - ); - witness.set_target( - ed_target.txn_number_after, - u256_to_u32(ed.txn_number_after)?, - ); - witness.set_target(ed_target.gas_used_before, u256_to_u32(ed.gas_used_before)?); - witness.set_target(ed_target.gas_used_after, u256_to_u32(ed.gas_used_after)?); - - Ok(()) -} diff --git a/evm/src/util.rs b/evm/src/util.rs deleted file mode 100644 index fdb5a98c3a..0000000000 --- a/evm/src/util.rs +++ /dev/null @@ -1,252 +0,0 @@ -use core::mem::{size_of, transmute_copy, ManuallyDrop}; - -use ethereum_types::{H160, H256, U256}; -use itertools::Itertools; -use num::BigUint; -use plonky2::field::extension::Extendable; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::iop::ext_target::ExtensionTarget; -use plonky2::util::transpose; - -use crate::witness::errors::ProgramError; - -/// Construct an integer from its constituent bits (in little-endian order) -pub(crate) fn limb_from_bits_le(iter: impl IntoIterator) -> P { - // TODO: This is technically wrong, as 1 << i won't be canonical for all fields... - iter.into_iter() - .enumerate() - .map(|(i, bit)| bit * P::Scalar::from_canonical_u64(1 << i)) - .sum() -} - -/// Construct an integer from its constituent bits (in little-endian order): recursive edition -pub(crate) fn limb_from_bits_le_recursive, const D: usize>( - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - iter: impl IntoIterator>, -) -> ExtensionTarget { - iter.into_iter() - .enumerate() - .fold(builder.zero_extension(), |acc, (i, bit)| { - // TODO: This is technically wrong, as 1 << i won't be canonical for all fields... - builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, acc) - }) -} - -/// Returns the lowest LE 32-bit limb of a `U256` as a field element, -/// and errors if the integer is actually greater. -pub(crate) fn u256_to_u32(u256: U256) -> Result { - if TryInto::::try_into(u256).is_err() { - return Err(ProgramError::IntegerTooLarge); - } - - Ok(F::from_canonical_u32(u256.low_u32())) -} - -/// Returns the lowest LE 64-bit word of a `U256` as two field elements -/// each storing a 32-bit limb, and errors if the integer is actually greater. -pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> { - if TryInto::::try_into(u256).is_err() { - return Err(ProgramError::IntegerTooLarge); - } - - Ok(( - F::from_canonical_u32(u256.low_u64() as u32), - F::from_canonical_u32((u256.low_u64() >> 32) as u32), - )) -} - -/// Safe alternative to `U256::as_usize()`, which errors in case of overflow instead of panicking. -pub(crate) fn u256_to_usize(u256: U256) -> Result { - u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) -} - -/// Converts a `U256` to a `u8`, erroring in case of overflow instead of panicking. -pub(crate) fn u256_to_u8(u256: U256) -> Result { - u256.try_into().map_err(|_| ProgramError::IntegerTooLarge) -} - -/// Converts a `U256` to a `bool`, erroring in case of overflow instead of panicking. -pub(crate) fn u256_to_bool(u256: U256) -> Result { - if u256 == U256::zero() { - Ok(false) - } else if u256 == U256::one() { - Ok(true) - } else { - Err(ProgramError::IntegerTooLarge) - } -} - -/// Converts a `U256` to a `H160`, erroring in case of overflow instead of panicking. -pub(crate) fn u256_to_h160(u256: U256) -> Result { - if u256.bits() / 8 > 20 { - return Err(ProgramError::IntegerTooLarge); - } - let mut bytes = [0u8; 32]; - u256.to_big_endian(&mut bytes); - Ok(H160( - bytes[12..] - .try_into() - .expect("This conversion cannot fail."), - )) -} - -/// Returns the 32-bit little-endian limbs of a `U256`. -pub(crate) fn u256_limbs(u256: U256) -> [F; 8] { - u256.0 - .into_iter() - .flat_map(|limb_64| { - let lo = limb_64 as u32; - let hi = (limb_64 >> 32) as u32; - [lo, hi] - }) - .map(F::from_canonical_u32) - .collect_vec() - .try_into() - .unwrap() -} - -/// Returns the 32-bit little-endian limbs of a `H256`. -pub(crate) fn h256_limbs(h256: H256) -> [F; 8] { - let mut temp_h256 = h256.0; - temp_h256.reverse(); - temp_h256 - .chunks(4) - .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) - .map(F::from_canonical_u32) - .collect_vec() - .try_into() - .unwrap() -} - -/// Returns the 32-bit limbs of a `U160`. -pub(crate) fn h160_limbs(h160: H160) -> [F; 5] { - h160.0 - .chunks(4) - .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) - .map(F::from_canonical_u32) - .collect_vec() - .try_into() - .unwrap() -} - -pub(crate) const fn indices_arr() -> [usize; N] { - let mut indices_arr = [0; N]; - let mut i = 0; - while i < N { - indices_arr[i] = i; - i += 1; - } - indices_arr -} - -pub(crate) unsafe fn transmute_no_compile_time_size_checks(value: T) -> U { - debug_assert_eq!(size_of::(), size_of::()); - // Need ManuallyDrop so that `value` is not dropped by this function. - let value = ManuallyDrop::new(value); - // Copy the bit pattern. The original value is no longer safe to use. - transmute_copy(&value) -} - -pub(crate) fn addmod(x: U256, y: U256, m: U256) -> U256 { - if m.is_zero() { - return m; - } - let x = u256_to_biguint(x); - let y = u256_to_biguint(y); - let m = u256_to_biguint(m); - biguint_to_u256((x + y) % m) -} - -pub(crate) fn mulmod(x: U256, y: U256, m: U256) -> U256 { - if m.is_zero() { - return m; - } - let x = u256_to_biguint(x); - let y = u256_to_biguint(y); - let m = u256_to_biguint(m); - biguint_to_u256(x * y % m) -} - -pub(crate) fn submod(x: U256, y: U256, m: U256) -> U256 { - if m.is_zero() { - return m; - } - let mut x = u256_to_biguint(x); - let y = u256_to_biguint(y); - let m = u256_to_biguint(m); - while x < y { - x += &m; - } - biguint_to_u256((x - y) % m) -} - -pub(crate) fn u256_to_biguint(x: U256) -> BigUint { - let mut bytes = [0u8; 32]; - x.to_little_endian(&mut bytes); - BigUint::from_bytes_le(&bytes) -} - -pub(crate) fn biguint_to_u256(x: BigUint) -> U256 { - let bytes = x.to_bytes_le(); - // This could panic if `bytes.len() > 32` but this is only - // used here with `BigUint` constructed from `U256`. - U256::from_little_endian(&bytes) -} - -pub(crate) fn mem_vec_to_biguint(x: &[U256]) -> BigUint { - BigUint::from_slice( - &x.iter() - .map(|&n| n.try_into().unwrap()) - .flat_map(|a: u128| { - [ - (a % (1 << 32)) as u32, - ((a >> 32) % (1 << 32)) as u32, - ((a >> 64) % (1 << 32)) as u32, - ((a >> 96) % (1 << 32)) as u32, - ] - }) - .collect::>(), - ) -} - -pub(crate) fn biguint_to_mem_vec(x: BigUint) -> Vec { - let num_limbs = ((x.bits() + 127) / 128) as usize; - - let mut digits = x.iter_u64_digits(); - - let mut mem_vec = Vec::with_capacity(num_limbs); - while let Some(lo) = digits.next() { - let hi = digits.next().unwrap_or(0); - mem_vec.push(U256::from(lo as u128 | (hi as u128) << 64)); - } - mem_vec -} - -pub(crate) fn h2u(h: H256) -> U256 { - U256::from_big_endian(&h.0) -} - -pub(crate) fn get_h160(slice: &[F]) -> H160 { - H160::from_slice( - &slice - .iter() - .rev() - .map(|x| x.to_canonical_u64() as u32) - .flat_map(|limb| limb.to_be_bytes()) - .collect_vec(), - ) -} - -pub(crate) fn get_h256(slice: &[F]) -> H256 { - H256::from_slice( - &slice - .iter() - .rev() - .map(|x| x.to_canonical_u64() as u32) - .flat_map(|limb| limb.to_be_bytes()) - .collect_vec(), - ) -} diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs deleted file mode 100644 index fd2af86388..0000000000 --- a/evm/src/verifier.rs +++ /dev/null @@ -1,421 +0,0 @@ -use anyhow::Result; -use ethereum_types::{BigEndianHash, U256}; -use itertools::Itertools; -use plonky2::field::extension::Extendable; -use plonky2::hash::hash_types::RichField; -use plonky2::plonk::config::GenericConfig; -use starky::config::StarkConfig; -use starky::cross_table_lookup::{get_ctl_vars_from_proofs, verify_cross_table_lookups}; -use starky::lookup::GrandProductChallenge; -use starky::stark::Stark; -use starky::verifier::verify_stark_proof_with_challenges; - -use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::memory::segments::Segment; -use crate::memory::VALUE_LIMBS; -use crate::proof::{AllProof, AllProofChallenges, PublicValues}; -use crate::util::h2u; - -pub fn verify_proof, C: GenericConfig, const D: usize>( - all_stark: &AllStark, - all_proof: AllProof, - config: &StarkConfig, -) -> Result<()> -where -{ - let AllProofChallenges { - stark_challenges, - ctl_challenges, - } = all_proof - .get_challenges(config) - .map_err(|_| anyhow::Error::msg("Invalid sampling of proof challenges."))?; - - let num_lookup_columns = all_stark.num_lookups_helper_columns(config); - - let AllStark { - arithmetic_stark, - byte_packing_stark, - cpu_stark, - keccak_stark, - keccak_sponge_stark, - logic_stark, - memory_stark, - cross_table_lookups, - } = all_stark; - - let ctl_vars_per_table = get_ctl_vars_from_proofs( - &all_proof.multi_proof, - cross_table_lookups, - &ctl_challenges, - &num_lookup_columns, - all_stark.arithmetic_stark.constraint_degree(), - ); - - let stark_proofs = &all_proof.multi_proof.stark_proofs; - - verify_stark_proof_with_challenges( - arithmetic_stark, - &stark_proofs[Table::Arithmetic as usize].proof, - &stark_challenges[Table::Arithmetic as usize], - Some(&ctl_vars_per_table[Table::Arithmetic as usize]), - &[], - config, - )?; - - verify_stark_proof_with_challenges( - byte_packing_stark, - &stark_proofs[Table::BytePacking as usize].proof, - &stark_challenges[Table::BytePacking as usize], - Some(&ctl_vars_per_table[Table::BytePacking as usize]), - &[], - config, - )?; - verify_stark_proof_with_challenges( - cpu_stark, - &stark_proofs[Table::Cpu as usize].proof, - &stark_challenges[Table::Cpu as usize], - Some(&ctl_vars_per_table[Table::Cpu as usize]), - &[], - config, - )?; - verify_stark_proof_with_challenges( - keccak_stark, - &stark_proofs[Table::Keccak as usize].proof, - &stark_challenges[Table::Keccak as usize], - Some(&ctl_vars_per_table[Table::Keccak as usize]), - &[], - config, - )?; - verify_stark_proof_with_challenges( - keccak_sponge_stark, - &stark_proofs[Table::KeccakSponge as usize].proof, - &stark_challenges[Table::KeccakSponge as usize], - Some(&ctl_vars_per_table[Table::KeccakSponge as usize]), - &[], - config, - )?; - verify_stark_proof_with_challenges( - logic_stark, - &stark_proofs[Table::Logic as usize].proof, - &stark_challenges[Table::Logic as usize], - Some(&ctl_vars_per_table[Table::Logic as usize]), - &[], - config, - )?; - verify_stark_proof_with_challenges( - memory_stark, - &stark_proofs[Table::Memory as usize].proof, - &stark_challenges[Table::Memory as usize], - Some(&ctl_vars_per_table[Table::Memory as usize]), - &[], - config, - )?; - - let public_values = all_proof.public_values; - - // Extra sums to add to the looked last value. - // Only necessary for the Memory values. - let mut extra_looking_sums = vec![vec![F::ZERO; config.num_challenges]; NUM_TABLES]; - - // Memory - extra_looking_sums[Table::Memory as usize] = (0..config.num_challenges) - .map(|i| get_memory_extra_looking_sum(&public_values, ctl_challenges.challenges[i])) - .collect_vec(); - - verify_cross_table_lookups::( - cross_table_lookups, - all_proof - .multi_proof - .stark_proofs - .map(|p| p.proof.openings.ctl_zs_first.unwrap()), - Some(&extra_looking_sums), - config, - ) -} - -/// Computes the extra product to multiply to the looked value. It contains memory operations not in the CPU trace: -/// - block metadata writes, -/// - trie roots writes. -pub(crate) fn get_memory_extra_looking_sum( - public_values: &PublicValues, - challenge: GrandProductChallenge, -) -> F -where - F: RichField + Extendable, -{ - let mut sum = F::ZERO; - - // Add metadata and tries writes. - let fields = [ - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian(&public_values.block_metadata.block_beneficiary.0), - ), - ( - GlobalMetadata::BlockTimestamp, - public_values.block_metadata.block_timestamp, - ), - ( - GlobalMetadata::BlockNumber, - public_values.block_metadata.block_number, - ), - ( - GlobalMetadata::BlockRandom, - public_values.block_metadata.block_random.into_uint(), - ), - ( - GlobalMetadata::BlockDifficulty, - public_values.block_metadata.block_difficulty, - ), - ( - GlobalMetadata::BlockGasLimit, - public_values.block_metadata.block_gaslimit, - ), - ( - GlobalMetadata::BlockChainId, - public_values.block_metadata.block_chain_id, - ), - ( - GlobalMetadata::BlockBaseFee, - public_values.block_metadata.block_base_fee, - ), - ( - GlobalMetadata::BlockCurrentHash, - h2u(public_values.block_hashes.cur_hash), - ), - ( - GlobalMetadata::BlockGasUsed, - public_values.block_metadata.block_gas_used, - ), - ( - GlobalMetadata::TxnNumberBefore, - public_values.extra_block_data.txn_number_before, - ), - ( - GlobalMetadata::TxnNumberAfter, - public_values.extra_block_data.txn_number_after, - ), - ( - GlobalMetadata::BlockGasUsedBefore, - public_values.extra_block_data.gas_used_before, - ), - ( - GlobalMetadata::BlockGasUsedAfter, - public_values.extra_block_data.gas_used_after, - ), - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(public_values.trie_roots_before.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(public_values.trie_roots_before.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(public_values.trie_roots_before.receipts_root), - ), - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(public_values.trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(public_values.trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(public_values.trie_roots_after.receipts_root), - ), - (GlobalMetadata::KernelHash, h2u(KERNEL.code_hash)), - (GlobalMetadata::KernelLen, KERNEL.code.len().into()), - ]; - - let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); - - fields.map(|(field, val)| { - // These fields are already scaled by their segment, and are in context 0 (kernel). - sum = add_data_write(challenge, segment, sum, field.unscale(), val) - }); - - // Add block bloom writes. - let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); - for index in 0..8 { - let val = public_values.block_metadata.block_bloom[index]; - sum = add_data_write(challenge, bloom_segment, sum, index, val); - } - - // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); - for index in 0..256 { - let val = h2u(public_values.block_hashes.prev_hashes[index]); - sum = add_data_write(challenge, block_hashes_segment, sum, index, val); - } - - sum -} - -fn add_data_write( - challenge: GrandProductChallenge, - segment: F, - running_sum: F, - index: usize, - val: U256, -) -> F -where - F: RichField + Extendable, -{ - let mut row = [F::ZERO; 13]; - row[0] = F::ZERO; // is_read - row[1] = F::ZERO; // context - row[2] = segment; - row[3] = F::from_canonical_usize(index); - - for j in 0..VALUE_LIMBS { - row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); - } - row[12] = F::ONE; // timestamp - running_sum + challenge.combine(row.iter()).inverse() -} - -#[cfg(debug_assertions)] -pub(crate) mod debug_utils { - use super::*; - - /// Output all the extra memory rows that don't appear in the CPU trace but are - /// necessary to correctly check the MemoryStark CTL. - pub(crate) fn get_memory_extra_looking_values( - public_values: &PublicValues, - ) -> Vec> - where - F: RichField + Extendable, - { - // Add metadata and tries writes. - let fields = [ - ( - GlobalMetadata::BlockBeneficiary, - U256::from_big_endian(&public_values.block_metadata.block_beneficiary.0), - ), - ( - GlobalMetadata::BlockTimestamp, - public_values.block_metadata.block_timestamp, - ), - ( - GlobalMetadata::BlockNumber, - public_values.block_metadata.block_number, - ), - ( - GlobalMetadata::BlockRandom, - public_values.block_metadata.block_random.into_uint(), - ), - ( - GlobalMetadata::BlockDifficulty, - public_values.block_metadata.block_difficulty, - ), - ( - GlobalMetadata::BlockGasLimit, - public_values.block_metadata.block_gaslimit, - ), - ( - GlobalMetadata::BlockChainId, - public_values.block_metadata.block_chain_id, - ), - ( - GlobalMetadata::BlockBaseFee, - public_values.block_metadata.block_base_fee, - ), - ( - GlobalMetadata::BlockCurrentHash, - h2u(public_values.block_hashes.cur_hash), - ), - ( - GlobalMetadata::BlockGasUsed, - public_values.block_metadata.block_gas_used, - ), - ( - GlobalMetadata::TxnNumberBefore, - public_values.extra_block_data.txn_number_before, - ), - ( - GlobalMetadata::TxnNumberAfter, - public_values.extra_block_data.txn_number_after, - ), - ( - GlobalMetadata::BlockGasUsedBefore, - public_values.extra_block_data.gas_used_before, - ), - ( - GlobalMetadata::BlockGasUsedAfter, - public_values.extra_block_data.gas_used_after, - ), - ( - GlobalMetadata::StateTrieRootDigestBefore, - h2u(public_values.trie_roots_before.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestBefore, - h2u(public_values.trie_roots_before.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestBefore, - h2u(public_values.trie_roots_before.receipts_root), - ), - ( - GlobalMetadata::StateTrieRootDigestAfter, - h2u(public_values.trie_roots_after.state_root), - ), - ( - GlobalMetadata::TransactionTrieRootDigestAfter, - h2u(public_values.trie_roots_after.transactions_root), - ), - ( - GlobalMetadata::ReceiptTrieRootDigestAfter, - h2u(public_values.trie_roots_after.receipts_root), - ), - (GlobalMetadata::KernelHash, h2u(KERNEL.code_hash)), - (GlobalMetadata::KernelLen, KERNEL.code.len().into()), - ]; - - let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); - let mut extra_looking_rows = Vec::new(); - - fields.map(|(field, val)| { - extra_looking_rows.push(add_extra_looking_row(segment, field.unscale(), val)) - }); - - // Add block bloom writes. - let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); - for index in 0..8 { - let val = public_values.block_metadata.block_bloom[index]; - extra_looking_rows.push(add_extra_looking_row(bloom_segment, index, val)); - } - - // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); - for index in 0..256 { - let val = h2u(public_values.block_hashes.prev_hashes[index]); - extra_looking_rows.push(add_extra_looking_row(block_hashes_segment, index, val)); - } - - extra_looking_rows - } - - fn add_extra_looking_row(segment: F, index: usize, val: U256) -> Vec - where - F: RichField + Extendable, - { - let mut row = vec![F::ZERO; 13]; - row[0] = F::ZERO; // is_read - row[1] = F::ZERO; // context - row[2] = segment; - row[3] = F::from_canonical_usize(index); - - for j in 0..VALUE_LIMBS { - row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); - } - row[12] = F::ONE; // timestamp - row - } -} diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs deleted file mode 100644 index 1b266aefde..0000000000 --- a/evm/src/witness/errors.rs +++ /dev/null @@ -1,41 +0,0 @@ -use ethereum_types::U256; - -#[derive(Debug)] -pub enum ProgramError { - OutOfGas, - InvalidOpcode, - StackUnderflow, - InvalidRlp, - InvalidJumpDestination, - InvalidJumpiDestination, - StackOverflow, - KernelPanic, - MemoryError(MemoryError), - GasLimitError, - InterpreterError, - IntegerTooLarge, - ProverInputError(ProverInputError), - UnknownContractCode, -} - -#[allow(clippy::enum_variant_names)] -#[derive(Debug)] -pub enum MemoryError { - ContextTooLarge { context: U256 }, - SegmentTooLarge { segment: U256 }, - VirtTooLarge { virt: U256 }, -} - -#[derive(Debug)] -pub enum ProverInputError { - OutOfMptData, - OutOfRlpData, - OutOfWithdrawalData, - CodeHashNotFound, - InvalidMptInput, - InvalidInput, - InvalidFunction, - NumBitsError, - InvalidJumpDestination, - InvalidJumpdestSimulation, -} diff --git a/evm/src/witness/gas.rs b/evm/src/witness/gas.rs deleted file mode 100644 index 54597a3ebc..0000000000 --- a/evm/src/witness/gas.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::witness::operation::Operation; - -pub(crate) const KERNEL_ONLY_INSTR: u64 = 0; -pub(crate) const G_JUMPDEST: u64 = 1; -pub(crate) const G_BASE: u64 = 2; -pub(crate) const G_VERYLOW: u64 = 3; -pub(crate) const G_LOW: u64 = 5; -pub(crate) const G_MID: u64 = 8; -pub(crate) const G_HIGH: u64 = 10; - -pub(crate) const fn gas_to_charge(op: Operation) -> u64 { - use crate::arithmetic::BinaryOperator::*; - use crate::arithmetic::TernaryOperator::*; - use crate::witness::operation::Operation::*; - match op { - Iszero => G_VERYLOW, - Not => G_VERYLOW, - Syscall(_, _, _) => KERNEL_ONLY_INSTR, - Eq => G_VERYLOW, - BinaryLogic(_) => G_VERYLOW, - BinaryArithmetic(Add) => G_VERYLOW, - BinaryArithmetic(Mul) => G_LOW, - BinaryArithmetic(Sub) => G_VERYLOW, - BinaryArithmetic(Div) => G_LOW, - BinaryArithmetic(Mod) => G_LOW, - BinaryArithmetic(Lt) => G_VERYLOW, - BinaryArithmetic(Gt) => G_VERYLOW, - BinaryArithmetic(Byte) => G_VERYLOW, - BinaryArithmetic(Shl) => G_VERYLOW, - BinaryArithmetic(Shr) => G_VERYLOW, - BinaryArithmetic(AddFp254) => KERNEL_ONLY_INSTR, - BinaryArithmetic(MulFp254) => KERNEL_ONLY_INSTR, - BinaryArithmetic(SubFp254) => KERNEL_ONLY_INSTR, - TernaryArithmetic(AddMod) => G_MID, - TernaryArithmetic(MulMod) => G_MID, - TernaryArithmetic(SubMod) => KERNEL_ONLY_INSTR, - KeccakGeneral => KERNEL_ONLY_INSTR, - ProverInput => KERNEL_ONLY_INSTR, - Pop => G_BASE, - Jump => G_MID, - Jumpi => G_HIGH, - Pc => G_BASE, - Jumpdest => G_JUMPDEST, - Push(0) => G_BASE, - Push(1..) => G_VERYLOW, - Dup(_) => G_VERYLOW, - Swap(_) => G_VERYLOW, - GetContext => KERNEL_ONLY_INSTR, - SetContext => KERNEL_ONLY_INSTR, - Mload32Bytes => KERNEL_ONLY_INSTR, - Mstore32Bytes(_) => KERNEL_ONLY_INSTR, - ExitKernel => KERNEL_ONLY_INSTR, - MloadGeneral => KERNEL_ONLY_INSTR, - MstoreGeneral => KERNEL_ONLY_INSTR, - } -} diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs deleted file mode 100644 index e6cb14f987..0000000000 --- a/evm/src/witness/memory.rs +++ /dev/null @@ -1,282 +0,0 @@ -use ethereum_types::U256; - -use crate::cpu::membus::{NUM_CHANNELS, NUM_GP_CHANNELS}; - -#[derive(Clone, Copy, Debug)] -pub(crate) enum MemoryChannel { - Code, - GeneralPurpose(usize), - PartialChannel, -} - -use MemoryChannel::{Code, GeneralPurpose, PartialChannel}; - -use super::operation::CONTEXT_SCALING_FACTOR; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; -use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; -use crate::witness::errors::ProgramError; -use crate::witness::errors::ProgramError::MemoryError; - -impl MemoryChannel { - pub(crate) fn index(&self) -> usize { - match *self { - Code => 0, - GeneralPurpose(n) => { - assert!(n < NUM_GP_CHANNELS); - n + 1 - } - PartialChannel => NUM_GP_CHANNELS + 1, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub(crate) struct MemoryAddress { - pub(crate) context: usize, - pub(crate) segment: usize, - pub(crate) virt: usize, -} - -impl MemoryAddress { - pub(crate) const fn new(context: usize, segment: Segment, virt: usize) -> Self { - Self { - context, - // segment is scaled - segment: segment.unscale(), - virt, - } - } - - pub(crate) fn new_u256s( - context: U256, - segment: U256, - virt: U256, - ) -> Result { - if context.bits() > 32 { - return Err(MemoryError(ContextTooLarge { context })); - } - if segment >= Segment::COUNT.into() { - return Err(MemoryError(SegmentTooLarge { segment })); - } - if virt.bits() > 32 { - return Err(MemoryError(VirtTooLarge { virt })); - } - - // Calling `as_usize` here is safe as those have been checked above. - Ok(Self { - context: context.as_usize(), - segment: segment.as_usize(), - virt: virt.as_usize(), - }) - } - - /// Creates a new `MemoryAddress` from a bundled address fitting a `U256`. - /// It will recover the virtual offset as the lowest 32-bit limb, the segment - /// as the next limb, and the context as the next one. - pub(crate) fn new_bundle(addr: U256) -> Result { - let virt = addr.low_u32().into(); - let segment = (addr >> SEGMENT_SCALING_FACTOR).low_u32().into(); - let context = (addr >> CONTEXT_SCALING_FACTOR).low_u32().into(); - - Self::new_u256s(context, segment, virt) - } - - pub(crate) fn increment(&mut self) { - self.virt = self.virt.saturating_add(1); - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum MemoryOpKind { - Read, - Write, -} - -#[derive(Clone, Copy, Debug)] -pub(crate) struct MemoryOp { - /// true if this is an actual memory operation, or false if it's a padding row. - pub filter: bool, - pub timestamp: usize, - pub address: MemoryAddress, - pub kind: MemoryOpKind, - pub value: U256, -} - -pub(crate) static DUMMY_MEMOP: MemoryOp = MemoryOp { - filter: false, - timestamp: 0, - address: MemoryAddress { - context: 0, - segment: 0, - virt: 0, - }, - kind: MemoryOpKind::Read, - value: U256::zero(), -}; - -impl MemoryOp { - pub(crate) fn new( - channel: MemoryChannel, - clock: usize, - address: MemoryAddress, - kind: MemoryOpKind, - value: U256, - ) -> Self { - let timestamp = clock * NUM_CHANNELS + channel.index(); - MemoryOp { - filter: true, - timestamp, - address, - kind, - value, - } - } - - pub(crate) const fn new_dummy_read( - address: MemoryAddress, - timestamp: usize, - value: U256, - ) -> Self { - Self { - filter: false, - timestamp, - address, - kind: MemoryOpKind::Read, - value, - } - } - - pub(crate) const fn sorting_key(&self) -> (usize, usize, usize, usize) { - ( - self.address.context, - self.address.segment, - self.address.virt, - self.timestamp, - ) - } -} - -#[derive(Clone, Debug)] -pub(crate) struct MemoryState { - pub(crate) contexts: Vec, -} - -impl MemoryState { - pub(crate) fn new(kernel_code: &[u8]) -> Self { - let code_u256s = kernel_code.iter().map(|&x| x.into()).collect(); - let mut result = Self::default(); - result.contexts[0].segments[Segment::Code.unscale()].content = code_u256s; - result - } - - pub(crate) fn apply_ops(&mut self, ops: &[MemoryOp]) { - for &op in ops { - let MemoryOp { - address, - kind, - value, - .. - } = op; - if kind == MemoryOpKind::Write { - self.set(address, value); - } - } - } - - pub(crate) fn get(&self, address: MemoryAddress) -> U256 { - if address.context >= self.contexts.len() { - return U256::zero(); - } - - let segment = Segment::all()[address.segment]; - - if let Some(constant) = Segment::constant(&segment, address.virt) { - return constant; - } - - let val = self.contexts[address.context].segments[address.segment].get(address.virt); - assert!( - val.bits() <= segment.bit_range(), - "Value {} exceeds {:?} range of {} bits", - val, - segment, - segment.bit_range() - ); - val - } - - pub(crate) fn set(&mut self, address: MemoryAddress, val: U256) { - while address.context >= self.contexts.len() { - self.contexts.push(MemoryContextState::default()); - } - - let segment = Segment::all()[address.segment]; - - if let Some(constant) = Segment::constant(&segment, address.virt) { - assert!( - constant == val, - "Attempting to set constant {} to incorrect value", - address.virt - ); - return; - } - assert!( - val.bits() <= segment.bit_range(), - "Value {} exceeds {:?} range of {} bits", - val, - segment, - segment.bit_range() - ); - self.contexts[address.context].segments[address.segment].set(address.virt, val); - } - - // These fields are already scaled by their respective segment. - pub(crate) fn read_global_metadata(&self, field: GlobalMetadata) -> U256 { - self.get(MemoryAddress::new_bundle(U256::from(field as usize)).unwrap()) - } -} - -impl Default for MemoryState { - fn default() -> Self { - Self { - // We start with an initial context for the kernel. - contexts: vec![MemoryContextState::default()], - } - } -} - -#[derive(Clone, Debug)] -pub(crate) struct MemoryContextState { - /// The content of each memory segment. - pub(crate) segments: [MemorySegmentState; Segment::COUNT], -} - -impl Default for MemoryContextState { - fn default() -> Self { - Self { - segments: std::array::from_fn(|_| MemorySegmentState::default()), - } - } -} - -#[derive(Clone, Default, Debug)] -pub(crate) struct MemorySegmentState { - pub(crate) content: Vec, -} - -impl MemorySegmentState { - pub(crate) fn get(&self, virtual_addr: usize) -> U256 { - self.content - .get(virtual_addr) - .copied() - .unwrap_or(U256::zero()) - } - - pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) { - if virtual_addr >= self.content.len() { - self.content.resize(virtual_addr + 1, U256::zero()); - } - self.content[virtual_addr] = value; - } -} diff --git a/evm/src/witness/mod.rs b/evm/src/witness/mod.rs deleted file mode 100644 index a38a552299..0000000000 --- a/evm/src/witness/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub(crate) mod errors; -pub(crate) mod gas; -pub(crate) mod memory; -pub(crate) mod operation; -pub(crate) mod state; -pub(crate) mod traces; -pub mod transition; -pub(crate) mod util; diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs deleted file mode 100644 index 4e6271d3b3..0000000000 --- a/evm/src/witness/operation.rs +++ /dev/null @@ -1,1003 +0,0 @@ -use ethereum_types::{BigEndianHash, U256}; -use itertools::Itertools; -use keccak_hash::keccak; -use plonky2::field::types::Field; - -use super::util::{ - byte_packing_log, byte_unpacking_log, mem_read_with_log, mem_write_log, - mem_write_partial_log_and_fill, push_no_write, push_with_write, -}; -use crate::arithmetic::BinaryOperator; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::assembler::BYTES_PER_OFFSET; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff; -use crate::cpu::stack::MAX_USER_STACK_SIZE; -use crate::extension_tower::BN_BASE; -use crate::generation::state::GenerationState; -use crate::memory::segments::Segment; -use crate::util::u256_to_usize; -use crate::witness::errors::MemoryError::VirtTooLarge; -use crate::witness::errors::ProgramError; -use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; -use crate::witness::operation::MemoryChannel::GeneralPurpose; -use crate::witness::transition::fill_stack_fields; -use crate::witness::util::{ - keccak_sponge_log, mem_read_gp_with_log_and_fill, mem_write_gp_log_and_fill, - stack_pop_with_log_and_fill, -}; -use crate::{arithmetic, logic}; - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub(crate) enum Operation { - Iszero, - Not, - Syscall(u8, usize, bool), // (syscall number, minimum stack length, increases stack length) - Eq, - BinaryLogic(logic::Op), - BinaryArithmetic(arithmetic::BinaryOperator), - TernaryArithmetic(arithmetic::TernaryOperator), - KeccakGeneral, - ProverInput, - Pop, - Jump, - Jumpi, - Pc, - Jumpdest, - Push(u8), - Dup(u8), - Swap(u8), - GetContext, - SetContext, - Mload32Bytes, - Mstore32Bytes(u8), - ExitKernel, - MloadGeneral, - MstoreGeneral, -} - -// Contexts in the kernel are shifted by 2^64, so that they can be combined with -// the segment and virtual address components in a single U256 word. -pub(crate) const CONTEXT_SCALING_FACTOR: usize = 64; - -/// Adds a CPU row filled with the two inputs and the output of a logic operation. -/// Generates a new logic operation and adds it to the vector of operation in `LogicStark`. -/// Adds three memory read operations to `MemoryStark`: for the two inputs and the output. -pub(crate) fn generate_binary_logic_op( - op: logic::Op, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(in0, _), (in1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let operation = logic::Operation::new(op, in0, in1); - - push_no_write(state, operation.result); - - state.traces.push_logic(operation); - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_binary_arithmetic_op( - operator: arithmetic::BinaryOperator, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(input0, _), (input1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let operation = arithmetic::Operation::binary(operator, input0, input1); - - if operator == arithmetic::BinaryOperator::AddFp254 - || operator == arithmetic::BinaryOperator::MulFp254 - || operator == arithmetic::BinaryOperator::SubFp254 - { - let channel = &mut row.mem_channels[2]; - - let val_limbs: [u64; 4] = BN_BASE.0; - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - } - - push_no_write(state, operation.result()); - - state.traces.push_arithmetic(operation); - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_ternary_arithmetic_op( - operator: arithmetic::TernaryOperator, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(input0, _), (input1, log_in1), (input2, log_in2)] = - stack_pop_with_log_and_fill::<3, _>(state, &mut row)?; - let operation = arithmetic::Operation::ternary(operator, input0, input1, input2); - - push_no_write(state, operation.result()); - - state.traces.push_arithmetic(operation); - state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_keccak_general( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let len = u256_to_usize(len)?; - - let base_address = MemoryAddress::new_bundle(addr)?; - let input = (0..len) - .map(|i| { - let address = MemoryAddress { - virt: base_address.virt.saturating_add(i), - ..base_address - }; - let val = state.memory.get(address); - val.low_u32() as u8 - }) - .collect_vec(); - log::debug!("Hashing {:?}", input); - - let hash = keccak(&input); - push_no_write(state, hash.into_uint()); - - keccak_sponge_log(state, base_address, input); - - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_prover_input( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let pc = state.registers.program_counter; - let input_fn = &KERNEL.prover_inputs[&pc]; - let input = state.prover_input(input_fn)?; - let opcode = 0x49.into(); - // `ArithmeticStark` range checks `mem_channels[0]`, which contains - // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and - // next_row's `mem_channels[0]` which contains the next top of the stack. - // Our goal here is to range-check the input, in the next stack top. - let range_check_op = arithmetic::Operation::range_check( - state.registers.stack_top, - U256::from(0), - U256::from(0), - opcode, - input, - ); - - push_with_write(state, &mut row, input)?; - - state.traces.push_arithmetic(range_check_op); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_pop( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(_, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - - let diff = row.stack_len - F::ONE; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - row.general.stack_mut().stack_inv_aux_2 = F::ONE; - state.registers.is_stack_top_read = true; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_cpu(row); - - Ok(()) -} - -pub(crate) fn generate_jump( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(dst, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - - let dst: u32 = dst - .try_into() - .map_err(|_| ProgramError::InvalidJumpDestination)?; - - let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill( - NUM_GP_CHANNELS - 1, - MemoryAddress::new(state.registers.context, Segment::JumpdestBits, dst as usize), - state, - &mut row, - ); - - row.mem_channels[1].value[0] = F::ONE; - - if state.registers.is_kernel { - // Don't actually do the read, just set the address, etc. - let channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1]; - channel.used = F::ZERO; - channel.value[0] = F::ONE; - } else { - if jumpdest_bit != U256::one() { - return Err(ProgramError::InvalidJumpDestination); - } - state.traces.push_memory(jumpdest_bit_log); - } - - // Extra fields required by the constraints. - row.general.jumps_mut().should_jump = F::ONE; - row.general.jumps_mut().cond_sum_pinv = F::ONE; - - let diff = row.stack_len - F::ONE; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_cpu(row); - state.jump_to(dst as usize)?; - Ok(()) -} - -pub(crate) fn generate_jumpi( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(dst, _), (cond, log_cond)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - - let should_jump = !cond.is_zero(); - if should_jump { - row.general.jumps_mut().should_jump = F::ONE; - let cond_sum_u64 = cond - .0 - .into_iter() - .map(|limb| ((limb as u32) as u64) + (limb >> 32)) - .sum(); - let cond_sum = F::from_canonical_u64(cond_sum_u64); - row.general.jumps_mut().cond_sum_pinv = cond_sum.inverse(); - - let dst: u32 = dst - .try_into() - .map_err(|_| ProgramError::InvalidJumpiDestination)?; - state.jump_to(dst as usize)?; - } else { - row.general.jumps_mut().should_jump = F::ZERO; - row.general.jumps_mut().cond_sum_pinv = F::ZERO; - state.registers.program_counter += 1; - } - - let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill( - NUM_GP_CHANNELS - 1, - MemoryAddress::new( - state.registers.context, - Segment::JumpdestBits, - dst.low_u32() as usize, - ), - state, - &mut row, - ); - if !should_jump || state.registers.is_kernel { - // Don't actually do the read, just set the address, etc. - let channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1]; - channel.used = F::ZERO; - channel.value[0] = F::ONE; - } else { - if jumpdest_bit != U256::one() { - return Err(ProgramError::InvalidJumpiDestination); - } - state.traces.push_memory(jumpdest_bit_log); - } - - let diff = row.stack_len - F::TWO; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_memory(log_cond); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_pc( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - push_with_write(state, &mut row, state.registers.program_counter.into())?; - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_jumpdest( - state: &mut GenerationState, - row: CpuColumnsView, -) -> Result<(), ProgramError> { - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_get_context( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - // Same logic as push_with_write, but we have to use channel 3 for stack constraint reasons. - let write = if state.registers.stack_len == 0 { - None - } else { - let address = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1, - ); - let res = mem_write_gp_log_and_fill(2, address, state, &mut row, state.registers.stack_top); - Some(res) - }; - push_no_write( - state, - // The fetched value needs to be scaled before being pushed. - U256::from(state.registers.context) << CONTEXT_SCALING_FACTOR, - ); - if let Some(log) = write { - state.traces.push_memory(log); - } - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_set_context( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(ctx, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - - let sp_to_save = state.registers.stack_len.into(); - - let old_ctx = state.registers.context; - // The popped value needs to be scaled down. - let new_ctx = u256_to_usize(ctx >> CONTEXT_SCALING_FACTOR)?; - - let sp_field = ContextMetadata::StackSize.unscale(); - let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); - let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); - - // This channel will hold in limb 0 and 1 the one-limb value of two separate memory operations: - // the old stack pointer write and the new stack pointer read. - // Channels only matter for time stamps: the write must happen before the read. - let log_write_old_sp = mem_write_log(GeneralPurpose(1), old_sp_addr, state, sp_to_save); - let (new_sp, log_read_new_sp) = if old_ctx == new_ctx { - let op = MemoryOp::new( - MemoryChannel::GeneralPurpose(2), - state.traces.clock(), - new_sp_addr, - MemoryOpKind::Read, - sp_to_save, - ); - (sp_to_save, op) - } else { - mem_read_with_log(GeneralPurpose(2), new_sp_addr, state) - }; - - // If the new stack isn't empty, read stack_top from memory. - let new_sp = u256_to_usize(new_sp)?; - if new_sp > 0 { - // Set up columns to disable the channel if it *is* empty. - let new_sp_field = F::from_canonical_usize(new_sp); - if let Some(inv) = new_sp_field.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - row.general.stack_mut().stack_inv_aux_2 = F::ONE; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - row.general.stack_mut().stack_inv_aux_2 = F::ZERO; - } - - let new_top_addr = MemoryAddress::new(new_ctx, Segment::Stack, new_sp - 1); - let (new_top, log_read_new_top) = - mem_read_gp_with_log_and_fill(2, new_top_addr, state, &mut row); - state.registers.stack_top = new_top; - state.traces.push_memory(log_read_new_top); - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.registers.context = new_ctx; - state.registers.stack_len = new_sp; - state.traces.push_memory(log_write_old_sp); - state.traces.push_memory(log_read_new_sp); - state.traces.push_cpu(row); - - Ok(()) -} - -pub(crate) fn generate_push( - n: u8, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let code_context = state.registers.code_context(); - let num_bytes = n as usize; - if num_bytes > 32 { - // The call to `U256::from_big_endian()` would panic. - return Err(ProgramError::IntegerTooLarge); - } - let initial_offset = state.registers.program_counter + 1; - - let base_address = MemoryAddress::new(code_context, Segment::Code, initial_offset); - // First read val without going through `mem_read_with_log` type methods, so we can pass it - // to stack_push_log_and_fill. - let bytes = (0..num_bytes) - .map(|i| { - state - .memory - .get(MemoryAddress { - virt: base_address.virt + i, - ..base_address - }) - .low_u32() as u8 - }) - .collect_vec(); - - let val = U256::from_big_endian(&bytes); - push_with_write(state, &mut row, val)?; - - byte_packing_log(state, base_address, bytes); - - state.traces.push_cpu(row); - - Ok(()) -} - -// This instruction is special. The order of the operations are: -// - Write `stack_top` at `stack[stack_len - 1]` -// - Read `val` at `stack[stack_len - 1 - n]` -// - Update `stack_top` with `val` and add 1 to `stack_len` -// Since the write must happen before the read, the normal way of assigning -// GP channels doesn't work and we must handle them manually. -pub(crate) fn generate_dup( - n: u8, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - // Same logic as in `push_with_write`, but we use the channel GP(0) instead. - if !state.registers.is_kernel && state.registers.stack_len >= MAX_USER_STACK_SIZE { - return Err(ProgramError::StackOverflow); - } - if n as usize >= state.registers.stack_len { - return Err(ProgramError::StackUnderflow); - } - let stack_top = state.registers.stack_top; - let address = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1, - ); - let log_push = mem_write_gp_log_and_fill(1, address, state, &mut row, stack_top); - state.traces.push_memory(log_push); - - let other_addr = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1 - n as usize, - ); - - // If n = 0, we read a value that hasn't been written to memory: the corresponding write - // is buffered in the mem_ops queue, but hasn't been applied yet. - let (val, log_read) = if n == 0 { - let op = MemoryOp::new( - MemoryChannel::GeneralPurpose(2), - state.traces.clock(), - other_addr, - MemoryOpKind::Read, - stack_top, - ); - - let channel = &mut row.mem_channels[2]; - assert_eq!(channel.used, F::ZERO); - channel.used = F::ONE; - channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_usize(other_addr.context); - channel.addr_segment = F::from_canonical_usize(other_addr.segment); - channel.addr_virtual = F::from_canonical_usize(other_addr.virt); - let val_limbs: [u64; 4] = state.registers.stack_top.0; - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - - (stack_top, op) - } else { - mem_read_gp_with_log_and_fill(2, other_addr, state, &mut row) - }; - push_no_write(state, val); - - state.traces.push_memory(log_read); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_swap( - n: u8, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let other_addr_lo = state - .registers - .stack_len - .checked_sub(2 + (n as usize)) - .ok_or(ProgramError::StackUnderflow)?; - let other_addr = MemoryAddress::new(state.registers.context, Segment::Stack, other_addr_lo); - - let [(in0, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let (in1, log_in1) = mem_read_gp_with_log_and_fill(1, other_addr, state, &mut row); - let log_out0 = mem_write_gp_log_and_fill(2, other_addr, state, &mut row, in0); - push_no_write(state, in1); - - state.traces.push_memory(log_in1); - state.traces.push_memory(log_out0); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_not( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(x, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let result = !x; - push_no_write(state, result); - - // This is necessary for the stack constraints for POP, - // since the two flags are combined. - let diff = row.stack_len - F::ONE; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_iszero( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(x, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let is_zero = x.is_zero(); - let result = { - let t: u64 = is_zero.into(); - t.into() - }; - - generate_pinv_diff(x, U256::zero(), &mut row); - - push_no_write(state, result); - state.traces.push_cpu(row); - Ok(()) -} - -fn append_shift( - state: &mut GenerationState, - mut row: CpuColumnsView, - is_shl: bool, - input0: U256, - input1: U256, - log_in1: MemoryOp, - result: U256, -) -> Result<(), ProgramError> { - const LOOKUP_CHANNEL: usize = 2; - let lookup_addr = MemoryAddress::new(0, Segment::ShiftTable, input0.low_u32() as usize); - if input0.bits() <= 32 { - let (_, read) = mem_read_gp_with_log_and_fill(LOOKUP_CHANNEL, lookup_addr, state, &mut row); - state.traces.push_memory(read); - } else { - // The shift constraints still expect the address to be set, even though no read will occur. - let channel = &mut row.mem_channels[LOOKUP_CHANNEL]; - channel.addr_context = F::from_canonical_usize(lookup_addr.context); - channel.addr_segment = F::from_canonical_usize(lookup_addr.segment); - channel.addr_virtual = F::from_canonical_usize(lookup_addr.virt); - - // Extra field required by the constraints for large shifts. - let high_limb_sum = row.mem_channels[0].value[1..].iter().copied().sum::(); - row.general.shift_mut().high_limb_sum_inv = high_limb_sum.inverse(); - } - - let operator = if is_shl { - BinaryOperator::Shl - } else { - BinaryOperator::Shr - }; - let operation = arithmetic::Operation::binary(operator, input0, input1); - - state.traces.push_arithmetic(operation); - push_no_write(state, result); - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_shl( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(input0, _), (input1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - - let result = if input0 > U256::from(255u64) { - U256::zero() - } else { - input1 << input0 - }; - append_shift(state, row, true, input0, input1, log_in1, result) -} - -pub(crate) fn generate_shr( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(input0, _), (input1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - - let result = if input0 > U256::from(255u64) { - U256::zero() - } else { - input1 >> input0 - }; - append_shift(state, row, false, input0, input1, log_in1, result) -} - -pub(crate) fn generate_syscall( - opcode: u8, - stack_values_read: usize, - stack_len_increased: bool, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - if TryInto::::try_into(state.registers.gas_used).is_err() { - return Err(ProgramError::GasLimitError); - } - - if state.registers.stack_len < stack_values_read { - return Err(ProgramError::StackUnderflow); - } - if stack_len_increased - && !state.registers.is_kernel - && state.registers.stack_len >= MAX_USER_STACK_SIZE - { - return Err(ProgramError::StackOverflow); - } - - let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"]; - let handler_addr_addr = - handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize); - assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); - let base_address = MemoryAddress::new(0, Segment::Code, handler_addr_addr); - let bytes = (0..BYTES_PER_OFFSET as usize) - .map(|i| { - let address = MemoryAddress { - virt: base_address.virt + i, - ..base_address - }; - let val = state.memory.get(address); - val.low_u32() as u8 - }) - .collect_vec(); - - let packed_int = U256::from_big_endian(&bytes); - - let jumptable_channel = &mut row.mem_channels[1]; - jumptable_channel.is_read = F::ONE; - jumptable_channel.addr_context = F::ZERO; - jumptable_channel.addr_segment = F::from_canonical_usize(Segment::Code as usize); - jumptable_channel.addr_virtual = F::from_canonical_usize(handler_addr_addr); - jumptable_channel.value[0] = F::from_canonical_usize(u256_to_usize(packed_int)?); - - byte_packing_log(state, base_address, bytes); - - let new_program_counter = u256_to_usize(packed_int)?; - - let gas = U256::from(state.registers.gas_used); - - let syscall_info = U256::from(state.registers.program_counter + 1) - + (U256::from(u64::from(state.registers.is_kernel)) << 32) - + (gas << 192); - - // `ArithmeticStark` range checks `mem_channels[0]`, which contains - // the top of the stack, `mem_channels[1]`, which contains the new PC, - // `mem_channels[2]`, which is empty, and next_row's `mem_channels[0]`, - // which contains the next top of the stack. - // Our goal here is to range-check the gas, contained in syscall_info, - // stored in the next stack top. - let range_check_op = arithmetic::Operation::range_check( - state.registers.stack_top, - packed_int, - U256::from(0), - U256::from(opcode), - syscall_info, - ); - // Set registers before pushing to the stack; in particular, we need to set kernel mode so we - // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we - // make `syscall_info`, which should contain the old values. - state.registers.program_counter = new_program_counter; - state.registers.is_kernel = true; - state.registers.gas_used = 0; - - push_with_write(state, &mut row, syscall_info)?; - - log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter)); - - state.traces.push_arithmetic(range_check_op); - state.traces.push_cpu(row); - - Ok(()) -} - -pub(crate) fn generate_eq( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(in0, _), (in1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let eq = in0 == in1; - let result = U256::from(u64::from(eq)); - - generate_pinv_diff(in0, in1, &mut row); - - push_no_write(state, result); - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_exit_kernel( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(kexit_info, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let kexit_info_u64 = kexit_info.0[0]; - let program_counter = kexit_info_u64 as u32 as usize; - let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32; - assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1); - let is_kernel_mode = is_kernel_mode_val != 0; - let gas_used_val = kexit_info.0[3]; - if TryInto::::try_into(gas_used_val).is_err() { - return Err(ProgramError::GasLimitError); - } - - state.registers.program_counter = program_counter; - state.registers.is_kernel = is_kernel_mode; - state.registers.gas_used = gas_used_val; - log::debug!( - "Exiting to {}, is_kernel={}", - program_counter, - is_kernel_mode - ); - - state.traces.push_cpu(row); - - Ok(()) -} - -pub(crate) fn generate_mload_general( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(addr, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - - let (val, log_read) = - mem_read_gp_with_log_and_fill(1, MemoryAddress::new_bundle(addr)?, state, &mut row); - push_no_write(state, val); - - // Because MLOAD_GENERAL performs 1 pop and 1 push, it does not make use of the `stack_inv_aux` general columns. - // We hence can set the diff to 2 (instead of 1) so that the stack constraint for MSTORE_GENERAL applies to both - // operations, which are combined into a single CPU flag. - let diff = row.stack_len - F::TWO; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_memory(log_read); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_mload_32bytes( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let len = u256_to_usize(len)?; - if len > 32 { - // The call to `U256::from_big_endian()` would panic. - return Err(ProgramError::IntegerTooLarge); - } - - let base_address = MemoryAddress::new_bundle(addr)?; - if usize::MAX - base_address.virt < len { - return Err(ProgramError::MemoryError(VirtTooLarge { - virt: base_address.virt.into(), - })); - } - let bytes = (0..len) - .map(|i| { - let address = MemoryAddress { - virt: base_address.virt + i, - ..base_address - }; - let val = state.memory.get(address); - val.low_u32() as u8 - }) - .collect_vec(); - - let packed_int = U256::from_big_endian(&bytes); - push_no_write(state, packed_int); - - byte_packing_log(state, base_address, bytes); - - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_mstore_general( - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(val, _), (addr, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - - let address = MemoryAddress::new_bundle(addr)?; - let log_write = mem_write_partial_log_and_fill(address, state, &mut row, val); - - let diff = row.stack_len - F::TWO; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - row.general.stack_mut().stack_inv_aux_2 = F::ONE; - state.registers.is_stack_top_read = true; - } else { - row.general.stack_mut().stack_inv = F::ZERO; - row.general.stack_mut().stack_inv_aux = F::ZERO; - } - - state.traces.push_memory(log_in1); - state.traces.push_memory(log_write); - - state.traces.push_cpu(row); - - Ok(()) -} - -pub(crate) fn generate_mstore_32bytes( - n: u8, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - let [(addr, _), (val, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - - let base_address = MemoryAddress::new_bundle(addr)?; - - byte_unpacking_log(state, base_address, val, n as usize); - - let new_addr = addr + n; - push_no_write(state, new_addr); - - state.traces.push_memory(log_in1); - state.traces.push_cpu(row); - Ok(()) -} - -pub(crate) fn generate_exception( - exc_code: u8, - state: &mut GenerationState, - mut row: CpuColumnsView, -) -> Result<(), ProgramError> { - if TryInto::::try_into(state.registers.gas_used).is_err() { - return Err(ProgramError::GasLimitError); - } - - row.op.exception = F::ONE; - - if let Some(inv) = row.stack_len.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } - - fill_stack_fields(state, &mut row)?; - - row.general.exception_mut().exc_code_bits = [ - F::from_bool(exc_code & 1 != 0), - F::from_bool(exc_code & 2 != 0), - F::from_bool(exc_code & 4 != 0), - ]; - - let handler_jumptable_addr = KERNEL.global_labels["exception_jumptable"]; - let handler_addr_addr = - handler_jumptable_addr + (exc_code as usize) * (BYTES_PER_OFFSET as usize); - assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset"); - let base_address = MemoryAddress::new(0, Segment::Code, handler_addr_addr); - let bytes = (0..BYTES_PER_OFFSET as usize) - .map(|i| { - let address = MemoryAddress { - virt: base_address.virt + i, - ..base_address - }; - let val = state.memory.get(address); - val.low_u32() as u8 - }) - .collect_vec(); - - let packed_int = U256::from_big_endian(&bytes); - - let jumptable_channel = &mut row.mem_channels[1]; - jumptable_channel.is_read = F::ONE; - jumptable_channel.addr_context = F::ZERO; - jumptable_channel.addr_segment = F::from_canonical_usize(Segment::Code as usize); - jumptable_channel.addr_virtual = F::from_canonical_usize(handler_addr_addr); - jumptable_channel.value[0] = F::from_canonical_usize(u256_to_usize(packed_int)?); - - byte_packing_log(state, base_address, bytes); - let new_program_counter = u256_to_usize(packed_int)?; - - let gas = U256::from(state.registers.gas_used); - - let exc_info = U256::from(state.registers.program_counter) + (gas << 192); - - // Get the opcode so we can provide it to the range_check operation. - let code_context = state.registers.code_context(); - let address = MemoryAddress::new(code_context, Segment::Code, state.registers.program_counter); - let opcode = state.memory.get(address); - - // `ArithmeticStark` range checks `mem_channels[0]`, which contains - // the top of the stack, `mem_channels[1]`, which contains the new PC, - // `mem_channels[2]`, which is empty, and next_row's `mem_channels[0]`, - // which contains the next top of the stack. - // Our goal here is to range-check the gas, contained in syscall_info, - // stored in the next stack top. - let range_check_op = arithmetic::Operation::range_check( - state.registers.stack_top, - packed_int, - U256::from(0), - opcode, - exc_info, - ); - // Set registers before pushing to the stack; in particular, we need to set kernel mode so we - // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we - // make `exc_info`, which should contain the old values. - state.registers.program_counter = new_program_counter; - state.registers.is_kernel = true; - state.registers.gas_used = 0; - - push_with_write(state, &mut row, exc_info)?; - - log::debug!("Exception to {}", KERNEL.offset_name(new_program_counter)); - state.traces.push_arithmetic(range_check_op); - state.traces.push_cpu(row); - - Ok(()) -} diff --git a/evm/src/witness/state.rs b/evm/src/witness/state.rs deleted file mode 100644 index 1070ee6439..0000000000 --- a/evm/src/witness/state.rs +++ /dev/null @@ -1,45 +0,0 @@ -use ethereum_types::U256; - -use crate::cpu::kernel::aggregator::KERNEL; - -const KERNEL_CONTEXT: usize = 0; - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct RegistersState { - pub program_counter: usize, - pub is_kernel: bool, - pub stack_len: usize, - pub stack_top: U256, - // Indicates if you read the new stack_top from memory to set the channel accordingly. - pub is_stack_top_read: bool, - // Indicates if the previous operation might have caused an overflow, and we must check - // if it's the case. - pub check_overflow: bool, - pub context: usize, - pub gas_used: u64, -} - -impl RegistersState { - pub(crate) const fn code_context(&self) -> usize { - if self.is_kernel { - KERNEL_CONTEXT - } else { - self.context - } - } -} - -impl Default for RegistersState { - fn default() -> Self { - Self { - program_counter: KERNEL.global_labels["main"], - is_kernel: true, - stack_len: 0, - stack_top: U256::zero(), - is_stack_top_read: false, - check_overflow: false, - context: 0, - gas_used: 0, - } - } -} diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs deleted file mode 100644 index 76267a0a41..0000000000 --- a/evm/src/witness/traces.rs +++ /dev/null @@ -1,242 +0,0 @@ -use core::mem::size_of; - -use itertools::Itertools; -use plonky2::field::extension::Extendable; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::hash::hash_types::RichField; -use plonky2::timed; -use plonky2::util::timing::TimingTree; -use starky::config::StarkConfig; -use starky::util::trace_rows_to_poly_values; - -use crate::all_stark::{AllStark, NUM_TABLES}; -use crate::arithmetic::{BinaryOperator, Operation}; -use crate::byte_packing::byte_packing_stark::BytePackingOp; -use crate::cpu::columns::CpuColumnsView; -use crate::keccak_sponge::columns::KECCAK_WIDTH_BYTES; -use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; -use crate::witness::memory::MemoryOp; -use crate::{arithmetic, keccak, keccak_sponge, logic}; - -#[derive(Clone, Copy, Debug)] -pub(crate) struct TraceCheckpoint { - pub(self) arithmetic_len: usize, - pub(self) byte_packing_len: usize, - pub(self) cpu_len: usize, - pub(self) keccak_len: usize, - pub(self) keccak_sponge_len: usize, - pub(self) logic_len: usize, - pub(self) memory_len: usize, -} - -#[derive(Debug)] -pub(crate) struct Traces { - pub(crate) arithmetic_ops: Vec, - pub(crate) byte_packing_ops: Vec, - pub(crate) cpu: Vec>, - pub(crate) logic_ops: Vec, - pub(crate) memory_ops: Vec, - pub(crate) keccak_inputs: Vec<([u64; keccak::keccak_stark::NUM_INPUTS], usize)>, - pub(crate) keccak_sponge_ops: Vec, -} - -impl Traces { - pub(crate) fn new() -> Self { - Traces { - arithmetic_ops: vec![], - byte_packing_ops: vec![], - cpu: vec![], - logic_ops: vec![], - memory_ops: vec![], - keccak_inputs: vec![], - keccak_sponge_ops: vec![], - } - } - - /// Returns the actual trace lengths for each STARK module. - // Uses a `TraceCheckPoint` as return object for convenience. - pub(crate) fn get_lengths(&self) -> TraceCheckpoint { - TraceCheckpoint { - arithmetic_len: self - .arithmetic_ops - .iter() - .map(|op| match op { - Operation::TernaryOperation { .. } => 2, - Operation::BinaryOperation { operator, .. } => match operator { - BinaryOperator::Div | BinaryOperator::Mod => 2, - _ => 1, - }, - Operation::RangeCheckOperation { .. } => 1, - }) - .sum(), - byte_packing_len: self - .byte_packing_ops - .iter() - .map(|op| usize::from(!op.bytes.is_empty())) - .sum(), - cpu_len: self.cpu.len(), - keccak_len: self.keccak_inputs.len() * keccak::keccak_stark::NUM_ROUNDS, - keccak_sponge_len: self - .keccak_sponge_ops - .iter() - .map(|op| op.input.len() / keccak_sponge::columns::KECCAK_RATE_BYTES + 1) - .sum(), - logic_len: self.logic_ops.len(), - // This is technically a lower-bound, as we may fill gaps, - // but this gives a relatively good estimate. - memory_len: self.memory_ops.len(), - } - } - - /// Returns the number of operations for each STARK module. - pub(crate) fn checkpoint(&self) -> TraceCheckpoint { - TraceCheckpoint { - arithmetic_len: self.arithmetic_ops.len(), - byte_packing_len: self.byte_packing_ops.len(), - cpu_len: self.cpu.len(), - keccak_len: self.keccak_inputs.len(), - keccak_sponge_len: self.keccak_sponge_ops.len(), - logic_len: self.logic_ops.len(), - memory_len: self.memory_ops.len(), - } - } - - pub(crate) fn rollback(&mut self, checkpoint: TraceCheckpoint) { - self.arithmetic_ops.truncate(checkpoint.arithmetic_len); - self.byte_packing_ops.truncate(checkpoint.byte_packing_len); - self.cpu.truncate(checkpoint.cpu_len); - self.keccak_inputs.truncate(checkpoint.keccak_len); - self.keccak_sponge_ops - .truncate(checkpoint.keccak_sponge_len); - self.logic_ops.truncate(checkpoint.logic_len); - self.memory_ops.truncate(checkpoint.memory_len); - } - - pub(crate) fn mem_ops_since(&self, checkpoint: TraceCheckpoint) -> &[MemoryOp] { - &self.memory_ops[checkpoint.memory_len..] - } - - pub(crate) fn push_cpu(&mut self, val: CpuColumnsView) { - self.cpu.push(val); - } - - pub(crate) fn push_logic(&mut self, op: logic::Operation) { - self.logic_ops.push(op); - } - - pub(crate) fn push_arithmetic(&mut self, op: arithmetic::Operation) { - self.arithmetic_ops.push(op); - } - - pub(crate) fn push_memory(&mut self, op: MemoryOp) { - self.memory_ops.push(op); - } - - pub(crate) fn push_byte_packing(&mut self, op: BytePackingOp) { - self.byte_packing_ops.push(op); - } - - pub(crate) fn push_keccak( - &mut self, - input: [u64; keccak::keccak_stark::NUM_INPUTS], - clock: usize, - ) { - self.keccak_inputs.push((input, clock)); - } - - pub(crate) fn push_keccak_bytes(&mut self, input: [u8; KECCAK_WIDTH_BYTES], clock: usize) { - let chunks = input - .chunks(size_of::()) - .map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap())) - .collect_vec() - .try_into() - .unwrap(); - self.push_keccak(chunks, clock); - } - - pub(crate) fn push_keccak_sponge(&mut self, op: KeccakSpongeOp) { - self.keccak_sponge_ops.push(op); - } - - pub(crate) fn clock(&self) -> usize { - self.cpu.len() - } - - pub(crate) fn into_tables( - self, - all_stark: &AllStark, - config: &StarkConfig, - timing: &mut TimingTree, - ) -> [Vec>; NUM_TABLES] - where - T: RichField + Extendable, - { - let cap_elements = config.fri_config.num_cap_elements(); - let Traces { - arithmetic_ops, - byte_packing_ops, - cpu, - logic_ops, - memory_ops, - keccak_inputs, - keccak_sponge_ops, - } = self; - - let arithmetic_trace = timed!( - timing, - "generate arithmetic trace", - all_stark.arithmetic_stark.generate_trace(arithmetic_ops) - ); - let byte_packing_trace = timed!( - timing, - "generate byte packing trace", - all_stark - .byte_packing_stark - .generate_trace(byte_packing_ops, cap_elements, timing) - ); - let cpu_rows = cpu.into_iter().map(|x| x.into()).collect(); - let cpu_trace = trace_rows_to_poly_values(cpu_rows); - let keccak_trace = timed!( - timing, - "generate Keccak trace", - all_stark - .keccak_stark - .generate_trace(keccak_inputs, cap_elements, timing) - ); - let keccak_sponge_trace = timed!( - timing, - "generate Keccak sponge trace", - all_stark - .keccak_sponge_stark - .generate_trace(keccak_sponge_ops, cap_elements, timing) - ); - let logic_trace = timed!( - timing, - "generate logic trace", - all_stark - .logic_stark - .generate_trace(logic_ops, cap_elements, timing) - ); - let memory_trace = timed!( - timing, - "generate memory trace", - all_stark.memory_stark.generate_trace(memory_ops, timing) - ); - - [ - arithmetic_trace, - byte_packing_trace, - cpu_trace, - keccak_trace, - keccak_sponge_trace, - logic_trace, - memory_trace, - ] - } -} - -impl Default for Traces { - fn default() -> Self { - Self::new() - } -} diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs deleted file mode 100644 index aed6ff5397..0000000000 --- a/evm/src/witness/transition.rs +++ /dev/null @@ -1,504 +0,0 @@ -use anyhow::bail; -use log::log_enabled; -use plonky2::field::types::Field; - -use super::memory::{MemoryOp, MemoryOpKind}; -use super::util::fill_channel_with_value; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::cpu::stack::{ - EQ_STACK_BEHAVIOR, IS_ZERO_STACK_BEHAVIOR, JUMPI_OP, JUMP_OP, MAX_USER_STACK_SIZE, - MIGHT_OVERFLOW, STACK_BEHAVIORS, -}; -use crate::generation::state::GenerationState; -use crate::memory::segments::Segment; -use crate::witness::errors::ProgramError; -use crate::witness::gas::gas_to_charge; -use crate::witness::memory::MemoryAddress; -use crate::witness::memory::MemoryChannel::GeneralPurpose; -use crate::witness::operation::*; -use crate::witness::state::RegistersState; -use crate::witness::util::mem_read_code_with_log_and_fill; -use crate::{arithmetic, logic}; - -fn read_code_memory(state: &mut GenerationState, row: &mut CpuColumnsView) -> u8 { - let code_context = state.registers.code_context(); - row.code_context = F::from_canonical_usize(code_context); - - let address = MemoryAddress::new(code_context, Segment::Code, state.registers.program_counter); - let (opcode, mem_log) = mem_read_code_with_log_and_fill(address, state, row); - - state.traces.push_memory(mem_log); - - opcode -} - -pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result { - match (opcode, registers.is_kernel) { - (0x00, _) => Ok(Operation::Syscall(opcode, 0, false)), // STOP - (0x01, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Add)), - (0x02, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Mul)), - (0x03, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Sub)), - (0x04, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Div)), - (0x05, _) => Ok(Operation::Syscall(opcode, 2, false)), // SDIV - (0x06, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Mod)), - (0x07, _) => Ok(Operation::Syscall(opcode, 2, false)), // SMOD - (0x08, _) => Ok(Operation::TernaryArithmetic( - arithmetic::TernaryOperator::AddMod, - )), - (0x09, _) => Ok(Operation::TernaryArithmetic( - arithmetic::TernaryOperator::MulMod, - )), - (0x0a, _) => Ok(Operation::Syscall(opcode, 2, false)), // EXP - (0x0b, _) => Ok(Operation::Syscall(opcode, 2, false)), // SIGNEXTEND - (0x0c, true) => Ok(Operation::BinaryArithmetic( - arithmetic::BinaryOperator::AddFp254, - )), - (0x0d, true) => Ok(Operation::BinaryArithmetic( - arithmetic::BinaryOperator::MulFp254, - )), - (0x0e, true) => Ok(Operation::BinaryArithmetic( - arithmetic::BinaryOperator::SubFp254, - )), - (0x0f, true) => Ok(Operation::TernaryArithmetic( - arithmetic::TernaryOperator::SubMod, - )), - (0x10, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Lt)), - (0x11, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Gt)), - (0x12, _) => Ok(Operation::Syscall(opcode, 2, false)), // SLT - (0x13, _) => Ok(Operation::Syscall(opcode, 2, false)), // SGT - (0x14, _) => Ok(Operation::Eq), - (0x15, _) => Ok(Operation::Iszero), - (0x16, _) => Ok(Operation::BinaryLogic(logic::Op::And)), - (0x17, _) => Ok(Operation::BinaryLogic(logic::Op::Or)), - (0x18, _) => Ok(Operation::BinaryLogic(logic::Op::Xor)), - (0x19, _) => Ok(Operation::Not), - (0x1a, _) => Ok(Operation::BinaryArithmetic( - arithmetic::BinaryOperator::Byte, - )), - (0x1b, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shl)), - (0x1c, _) => Ok(Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shr)), - (0x1d, _) => Ok(Operation::Syscall(opcode, 2, false)), // SAR - (0x20, _) => Ok(Operation::Syscall(opcode, 2, false)), // KECCAK256 - (0x21, true) => Ok(Operation::KeccakGeneral), - (0x30, _) => Ok(Operation::Syscall(opcode, 0, true)), // ADDRESS - (0x31, _) => Ok(Operation::Syscall(opcode, 1, false)), // BALANCE - (0x32, _) => Ok(Operation::Syscall(opcode, 0, true)), // ORIGIN - (0x33, _) => Ok(Operation::Syscall(opcode, 0, true)), // CALLER - (0x34, _) => Ok(Operation::Syscall(opcode, 0, true)), // CALLVALUE - (0x35, _) => Ok(Operation::Syscall(opcode, 1, false)), // CALLDATALOAD - (0x36, _) => Ok(Operation::Syscall(opcode, 0, true)), // CALLDATASIZE - (0x37, _) => Ok(Operation::Syscall(opcode, 3, false)), // CALLDATACOPY - (0x38, _) => Ok(Operation::Syscall(opcode, 0, true)), // CODESIZE - (0x39, _) => Ok(Operation::Syscall(opcode, 3, false)), // CODECOPY - (0x3a, _) => Ok(Operation::Syscall(opcode, 0, true)), // GASPRICE - (0x3b, _) => Ok(Operation::Syscall(opcode, 1, false)), // EXTCODESIZE - (0x3c, _) => Ok(Operation::Syscall(opcode, 4, false)), // EXTCODECOPY - (0x3d, _) => Ok(Operation::Syscall(opcode, 0, true)), // RETURNDATASIZE - (0x3e, _) => Ok(Operation::Syscall(opcode, 3, false)), // RETURNDATACOPY - (0x3f, _) => Ok(Operation::Syscall(opcode, 1, false)), // EXTCODEHASH - (0x40, _) => Ok(Operation::Syscall(opcode, 1, false)), // BLOCKHASH - (0x41, _) => Ok(Operation::Syscall(opcode, 0, true)), // COINBASE - (0x42, _) => Ok(Operation::Syscall(opcode, 0, true)), // TIMESTAMP - (0x43, _) => Ok(Operation::Syscall(opcode, 0, true)), // NUMBER - (0x44, _) => Ok(Operation::Syscall(opcode, 0, true)), // DIFFICULTY - (0x45, _) => Ok(Operation::Syscall(opcode, 0, true)), // GASLIMIT - (0x46, _) => Ok(Operation::Syscall(opcode, 0, true)), // CHAINID - (0x47, _) => Ok(Operation::Syscall(opcode, 0, true)), // SELFBALANCE - (0x48, _) => Ok(Operation::Syscall(opcode, 0, true)), // BASEFEE - (0x49, true) => Ok(Operation::ProverInput), - (0x50, _) => Ok(Operation::Pop), - (0x51, _) => Ok(Operation::Syscall(opcode, 1, false)), // MLOAD - (0x52, _) => Ok(Operation::Syscall(opcode, 2, false)), // MSTORE - (0x53, _) => Ok(Operation::Syscall(opcode, 2, false)), // MSTORE8 - (0x54, _) => Ok(Operation::Syscall(opcode, 1, false)), // SLOAD - (0x55, _) => Ok(Operation::Syscall(opcode, 2, false)), // SSTORE - (0x56, _) => Ok(Operation::Jump), - (0x57, _) => Ok(Operation::Jumpi), - (0x58, _) => Ok(Operation::Pc), - (0x59, _) => Ok(Operation::Syscall(opcode, 0, true)), // MSIZE - (0x5a, _) => Ok(Operation::Syscall(opcode, 0, true)), // GAS - (0x5b, _) => Ok(Operation::Jumpdest), - (0x5f..=0x7f, _) => Ok(Operation::Push(opcode - 0x5f)), - (0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)), - (0x90..=0x9f, _) => Ok(Operation::Swap(opcode & 0xf)), - (0xa0, _) => Ok(Operation::Syscall(opcode, 2, false)), // LOG0 - (0xa1, _) => Ok(Operation::Syscall(opcode, 3, false)), // LOG1 - (0xa2, _) => Ok(Operation::Syscall(opcode, 4, false)), // LOG2 - (0xa3, _) => Ok(Operation::Syscall(opcode, 5, false)), // LOG3 - (0xa4, _) => Ok(Operation::Syscall(opcode, 6, false)), // LOG4 - (0xa5, true) => { - log::warn!( - "Kernel panic at {}", - KERNEL.offset_name(registers.program_counter), - ); - Err(ProgramError::KernelPanic) - } - (0xc0..=0xdf, true) => Ok(Operation::Mstore32Bytes(opcode - 0xc0 + 1)), - (0xf0, _) => Ok(Operation::Syscall(opcode, 3, false)), // CREATE - (0xf1, _) => Ok(Operation::Syscall(opcode, 7, false)), // CALL - (0xf2, _) => Ok(Operation::Syscall(opcode, 7, false)), // CALLCODE - (0xf3, _) => Ok(Operation::Syscall(opcode, 2, false)), // RETURN - (0xf4, _) => Ok(Operation::Syscall(opcode, 6, false)), // DELEGATECALL - (0xf5, _) => Ok(Operation::Syscall(opcode, 4, false)), // CREATE2 - (0xf6, true) => Ok(Operation::GetContext), - (0xf7, true) => Ok(Operation::SetContext), - (0xf8, true) => Ok(Operation::Mload32Bytes), - (0xf9, true) => Ok(Operation::ExitKernel), - (0xfa, _) => Ok(Operation::Syscall(opcode, 6, false)), // STATICCALL - (0xfb, true) => Ok(Operation::MloadGeneral), - (0xfc, true) => Ok(Operation::MstoreGeneral), - (0xfd, _) => Ok(Operation::Syscall(opcode, 2, false)), // REVERT - (0xff, _) => Ok(Operation::Syscall(opcode, 1, false)), // SELFDESTRUCT - _ => { - log::warn!("Invalid opcode: {}", opcode); - Err(ProgramError::InvalidOpcode) - } - } -} - -fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { - let flags = &mut row.op; - *match op { - Operation::Dup(_) | Operation::Swap(_) => &mut flags.dup_swap, - Operation::Iszero | Operation::Eq => &mut flags.eq_iszero, - Operation::Not | Operation::Pop => &mut flags.not_pop, - Operation::Syscall(_, _, _) => &mut flags.syscall, - Operation::BinaryLogic(_) => &mut flags.logic_op, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::AddFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::MulFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::SubFp254) => &mut flags.fp254_op, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shl) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shr) => &mut flags.shift, - Operation::BinaryArithmetic(_) => &mut flags.binary_op, - Operation::TernaryArithmetic(_) => &mut flags.ternary_op, - Operation::KeccakGeneral | Operation::Jumpdest => &mut flags.jumpdest_keccak_general, - Operation::ProverInput | Operation::Push(1..) => &mut flags.push_prover_input, - Operation::Jump | Operation::Jumpi => &mut flags.jumps, - Operation::Pc | Operation::Push(0) => &mut flags.pc_push0, - Operation::GetContext | Operation::SetContext => &mut flags.context_op, - Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => &mut flags.m_op_32bytes, - Operation::ExitKernel => &mut flags.exit_kernel, - Operation::MloadGeneral | Operation::MstoreGeneral => &mut flags.m_op_general, - } = F::ONE; -} - -// Equal to the number of pops if an operation pops without pushing, and `None` otherwise. -const fn get_op_special_length(op: Operation) -> Option { - let behavior_opt = match op { - Operation::Push(0) | Operation::Pc => STACK_BEHAVIORS.pc_push0, - Operation::Push(1..) | Operation::ProverInput => STACK_BEHAVIORS.push_prover_input, - Operation::Dup(_) | Operation::Swap(_) => STACK_BEHAVIORS.dup_swap, - Operation::Iszero => IS_ZERO_STACK_BEHAVIOR, - Operation::Not | Operation::Pop => STACK_BEHAVIORS.not_pop, - Operation::Syscall(_, _, _) => STACK_BEHAVIORS.syscall, - Operation::Eq => EQ_STACK_BEHAVIOR, - Operation::BinaryLogic(_) => STACK_BEHAVIORS.logic_op, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::AddFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::MulFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::SubFp254) => { - STACK_BEHAVIORS.fp254_op - } - Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shl) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shr) => STACK_BEHAVIORS.shift, - Operation::BinaryArithmetic(_) => STACK_BEHAVIORS.binary_op, - Operation::TernaryArithmetic(_) => STACK_BEHAVIORS.ternary_op, - Operation::KeccakGeneral | Operation::Jumpdest => STACK_BEHAVIORS.jumpdest_keccak_general, - Operation::Jump => JUMP_OP, - Operation::Jumpi => JUMPI_OP, - Operation::GetContext | Operation::SetContext => None, - Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => STACK_BEHAVIORS.m_op_32bytes, - Operation::ExitKernel => STACK_BEHAVIORS.exit_kernel, - Operation::MloadGeneral | Operation::MstoreGeneral => STACK_BEHAVIORS.m_op_general, - }; - if let Some(behavior) = behavior_opt { - if behavior.num_pops > 0 && !behavior.pushes { - Some(behavior.num_pops) - } else { - None - } - } else { - None - } -} - -// These operations might trigger a stack overflow, typically those pushing without popping. -// Kernel-only pushing instructions aren't considered; they can't overflow. -const fn might_overflow_op(op: Operation) -> bool { - match op { - Operation::Push(1..) | Operation::ProverInput => MIGHT_OVERFLOW.push_prover_input, - Operation::Dup(_) | Operation::Swap(_) => MIGHT_OVERFLOW.dup_swap, - Operation::Iszero | Operation::Eq => MIGHT_OVERFLOW.eq_iszero, - Operation::Not | Operation::Pop => MIGHT_OVERFLOW.not_pop, - Operation::Syscall(_, _, _) => MIGHT_OVERFLOW.syscall, - Operation::BinaryLogic(_) => MIGHT_OVERFLOW.logic_op, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::AddFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::MulFp254) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::SubFp254) => { - MIGHT_OVERFLOW.fp254_op - } - Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shl) - | Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shr) => MIGHT_OVERFLOW.shift, - Operation::BinaryArithmetic(_) => MIGHT_OVERFLOW.binary_op, - Operation::TernaryArithmetic(_) => MIGHT_OVERFLOW.ternary_op, - Operation::KeccakGeneral | Operation::Jumpdest => MIGHT_OVERFLOW.jumpdest_keccak_general, - Operation::Jump | Operation::Jumpi => MIGHT_OVERFLOW.jumps, - Operation::Pc | Operation::Push(0) => MIGHT_OVERFLOW.pc_push0, - Operation::GetContext | Operation::SetContext => MIGHT_OVERFLOW.context_op, - Operation::Mload32Bytes | Operation::Mstore32Bytes(_) => MIGHT_OVERFLOW.m_op_32bytes, - Operation::ExitKernel => MIGHT_OVERFLOW.exit_kernel, - Operation::MloadGeneral | Operation::MstoreGeneral => MIGHT_OVERFLOW.m_op_general, - } -} - -fn perform_op( - state: &mut GenerationState, - op: Operation, - row: CpuColumnsView, -) -> Result { - match op { - Operation::Push(n) => generate_push(n, state, row)?, - Operation::Dup(n) => generate_dup(n, state, row)?, - Operation::Swap(n) => generate_swap(n, state, row)?, - Operation::Iszero => generate_iszero(state, row)?, - Operation::Not => generate_not(state, row)?, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shl) => generate_shl(state, row)?, - Operation::BinaryArithmetic(arithmetic::BinaryOperator::Shr) => generate_shr(state, row)?, - Operation::Syscall(opcode, stack_values_read, stack_len_increased) => { - generate_syscall(opcode, stack_values_read, stack_len_increased, state, row)? - } - Operation::Eq => generate_eq(state, row)?, - Operation::BinaryLogic(binary_logic_op) => { - generate_binary_logic_op(binary_logic_op, state, row)? - } - Operation::BinaryArithmetic(op) => generate_binary_arithmetic_op(op, state, row)?, - Operation::TernaryArithmetic(op) => generate_ternary_arithmetic_op(op, state, row)?, - Operation::KeccakGeneral => generate_keccak_general(state, row)?, - Operation::ProverInput => generate_prover_input(state, row)?, - Operation::Pop => generate_pop(state, row)?, - Operation::Jump => generate_jump(state, row)?, - Operation::Jumpi => generate_jumpi(state, row)?, - Operation::Pc => generate_pc(state, row)?, - Operation::Jumpdest => generate_jumpdest(state, row)?, - Operation::GetContext => generate_get_context(state, row)?, - Operation::SetContext => generate_set_context(state, row)?, - Operation::Mload32Bytes => generate_mload_32bytes(state, row)?, - Operation::Mstore32Bytes(n) => generate_mstore_32bytes(n, state, row)?, - Operation::ExitKernel => generate_exit_kernel(state, row)?, - Operation::MloadGeneral => generate_mload_general(state, row)?, - Operation::MstoreGeneral => generate_mstore_general(state, row)?, - }; - - state.registers.program_counter += match op { - Operation::Syscall(_, _, _) | Operation::ExitKernel => 0, - Operation::Push(n) => n as usize + 1, - Operation::Jump | Operation::Jumpi => 0, - _ => 1, - }; - - state.registers.gas_used += gas_to_charge(op); - - let gas_limit_address = MemoryAddress::new( - state.registers.context, - Segment::ContextMetadata, - ContextMetadata::GasLimit.unscale(), // context offsets are already scaled - ); - if !state.registers.is_kernel { - let gas_limit = TryInto::::try_into(state.memory.get(gas_limit_address)); - match gas_limit { - Ok(limit) => { - if state.registers.gas_used > limit { - return Err(ProgramError::OutOfGas); - } - } - Err(_) => return Err(ProgramError::IntegerTooLarge), - } - } - - Ok(op) -} - -/// Row that has the correct values for system registers and the code channel, but is otherwise -/// blank. It fulfills the constraints that are common to successful operations and the exception -/// operation. It also returns the opcode. -fn base_row(state: &mut GenerationState) -> (CpuColumnsView, u8) { - let mut row: CpuColumnsView = CpuColumnsView::default(); - row.clock = F::from_canonical_usize(state.traces.clock()); - row.context = F::from_canonical_usize(state.registers.context); - row.program_counter = F::from_canonical_usize(state.registers.program_counter); - row.is_kernel_mode = F::from_bool(state.registers.is_kernel); - row.gas = F::from_canonical_u64(state.registers.gas_used); - row.stack_len = F::from_canonical_usize(state.registers.stack_len); - fill_channel_with_value(&mut row, 0, state.registers.stack_top); - - let opcode = read_code_memory(state, &mut row); - (row, opcode) -} - -pub(crate) fn fill_stack_fields( - state: &mut GenerationState, - row: &mut CpuColumnsView, -) -> Result<(), ProgramError> { - if state.registers.is_stack_top_read { - let channel = &mut row.mem_channels[0]; - channel.used = F::ONE; - channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_usize(state.registers.context); - channel.addr_segment = F::from_canonical_usize(Segment::Stack.unscale()); - channel.addr_virtual = F::from_canonical_usize(state.registers.stack_len - 1); - - let address = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1, - ); - - let mem_op = MemoryOp::new( - GeneralPurpose(0), - state.traces.clock(), - address, - MemoryOpKind::Read, - state.registers.stack_top, - ); - state.traces.push_memory(mem_op); - state.registers.is_stack_top_read = false; - } - - if state.registers.check_overflow { - if state.registers.is_kernel { - row.general.stack_mut().stack_len_bounds_aux = F::ZERO; - } else { - let clock = state.traces.clock(); - let last_row = &mut state.traces.cpu[clock - 1]; - let disallowed_len = F::from_canonical_usize(MAX_USER_STACK_SIZE + 1); - let diff = row.stack_len - disallowed_len; - if let Some(inv) = diff.try_inverse() { - last_row.general.stack_mut().stack_len_bounds_aux = inv; - } else { - // This is a stack overflow that should have been caught earlier. - return Err(ProgramError::InterpreterError); - } - } - state.registers.check_overflow = false; - } - - Ok(()) -} - -fn try_perform_instruction( - state: &mut GenerationState, -) -> Result { - let (mut row, opcode) = base_row(state); - let op = decode(state.registers, opcode)?; - - if state.registers.is_kernel { - log_kernel_instruction(state, op); - } else { - log::debug!("User instruction: {:?}", op); - } - - fill_op_flag(op, &mut row); - - fill_stack_fields(state, &mut row)?; - - // Might write in general CPU columns when it shouldn't, but the correct values will - // overwrite these ones during the op generation. - if let Some(special_len) = get_op_special_length(op) { - let special_len = F::from_canonical_usize(special_len); - let diff = row.stack_len - special_len; - if let Some(inv) = diff.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - state.registers.is_stack_top_read = true; - } - } else if let Some(inv) = row.stack_len.try_inverse() { - row.general.stack_mut().stack_inv = inv; - row.general.stack_mut().stack_inv_aux = F::ONE; - } - - perform_op(state, op, row) -} - -fn log_kernel_instruction(state: &GenerationState, op: Operation) { - // The logic below is a bit costly, so skip it if debug logs aren't enabled. - if !log_enabled!(log::Level::Debug) { - return; - } - - let pc = state.registers.program_counter; - let is_interesting_offset = KERNEL - .offset_label(pc) - .filter(|label| !label.starts_with("halt")) - .is_some(); - let level = if is_interesting_offset { - log::Level::Debug - } else { - log::Level::Trace - }; - log::log!( - level, - "Cycle {}, ctx={}, pc={}, instruction={:?}, stack={:?}", - state.traces.clock(), - state.registers.context, - KERNEL.offset_name(pc), - op, - state.stack(), - ); - - assert!(pc < KERNEL.code.len(), "Kernel PC is out of range: {}", pc); -} - -fn handle_error(state: &mut GenerationState, err: ProgramError) -> anyhow::Result<()> { - let exc_code: u8 = match err { - ProgramError::OutOfGas => 0, - ProgramError::InvalidOpcode => 1, - ProgramError::StackUnderflow => 2, - ProgramError::InvalidJumpDestination => 3, - ProgramError::InvalidJumpiDestination => 4, - ProgramError::StackOverflow => 5, - _ => bail!("TODO: figure out what to do with this..."), - }; - - let checkpoint = state.checkpoint(); - - let (row, _) = base_row(state); - generate_exception(exc_code, state, row) - .map_err(|_| anyhow::Error::msg("error handling errored..."))?; - - state - .memory - .apply_ops(state.traces.mem_ops_since(checkpoint.traces)); - Ok(()) -} - -pub(crate) fn transition(state: &mut GenerationState) -> anyhow::Result<()> { - let checkpoint = state.checkpoint(); - let result = try_perform_instruction(state); - - match result { - Ok(op) => { - state - .memory - .apply_ops(state.traces.mem_ops_since(checkpoint.traces)); - if might_overflow_op(op) { - state.registers.check_overflow = true; - } - Ok(()) - } - Err(e) => { - if state.registers.is_kernel { - let offset_name = KERNEL.offset_name(state.registers.program_counter); - bail!( - "{:?} in kernel at pc={}, stack={:?}, memory={:?}", - e, - offset_name, - state.stack(), - state.memory.contexts[0].segments[Segment::KernelGeneral.unscale()].content, - ); - } - state.rollback(checkpoint); - handle_error(state, e) - } - } -} diff --git a/evm/src/witness/util.rs b/evm/src/witness/util.rs deleted file mode 100644 index 5f39809392..0000000000 --- a/evm/src/witness/util.rs +++ /dev/null @@ -1,393 +0,0 @@ -use ethereum_types::U256; -use plonky2::field::types::Field; - -use super::memory::DUMMY_MEMOP; -use crate::byte_packing::byte_packing_stark::BytePackingOp; -use crate::cpu::columns::CpuColumnsView; -use crate::cpu::kernel::keccak_util::keccakf_u8s; -use crate::cpu::membus::NUM_CHANNELS; -use crate::cpu::stack::MAX_USER_STACK_SIZE; -use crate::generation::state::GenerationState; -use crate::keccak_sponge::columns::{KECCAK_RATE_BYTES, KECCAK_WIDTH_BYTES}; -use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; -use crate::logic; -use crate::memory::segments::Segment; -use crate::witness::errors::ProgramError; -use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; - -fn to_byte_checked(n: U256) -> u8 { - let res = n.byte(0); - assert_eq!(n, res.into()); - res -} - -fn to_bits_le(n: u8) -> [F; 8] { - let mut res = [F::ZERO; 8]; - for (i, bit) in res.iter_mut().enumerate() { - *bit = F::from_bool(n & (1 << i) != 0); - } - res -} - -/// Peek at the stack item `i`th from the top. If `i=0` this gives the tip. -pub(crate) fn stack_peek( - state: &GenerationState, - i: usize, -) -> Result { - if i >= state.registers.stack_len { - return Err(ProgramError::StackUnderflow); - } - if i == 0 { - return Ok(state.registers.stack_top); - } - - Ok(state.memory.get(MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1 - i, - ))) -} - -/// Peek at kernel at specified segment and address -pub(crate) fn current_context_peek( - state: &GenerationState, - segment: Segment, - virt: usize, -) -> U256 { - let context = state.registers.context; - state.memory.get(MemoryAddress::new(context, segment, virt)) -} - -pub(crate) fn fill_channel_with_value(row: &mut CpuColumnsView, n: usize, val: U256) { - let channel = &mut row.mem_channels[n]; - let val_limbs: [u64; 4] = val.0; - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } -} - -/// Pushes without writing in memory. This happens in opcodes where a push immediately follows a pop. -pub(crate) fn push_no_write(state: &mut GenerationState, val: U256) { - state.registers.stack_top = val; - state.registers.stack_len += 1; -} - -/// Pushes and (maybe) writes the previous stack top in memory. This happens in opcodes which only push. -pub(crate) fn push_with_write( - state: &mut GenerationState, - row: &mut CpuColumnsView, - val: U256, -) -> Result<(), ProgramError> { - if !state.registers.is_kernel && state.registers.stack_len >= MAX_USER_STACK_SIZE { - return Err(ProgramError::StackOverflow); - } - - let write = if state.registers.stack_len == 0 { - None - } else { - let address = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1, - ); - let res = mem_write_partial_log_and_fill(address, state, row, state.registers.stack_top); - Some(res) - }; - push_no_write(state, val); - if let Some(log) = write { - state.traces.push_memory(log); - row.partial_channel.used = F::ONE; - } - Ok(()) -} - -pub(crate) fn mem_read_with_log( - channel: MemoryChannel, - address: MemoryAddress, - state: &GenerationState, -) -> (U256, MemoryOp) { - let val = state.memory.get(address); - let op = MemoryOp::new( - channel, - state.traces.clock(), - address, - MemoryOpKind::Read, - val, - ); - (val, op) -} - -pub(crate) fn mem_write_log( - channel: MemoryChannel, - address: MemoryAddress, - state: &GenerationState, - val: U256, -) -> MemoryOp { - MemoryOp::new( - channel, - state.traces.clock(), - address, - MemoryOpKind::Write, - val, - ) -} - -pub(crate) fn mem_read_code_with_log_and_fill( - address: MemoryAddress, - state: &GenerationState, - row: &mut CpuColumnsView, -) -> (u8, MemoryOp) { - let (val, op) = mem_read_with_log(MemoryChannel::Code, address, state); - - let val_u8 = to_byte_checked(val); - row.opcode_bits = to_bits_le(val_u8); - - (val_u8, op) -} - -pub(crate) fn mem_read_gp_with_log_and_fill( - n: usize, - address: MemoryAddress, - state: &GenerationState, - row: &mut CpuColumnsView, -) -> (U256, MemoryOp) { - let (val, op) = mem_read_with_log(MemoryChannel::GeneralPurpose(n), address, state); - let val_limbs: [u64; 4] = val.0; - - let channel = &mut row.mem_channels[n]; - assert_eq!(channel.used, F::ZERO); - channel.used = F::ONE; - channel.is_read = F::ONE; - channel.addr_context = F::from_canonical_usize(address.context); - channel.addr_segment = F::from_canonical_usize(address.segment); - channel.addr_virtual = F::from_canonical_usize(address.virt); - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - - (val, op) -} - -pub(crate) fn mem_write_gp_log_and_fill( - n: usize, - address: MemoryAddress, - state: &GenerationState, - row: &mut CpuColumnsView, - val: U256, -) -> MemoryOp { - let op = mem_write_log(MemoryChannel::GeneralPurpose(n), address, state, val); - let val_limbs: [u64; 4] = val.0; - - let channel = &mut row.mem_channels[n]; - assert_eq!(channel.used, F::ZERO); - channel.used = F::ONE; - channel.is_read = F::ZERO; - channel.addr_context = F::from_canonical_usize(address.context); - channel.addr_segment = F::from_canonical_usize(address.segment); - channel.addr_virtual = F::from_canonical_usize(address.virt); - for (i, limb) in val_limbs.into_iter().enumerate() { - channel.value[2 * i] = F::from_canonical_u32(limb as u32); - channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); - } - - op -} - -pub(crate) fn mem_write_partial_log_and_fill( - address: MemoryAddress, - state: &GenerationState, - row: &mut CpuColumnsView, - val: U256, -) -> MemoryOp { - let op = mem_write_log(MemoryChannel::PartialChannel, address, state, val); - - let channel = &mut row.partial_channel; - assert!(channel.used.is_zero()); - channel.used = F::ONE; - channel.is_read = F::ZERO; - channel.addr_context = F::from_canonical_usize(address.context); - channel.addr_segment = F::from_canonical_usize(address.segment); - channel.addr_virtual = F::from_canonical_usize(address.virt); - - op -} - -// Channel 0 already contains the top of the stack. You only need to read -// from the second popped element. -// If the resulting stack isn't empty, update `stack_top`. -pub(crate) fn stack_pop_with_log_and_fill( - state: &mut GenerationState, - row: &mut CpuColumnsView, -) -> Result<[(U256, MemoryOp); N], ProgramError> { - if state.registers.stack_len < N { - return Err(ProgramError::StackUnderflow); - } - - let new_stack_top = if state.registers.stack_len == N { - None - } else { - Some(stack_peek(state, N)?) - }; - - let result = core::array::from_fn(|i| { - if i == 0 { - (state.registers.stack_top, DUMMY_MEMOP) - } else { - let address = MemoryAddress::new( - state.registers.context, - Segment::Stack, - state.registers.stack_len - 1 - i, - ); - - mem_read_gp_with_log_and_fill(i, address, state, row) - } - }); - - state.registers.stack_len -= N; - - if let Some(val) = new_stack_top { - state.registers.stack_top = val; - } - - Ok(result) -} - -fn xor_into_sponge( - state: &mut GenerationState, - sponge_state: &mut [u8; KECCAK_WIDTH_BYTES], - block: &[u8; KECCAK_RATE_BYTES], -) { - for i in (0..KECCAK_RATE_BYTES).step_by(32) { - let range = i..KECCAK_RATE_BYTES.min(i + 32); - let lhs = U256::from_little_endian(&sponge_state[range.clone()]); - let rhs = U256::from_little_endian(&block[range]); - state - .traces - .push_logic(logic::Operation::new(logic::Op::Xor, lhs, rhs)); - } - for i in 0..KECCAK_RATE_BYTES { - sponge_state[i] ^= block[i]; - } -} - -pub(crate) fn keccak_sponge_log( - state: &mut GenerationState, - base_address: MemoryAddress, - input: Vec, -) { - let clock = state.traces.clock(); - - let mut address = base_address; - let mut input_blocks = input.chunks_exact(KECCAK_RATE_BYTES); - let mut sponge_state = [0u8; KECCAK_WIDTH_BYTES]; - for block in input_blocks.by_ref() { - for &byte in block { - state.traces.push_memory(MemoryOp::new( - MemoryChannel::Code, - clock, - address, - MemoryOpKind::Read, - byte.into(), - )); - address.increment(); - } - xor_into_sponge(state, &mut sponge_state, block.try_into().unwrap()); - state - .traces - .push_keccak_bytes(sponge_state, clock * NUM_CHANNELS); - keccakf_u8s(&mut sponge_state); - } - - for &byte in input_blocks.remainder() { - state.traces.push_memory(MemoryOp::new( - MemoryChannel::Code, - clock, - address, - MemoryOpKind::Read, - byte.into(), - )); - address.increment(); - } - let mut final_block = [0u8; KECCAK_RATE_BYTES]; - final_block[..input_blocks.remainder().len()].copy_from_slice(input_blocks.remainder()); - // pad10*1 rule - if input_blocks.remainder().len() == KECCAK_RATE_BYTES - 1 { - // Both 1s are placed in the same byte. - final_block[input_blocks.remainder().len()] = 0b10000001; - } else { - final_block[input_blocks.remainder().len()] = 1; - final_block[KECCAK_RATE_BYTES - 1] = 0b10000000; - } - xor_into_sponge(state, &mut sponge_state, &final_block); - state - .traces - .push_keccak_bytes(sponge_state, clock * NUM_CHANNELS); - - state.traces.push_keccak_sponge(KeccakSpongeOp { - base_address, - timestamp: clock * NUM_CHANNELS, - input, - }); -} - -pub(crate) fn byte_packing_log( - state: &mut GenerationState, - base_address: MemoryAddress, - bytes: Vec, -) { - let clock = state.traces.clock(); - - let mut address = base_address; - for &byte in &bytes { - state.traces.push_memory(MemoryOp::new( - MemoryChannel::Code, - clock, - address, - MemoryOpKind::Read, - byte.into(), - )); - address.increment(); - } - - state.traces.push_byte_packing(BytePackingOp { - is_read: true, - base_address, - timestamp: clock * NUM_CHANNELS, - bytes, - }); -} - -pub(crate) fn byte_unpacking_log( - state: &mut GenerationState, - base_address: MemoryAddress, - val: U256, - len: usize, -) { - let clock = state.traces.clock(); - - let mut bytes = vec![0; 32]; - val.to_little_endian(&mut bytes); - bytes.resize(len, 0); - bytes.reverse(); - - let mut address = base_address; - for &byte in &bytes { - state.traces.push_memory(MemoryOp::new( - MemoryChannel::Code, - clock, - address, - MemoryOpKind::Write, - byte.into(), - )); - address.increment(); - } - - state.traces.push_byte_packing(BytePackingOp { - is_read: false, - base_address, - timestamp: clock * NUM_CHANNELS, - bytes, - }); -} diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs deleted file mode 100644 index 51da107c53..0000000000 --- a/evm/tests/add11_yml.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// The `add11_yml` test case from https://github.com/ethereum/tests -#[test] -fn add11_yml() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); - let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], - }; - - let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 0xa868u64.into(), - block_bloom: [0.into(); 8], - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let expected_state_trie_after = { - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_after = AccountRlp { - balance: 0xde0b6b3a75be550u64.into(), - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - balance: 0xde0b6b3a76586a0u64.into(), - code_hash, - // Storage map: { 0 => 2 } - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), - ..AccountRlp::default() - }; - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 0xa868u64.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - block_metadata, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0xa868u64.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs deleted file mode 100644 index 69c90988c5..0000000000 --- a/evm/tests/basic_smart_contract.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// Test a simple token transfer to a new address. -#[test] -#[ignore] // Too slow to run on CI. -fn test_basic_smart_contract() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let push1 = get_push_opcode(1); - let add = get_opcode("ADD"); - let stop = get_opcode("STOP"); - let code = [push1, 3, push1, 4, add, stop]; - let code_gas = 3 + 3 + 3; - let code_hash = keccak(code); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - code_hash, - ..AccountRlp::default() - }; - - let state_trie_before = { - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[beneficiary_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&beneficiary_account_before).to_vec(), - } - .into(); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_before).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - } - .into(); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![], - }; - - let txdata_gas = 2 * 16; - let gas_used = 21_000 + code_gas + txdata_gas; - - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_difficulty: 0x20000.into(), - block_number: 1.into(), - block_chain_id: 1.into(), - block_timestamp: 0x03e8.into(), - block_gaslimit: 0xff112233u32.into(), - block_gas_used: gas_used.into(), - block_bloom: [0.into(); 8], - block_base_fee: 0xa.into(), - block_random: Default::default(), - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let expected_state_trie_after: HashedPartialTrie = { - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: to_account_before.balance + value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[beneficiary_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: beneficiary_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&beneficiary_account_after).to_vec(), - } - .into(); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - } - .into(); - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs deleted file mode 100644 index 8f482f72dc..0000000000 --- a/evm/tests/empty_txn_list.rs +++ /dev/null @@ -1,150 +0,0 @@ -use core::marker::PhantomData; -use std::collections::HashMap; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{BigEndianHash, H256}; -use keccak_hash::keccak; -use log::info; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::PoseidonGoldilocksConfig; -use plonky2::util::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer}; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, PublicValues, TrieRoots}; -use plonky2_evm::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = PoseidonGoldilocksConfig; - -/// Execute the empty list of transactions, i.e. a no-op. -#[test] -#[ignore] // Too slow to run on CI. -fn test_empty_txn_list() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let block_metadata = BlockMetadata { - block_number: 1.into(), - ..Default::default() - }; - - let state_trie = HashedPartialTrie::from(Node::Empty); - let transactions_trie = HashedPartialTrie::from(Node::Empty); - let receipts_trie = HashedPartialTrie::from(Node::Empty); - let storage_tries = vec![]; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - // No transactions, so no trie roots change. - let trie_roots_after = TrieRoots { - state_root: state_trie.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let mut initial_block_hashes = vec![H256::default(); 256]; - initial_block_hashes[255] = H256::from_uint(&0x200.into()); - let inputs = GenerationInputs { - signed_txn: None, - withdrawals: vec![], - tries: TrieInputs { - state_trie, - transactions_trie, - receipts_trie, - storage_tries, - }, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0.into(), - block_hashes: BlockHashes { - prev_hashes: initial_block_hashes, - cur_hash: H256::default(), - }, - }; - - // Initialize the preprocessed circuits for the zkEVM. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 9..11, 12..13, 14..15, 9..11, 12..13, 17..18], // Minimal ranges to prove an empty list - &config, - ); - - { - let gate_serializer = DefaultGateSerializer; - let generator_serializer = DefaultGeneratorSerializer:: { - _phantom: PhantomData::, - }; - - let timing = TimingTree::new("serialize AllRecursiveCircuits", log::Level::Info); - let all_circuits_bytes = all_circuits - .to_bytes(false, &gate_serializer, &generator_serializer) - .map_err(|_| anyhow::Error::msg("AllRecursiveCircuits serialization failed."))?; - timing.filter(Duration::from_millis(100)).print(); - info!( - "AllRecursiveCircuits length: {} bytes", - all_circuits_bytes.len() - ); - - let timing = TimingTree::new("deserialize AllRecursiveCircuits", log::Level::Info); - let all_circuits_from_bytes = AllRecursiveCircuits::::from_bytes( - &all_circuits_bytes, - false, - &gate_serializer, - &generator_serializer, - ) - .map_err(|_| anyhow::Error::msg("AllRecursiveCircuits deserialization failed."))?; - timing.filter(Duration::from_millis(100)).print(); - - assert_eq!(all_circuits, all_circuits_from_bytes); - } - - let mut timing = TimingTree::new("prove", log::Level::Info); - let (root_proof, public_values) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - all_circuits.verify_root(root_proof.clone())?; - - // Test retrieved public values from the proof public inputs. - let retrieved_public_values = PublicValues::from_public_inputs(&root_proof.public_inputs); - assert_eq!(retrieved_public_values, public_values); - - // We can duplicate the proofs here because the state hasn't mutated. - let (agg_proof, agg_public_values) = all_circuits.prove_aggregation( - false, - &root_proof, - public_values.clone(), - false, - &root_proof, - public_values, - )?; - all_circuits.verify_aggregation(&agg_proof)?; - - // Test retrieved public values from the proof public inputs. - let retrieved_public_values = PublicValues::from_public_inputs(&agg_proof.public_inputs); - assert_eq!(retrieved_public_values, agg_public_values); - - let (block_proof, block_public_values) = - all_circuits.prove_block(None, &agg_proof, agg_public_values)?; - all_circuits.verify_block(&block_proof)?; - - // Test retrieved public values from the proof public inputs. - let retrieved_public_values = PublicValues::from_public_inputs(&block_proof.public_inputs); - assert_eq!(retrieved_public_values, block_public_values); - - // Get the verifier associated to these preprocessed circuits, and have it verify the block_proof. - let verifier = all_circuits.final_verifier_data(); - verifier.verify(block_proof) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/erc20.rs b/evm/tests/erc20.rs deleted file mode 100644 index 430da14d5e..0000000000 --- a/evm/tests/erc20.rs +++ /dev/null @@ -1,285 +0,0 @@ -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// Test a simple ERC20 transfer. -/// Used the following Solidity code: -/// ```solidity -/// pragma solidity ^0.8.13; -/// import "../lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; -/// contract Token is ERC20 { -/// constructor() ERC20("Token", "TKN") { -/// _mint(msg.sender, 1_000_000 ether); -/// } -/// } -/// contract Giver { -/// Token public token; -/// constructor(address _token) { -/// token = Token(_token); -/// } -/// function send(uint256 amount) public { -/// token.transfer(0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326, amount); -/// } -/// } -/// ``` -#[test] -fn test_erc20() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("70997970C51812dc3A010C7d01b50e0d17dc79C8"); - let giver = hex!("e7f1725E7734CE288F8367e1Bb143E90bb3F0512"); - let token = hex!("5FbDB2315678afecb367f032d93F642f64180aa3"); - - let sender_state_key = keccak(sender); - let giver_state_key = keccak(giver); - let token_state_key = keccak(token); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let giver_nibbles = Nibbles::from_bytes_be(giver_state_key.as_bytes()).unwrap(); - let token_nibbles = Nibbles::from_bytes_be(token_state_key.as_bytes()).unwrap(); - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account()).to_vec()); - state_trie_before.insert(giver_nibbles, rlp::encode(&giver_account()).to_vec()); - state_trie_before.insert(token_nibbles, rlp::encode(&token_account()).to_vec()); - - let storage_tries = vec![ - (giver_state_key, giver_storage()), - (token_state_key, token_storage()), - ]; - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, - }; - - let txn = signed_tx(); - - let gas_used = 56_499.into(); - let bloom = bloom(); - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: gas_used, - block_bloom: bloom, - }; - - let contract_code = [giver_bytecode(), token_bytecode(), vec![]] - .map(|v| (keccak(v.clone()), v)) - .into(); - - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); - let sender_account = sender_account(); - let sender_account_after = AccountRlp { - nonce: sender_account.nonce + 1, - balance: sender_account.balance - gas_used * 0xa, - ..sender_account - }; - state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - state_trie_after.insert(giver_nibbles, rlp::encode(&giver_account()).to_vec()); - let token_account_after = AccountRlp { - storage_root: token_storage_after().hash(), - ..token_account() - }; - state_trie_after.insert(token_nibbles, rlp::encode(&token_account_after).to_vec()); - - state_trie_after - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used, - bloom: bloom_bytes().to_vec().into(), - logs: vec![LogRlp { - address: H160::from_str("0x5fbdb2315678afecb367f032d93f642f64180aa3").unwrap(), - topics: vec![ - H256::from_str( - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - ) - .unwrap(), - H256::from_str( - "0x000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f0512", - ) - .unwrap(), - H256::from_str( - "0x0000000000000000000000001f9090aae28b8a3dceadf281b0f12828e676c326", - ) - .unwrap(), - ], - data: hex!("0000000000000000000000000000000000000000000000056bc75e2d63100000") - .to_vec() - .into(), - }], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert(Nibbles::from_str("0x80").unwrap(), receipt_0.encode(2)); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used, - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} - -fn giver_bytecode() -> Vec { - hex!("608060405234801561001057600080fd5b50600436106100365760003560e01c8063a52c101e1461003b578063fc0c546a14610050575b600080fd5b61004e61004936600461010c565b61007f565b005b600054610063906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b60005460405163a9059cbb60e01b8152731f9090aae28b8a3dceadf281b0f12828e676c3266004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156100e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101089190610125565b5050565b60006020828403121561011e57600080fd5b5035919050565b60006020828403121561013757600080fd5b8151801515811461014757600080fd5b939250505056fea264697066735822122050741efdbac11eb0bbb776ce3ac6004e596b7d7559658a12506164388c371cfd64736f6c63430008140033").into() -} - -fn token_bytecode() -> Vec { - hex!("608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce567146100fe57806370a082311461010d57806395d89b4114610136578063a9059cbb1461013e578063dd62ed3e1461015157600080fd5b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100d957806323b872dd146100eb575b600080fd5b6100a061018a565b6040516100ad919061056a565b60405180910390f35b6100c96100c43660046105d4565b61021c565b60405190151581526020016100ad565b6002545b6040519081526020016100ad565b6100c96100f93660046105fe565b610236565b604051601281526020016100ad565b6100dd61011b36600461063a565b6001600160a01b031660009081526020819052604090205490565b6100a061025a565b6100c961014c3660046105d4565b610269565b6100dd61015f36600461065c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101999061068f565b80601f01602080910402602001604051908101604052809291908181526020018280546101c59061068f565b80156102125780601f106101e757610100808354040283529160200191610212565b820191906000526020600020905b8154815290600101906020018083116101f557829003601f168201915b5050505050905090565b60003361022a818585610277565b60019150505b92915050565b600033610244858285610289565b61024f85858561030c565b506001949350505050565b6060600480546101999061068f565b60003361022a81858561030c565b610284838383600161036b565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461030657818110156102f757604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6103068484848403600061036b565b50505050565b6001600160a01b03831661033657604051634b637e8f60e11b8152600060048201526024016102ee565b6001600160a01b0382166103605760405163ec442f0560e01b8152600060048201526024016102ee565b610284838383610440565b6001600160a01b0384166103955760405163e602df0560e01b8152600060048201526024016102ee565b6001600160a01b0383166103bf57604051634a1406b160e11b8152600060048201526024016102ee565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561030657826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161043291815260200190565b60405180910390a350505050565b6001600160a01b03831661046b57806002600082825461046091906106c9565b909155506104dd9050565b6001600160a01b038316600090815260208190526040902054818110156104be5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102ee565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166104f957600280548290039055610518565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161055d91815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156105975785810183015185820160400152820161057b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146105cf57600080fd5b919050565b600080604083850312156105e757600080fd5b6105f0836105b8565b946020939093013593505050565b60008060006060848603121561061357600080fd5b61061c846105b8565b925061062a602085016105b8565b9150604084013590509250925092565b60006020828403121561064c57600080fd5b610655826105b8565b9392505050565b6000806040838503121561066f57600080fd5b610678836105b8565b9150610686602084016105b8565b90509250929050565b600181811c908216806106a357607f821691505b6020821081036106c357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561023057634e487b7160e01b600052601160045260246000fdfea2646970667358221220266a323ae4a816f6c6342a5be431fedcc0d45c44b02ea75f5474eb450b5d45b364736f6c63430008140033").into() -} - -fn insert_storage(trie: &mut HashedPartialTrie, slot: U256, value: U256) { - let mut bytes = [0; 32]; - slot.to_big_endian(&mut bytes); - let key = keccak(bytes); - let nibbles = Nibbles::from_bytes_be(key.as_bytes()).unwrap(); - let r = rlp::encode(&value); - let r = r.freeze().to_vec(); - trie.insert(nibbles, r); -} - -fn sd2u(s: &str) -> U256 { - U256::from_dec_str(s).unwrap() -} - -fn giver_storage() -> HashedPartialTrie { - let mut trie = HashedPartialTrie::from(Node::Empty); - insert_storage( - &mut trie, - U256::zero(), - sd2u("546584486846459126461364135121053344201067465379"), - ); - trie -} - -fn token_storage() -> HashedPartialTrie { - let mut trie = HashedPartialTrie::from(Node::Empty); - insert_storage( - &mut trie, - sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), - sd2u("1000000000000000000000"), - ); - trie -} - -fn token_storage_after() -> HashedPartialTrie { - let mut trie = HashedPartialTrie::from(Node::Empty); - insert_storage( - &mut trie, - sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), - sd2u("900000000000000000000"), - ); - insert_storage( - &mut trie, - sd2u("53006154680716014998529145169423020330606407246856709517064848190396281160729"), - sd2u("100000000000000000000"), - ); - trie -} - -fn giver_account() -> AccountRlp { - AccountRlp { - nonce: 1.into(), - balance: 0.into(), - storage_root: giver_storage().hash(), - code_hash: keccak(giver_bytecode()), - } -} - -fn token_account() -> AccountRlp { - AccountRlp { - nonce: 1.into(), - balance: 0.into(), - storage_root: token_storage().hash(), - code_hash: keccak(token_bytecode()), - } -} - -fn sender_account() -> AccountRlp { - AccountRlp { - nonce: 0.into(), - balance: sd2u("10000000000000000000000"), - storage_root: Default::default(), - code_hash: keccak([]), - } -} - -fn signed_tx() -> Vec { - hex!("02f88701800a0a830142c594e7f1725e7734ce288f8367e1bb143e90bb3f051280a4a52c101e0000000000000000000000000000000000000000000000056bc75e2d63100000c001a0303f5591159d7ea303faecb1c8bd8624b55732f769de28b111190dfb9a7c5234a019d5d6d38938dc1c63acbe106cf361672def773ace4ca587860117d057326627").into() -} - -fn bloom_bytes() -> [u8; 256] { - hex!("00000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000002000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000020000000000080000000000000000000000000000000000000000000000000000000000000000") -} - -fn bloom() -> [U256; 8] { - let bloom = bloom_bytes() - .chunks_exact(32) - .map(U256::from_big_endian) - .collect::>(); - bloom.try_into().unwrap() -} diff --git a/evm/tests/erc721.rs b/evm/tests/erc721.rs deleted file mode 100644 index 4dfed24958..0000000000 --- a/evm/tests/erc721.rs +++ /dev/null @@ -1,312 +0,0 @@ -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// Test a simple ERC721 token transfer. -/// Used the following Solidity code: -/// ```solidity -/// pragma solidity ^0.8.20; -/// -/// import "@openzeppelin/contracts@5.0.1/token/ERC721/ERC721.sol"; -/// import "@openzeppelin/contracts@5.0.1/access/Ownable.sol"; -/// -/// contract TestToken is ERC721, Ownable { -/// constructor(address initialOwner) -/// ERC721("TestToken", "TEST") -/// Ownable(initialOwner) -/// {} -/// -/// function safeMint(address to, uint256 tokenId) public onlyOwner { -/// _safeMint(to, tokenId); -/// } -/// } -/// ``` -/// -/// The transaction calls the `safeTransferFrom` function to transfer token `1337` from address -/// `0x5B38Da6a701c568545dCfcB03FcB875f56beddC4` to address `0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2`. -#[test] -fn test_erc721() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let owner = hex!("5B38Da6a701c568545dCfcB03FcB875f56beddC4"); - let contract = hex!("f2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9"); - - let owner_state_key = keccak(owner); - let contract_state_key = keccak(contract); - - let owner_nibbles = Nibbles::from_bytes_be(owner_state_key.as_bytes()).unwrap(); - let contract_nibbles = Nibbles::from_bytes_be(contract_state_key.as_bytes()).unwrap(); - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert(owner_nibbles, rlp::encode(&owner_account()).to_vec()); - state_trie_before.insert(contract_nibbles, rlp::encode(&contract_account()).to_vec()); - - let storage_tries = vec![(contract_state_key, contract_storage())]; - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, - }; - - let txn = signed_tx(); - - let gas_used = 58_418.into(); - - let contract_code = [contract_bytecode(), vec![]] - .map(|v| (keccak(v.clone()), v)) - .into(); - - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); - let owner_account = owner_account(); - let owner_account_after = AccountRlp { - nonce: owner_account.nonce + 1, - balance: owner_account.balance - gas_used * 0xa, - ..owner_account - }; - state_trie_after.insert(owner_nibbles, rlp::encode(&owner_account_after).to_vec()); - let contract_account_after = AccountRlp { - storage_root: contract_storage_after().hash(), - ..contract_account() - }; - state_trie_after.insert( - contract_nibbles, - rlp::encode(&contract_account_after).to_vec(), - ); - - state_trie_after - }; - - let logs = vec![LogRlp { - address: H160::from_str("0xf2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9").unwrap(), - topics: vec![ - H256::from_str("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") - .unwrap(), - H256::from_str("0x0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4") - .unwrap(), - H256::from_str("0x000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2") - .unwrap(), - H256::from_str("0x0000000000000000000000000000000000000000000000000000000000000539") - .unwrap(), - ], - data: vec![].into(), - }]; - - let mut bloom_bytes = [0u8; 256]; - add_logs_to_bloom(&mut bloom_bytes, &logs); - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used, - bloom: bloom_bytes.to_vec().into(), - logs, - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert(Nibbles::from_str("0x80").unwrap(), receipt_0.encode(0)); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let bloom = bloom_bytes - .chunks_exact(32) - .map(U256::from_big_endian) - .collect::>(); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: gas_used, - block_bloom: bloom.try_into().unwrap(), - }; - - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used, - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} - -fn contract_bytecode() -> Vec { - hex!("608060405234801561000f575f80fd5b5060043610610109575f3560e01c8063715018a6116100a0578063a22cb4651161006f578063a22cb465146102a1578063b88d4fde146102bd578063c87b56dd146102d9578063e985e9c514610309578063f2fde38b1461033957610109565b8063715018a61461023f5780638da5cb5b1461024957806395d89b4114610267578063a14481941461028557610109565b806323b872dd116100dc57806323b872dd146101a757806342842e0e146101c35780636352211e146101df57806370a082311461020f57610109565b806301ffc9a71461010d57806306fdde031461013d578063081812fc1461015b578063095ea7b31461018b575b5f80fd5b61012760048036038101906101229190611855565b610355565b604051610134919061189a565b60405180910390f35b610145610436565b604051610152919061193d565b60405180910390f35b61017560048036038101906101709190611990565b6104c5565b60405161018291906119fa565b60405180910390f35b6101a560048036038101906101a09190611a3d565b6104e0565b005b6101c160048036038101906101bc9190611a7b565b6104f6565b005b6101dd60048036038101906101d89190611a7b565b6105f5565b005b6101f960048036038101906101f49190611990565b610614565b60405161020691906119fa565b60405180910390f35b61022960048036038101906102249190611acb565b610625565b6040516102369190611b05565b60405180910390f35b6102476106db565b005b6102516106ee565b60405161025e91906119fa565b60405180910390f35b61026f610716565b60405161027c919061193d565b60405180910390f35b61029f600480360381019061029a9190611a3d565b6107a6565b005b6102bb60048036038101906102b69190611b48565b6107bc565b005b6102d760048036038101906102d29190611cb2565b6107d2565b005b6102f360048036038101906102ee9190611990565b6107ef565b604051610300919061193d565b60405180910390f35b610323600480360381019061031e9190611d32565b610855565b604051610330919061189a565b60405180910390f35b610353600480360381019061034e9190611acb565b6108e3565b005b5f7f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061041f57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061042f575061042e82610967565b5b9050919050565b60605f805461044490611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461047090611d9d565b80156104bb5780601f10610492576101008083540402835291602001916104bb565b820191905f5260205f20905b81548152906001019060200180831161049e57829003601f168201915b5050505050905090565b5f6104cf826109d0565b506104d982610a56565b9050919050565b6104f282826104ed610a8f565b610a96565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610566575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161055d91906119fa565b60405180910390fd5b5f6105798383610574610a8f565b610aa8565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146105ef578382826040517f64283d7b0000000000000000000000000000000000000000000000000000000081526004016105e693929190611dcd565b60405180910390fd5b50505050565b61060f83838360405180602001604052805f8152506107d2565b505050565b5f61061e826109d0565b9050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610696575f6040517f89c62b6400000000000000000000000000000000000000000000000000000000815260040161068d91906119fa565b60405180910390fd5b60035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b6106e3610cb3565b6106ec5f610d3a565b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606001805461072590611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461075190611d9d565b801561079c5780601f106107735761010080835404028352916020019161079c565b820191905f5260205f20905b81548152906001019060200180831161077f57829003601f168201915b5050505050905090565b6107ae610cb3565b6107b88282610dfd565b5050565b6107ce6107c7610a8f565b8383610e1a565b5050565b6107dd8484846104f6565b6107e984848484610f83565b50505050565b60606107fa826109d0565b505f610804611135565b90505f8151116108225760405180602001604052805f81525061084d565b8061082c8461114b565b60405160200161083d929190611e3c565b6040516020818303038152906040525b915050919050565b5f60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b6108eb610cb3565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361095b575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161095291906119fa565b60405180910390fd5b61096481610d3a565b50565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f806109db83611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610a4d57826040517f7e273289000000000000000000000000000000000000000000000000000000008152600401610a449190611b05565b60405180910390fd5b80915050919050565b5f60045f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f33905090565b610aa3838383600161124e565b505050565b5f80610ab384611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614610af457610af381848661140d565b5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610b7f57610b335f855f8061124e565b600160035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825403925050819055505b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610bfe57600160035f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8460025f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4809150509392505050565b610cbb610a8f565b73ffffffffffffffffffffffffffffffffffffffff16610cd96106ee565b73ffffffffffffffffffffffffffffffffffffffff1614610d3857610cfc610a8f565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401610d2f91906119fa565b60405180910390fd5b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160065f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610e16828260405180602001604052805f8152506114d0565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610e8a57816040517f5b08ba18000000000000000000000000000000000000000000000000000000008152600401610e8191906119fa565b60405180910390fd5b8060055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610f76919061189a565b60405180910390a3505050565b5f8373ffffffffffffffffffffffffffffffffffffffff163b111561112f578273ffffffffffffffffffffffffffffffffffffffff1663150b7a02610fc6610a8f565b8685856040518563ffffffff1660e01b8152600401610fe89493929190611eb1565b6020604051808303815f875af192505050801561102357506040513d601f19601f820116820180604052508101906110209190611f0f565b60015b6110a4573d805f8114611051576040519150601f19603f3d011682016040523d82523d5f602084013e611056565b606091505b505f81510361109c57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161109391906119fa565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461112d57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161112491906119fa565b60405180910390fd5b505b50505050565b606060405180602001604052805f815250905090565b60605f6001611159846114eb565b0190505f8167ffffffffffffffff81111561117757611176611b8e565b5b6040519080825280601f01601f1916602001820160405280156111a95781602001600182028036833780820191505090505b5090505f82602001820190505b60011561120a578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816111ff576111fe611f3a565b5b0494505f85036111b6575b819350505050919050565b5f60025f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b808061128657505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156113b8575f611295846109d0565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156112ff57508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561131257506113108184610855565b155b1561135457826040517fa9fbf51f00000000000000000000000000000000000000000000000000000000815260040161134b91906119fa565b60405180910390fd5b81156113b657838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b8360045f8581526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b61141883838361163c565b6114cb575f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361148c57806040517f7e2732890000000000000000000000000000000000000000000000000000000081526004016114839190611b05565b60405180910390fd5b81816040517f177e802f0000000000000000000000000000000000000000000000000000000081526004016114c2929190611f67565b60405180910390fd5b505050565b6114da83836116fc565b6114e65f848484610f83565b505050565b5f805f90507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611547577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161153d5761153c611f3a565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310611584576d04ee2d6d415b85acef8100000000838161157a57611579611f3a565b5b0492506020810190505b662386f26fc1000083106115b357662386f26fc1000083816115a9576115a8611f3a565b5b0492506010810190505b6305f5e10083106115dc576305f5e10083816115d2576115d1611f3a565b5b0492506008810190505b61271083106116015761271083816115f7576115f6611f3a565b5b0492506004810190505b60648310611624576064838161161a57611619611f3a565b5b0492506002810190505b600a8310611633576001810190505b80915050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156116f357508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806116b457506116b38484610855565b5b806116f257508273ffffffffffffffffffffffffffffffffffffffff166116da83610a56565b73ffffffffffffffffffffffffffffffffffffffff16145b5b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361176c575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161176391906119fa565b60405180910390fd5b5f61177883835f610aa8565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146117ea575f6040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081526004016117e191906119fa565b60405180910390fd5b505050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61183481611800565b811461183e575f80fd5b50565b5f8135905061184f8161182b565b92915050565b5f6020828403121561186a576118696117f8565b5b5f61187784828501611841565b91505092915050565b5f8115159050919050565b61189481611880565b82525050565b5f6020820190506118ad5f83018461188b565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156118ea5780820151818401526020810190506118cf565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61190f826118b3565b61191981856118bd565b93506119298185602086016118cd565b611932816118f5565b840191505092915050565b5f6020820190508181035f8301526119558184611905565b905092915050565b5f819050919050565b61196f8161195d565b8114611979575f80fd5b50565b5f8135905061198a81611966565b92915050565b5f602082840312156119a5576119a46117f8565b5b5f6119b28482850161197c565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6119e4826119bb565b9050919050565b6119f4816119da565b82525050565b5f602082019050611a0d5f8301846119eb565b92915050565b611a1c816119da565b8114611a26575f80fd5b50565b5f81359050611a3781611a13565b92915050565b5f8060408385031215611a5357611a526117f8565b5b5f611a6085828601611a29565b9250506020611a718582860161197c565b9150509250929050565b5f805f60608486031215611a9257611a916117f8565b5b5f611a9f86828701611a29565b9350506020611ab086828701611a29565b9250506040611ac18682870161197c565b9150509250925092565b5f60208284031215611ae057611adf6117f8565b5b5f611aed84828501611a29565b91505092915050565b611aff8161195d565b82525050565b5f602082019050611b185f830184611af6565b92915050565b611b2781611880565b8114611b31575f80fd5b50565b5f81359050611b4281611b1e565b92915050565b5f8060408385031215611b5e57611b5d6117f8565b5b5f611b6b85828601611a29565b9250506020611b7c85828601611b34565b9150509250929050565b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b611bc4826118f5565b810181811067ffffffffffffffff82111715611be357611be2611b8e565b5b80604052505050565b5f611bf56117ef565b9050611c018282611bbb565b919050565b5f67ffffffffffffffff821115611c2057611c1f611b8e565b5b611c29826118f5565b9050602081019050919050565b828183375f83830152505050565b5f611c56611c5184611c06565b611bec565b905082815260208101848484011115611c7257611c71611b8a565b5b611c7d848285611c36565b509392505050565b5f82601f830112611c9957611c98611b86565b5b8135611ca9848260208601611c44565b91505092915050565b5f805f8060808587031215611cca57611cc96117f8565b5b5f611cd787828801611a29565b9450506020611ce887828801611a29565b9350506040611cf98782880161197c565b925050606085013567ffffffffffffffff811115611d1a57611d196117fc565b5b611d2687828801611c85565b91505092959194509250565b5f8060408385031215611d4857611d476117f8565b5b5f611d5585828601611a29565b9250506020611d6685828601611a29565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611db457607f821691505b602082108103611dc757611dc6611d70565b5b50919050565b5f606082019050611de05f8301866119eb565b611ded6020830185611af6565b611dfa60408301846119eb565b949350505050565b5f81905092915050565b5f611e16826118b3565b611e208185611e02565b9350611e308185602086016118cd565b80840191505092915050565b5f611e478285611e0c565b9150611e538284611e0c565b91508190509392505050565b5f81519050919050565b5f82825260208201905092915050565b5f611e8382611e5f565b611e8d8185611e69565b9350611e9d8185602086016118cd565b611ea6816118f5565b840191505092915050565b5f608082019050611ec45f8301876119eb565b611ed160208301866119eb565b611ede6040830185611af6565b8181036060830152611ef08184611e79565b905095945050505050565b5f81519050611f098161182b565b92915050565b5f60208284031215611f2457611f236117f8565b5b5f611f3184828501611efb565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f604082019050611f7a5f8301856119eb565b611f876020830184611af6565b939250505056fea2646970667358221220432b30673e00c0eb009e1718c271f4cfdfbeded17345829703b06d322360990164736f6c63430008160033").into() -} - -fn insert_storage(trie: &mut HashedPartialTrie, slot: U256, value: U256) { - let mut bytes = [0; 32]; - slot.to_big_endian(&mut bytes); - let key = keccak(bytes); - let nibbles = Nibbles::from_bytes_be(key.as_bytes()).unwrap(); - let r = rlp::encode(&value); - let r = r.freeze().to_vec(); - trie.insert(nibbles, r); -} - -fn sd2u(s: &str) -> U256 { - U256::from_dec_str(s).unwrap() -} - -fn sh2u(s: &str) -> U256 { - U256::from_str_radix(s, 16).unwrap() -} - -fn contract_storage() -> HashedPartialTrie { - let mut trie = HashedPartialTrie::from(Node::Empty); - insert_storage( - &mut trie, - U256::zero(), - sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), - ); - insert_storage( - &mut trie, - U256::one(), - sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), - ); - insert_storage( - &mut trie, - sd2u("6"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ); - insert_storage( - &mut trie, - sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ); - insert_storage( - &mut trie, - sh2u("0x118c1ea466562cb796e30ef705e4db752f5c39d773d22c5efd8d46f67194e78a"), - sd2u("1"), - ); - trie -} - -fn contract_storage_after() -> HashedPartialTrie { - let mut trie = HashedPartialTrie::from(Node::Empty); - insert_storage( - &mut trie, - U256::zero(), - sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), - ); - insert_storage( - &mut trie, - U256::one(), - sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), - ); - insert_storage( - &mut trie, - sd2u("6"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ); - insert_storage( - &mut trie, - sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), - sh2u("0xab8483f64d9c6d1ecf9b849ae677dd3315835cb2"), - ); - insert_storage( - &mut trie, - sh2u("0xf3aa6a8a9f7e3707e36cc99c499a27514922afe861ec3d80a1a314409cba92f9"), - sd2u("1"), - ); - trie -} - -fn owner_account() -> AccountRlp { - AccountRlp { - nonce: 2.into(), - balance: 0x1000000.into(), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - } -} - -fn contract_account() -> AccountRlp { - AccountRlp { - nonce: 0.into(), - balance: 0.into(), - storage_root: contract_storage().hash(), - code_hash: keccak(contract_bytecode()), - } -} - -fn signed_tx() -> Vec { - hex!("f8c5020a8307a12094f2b1114c644cbb3ff63bf1dd284c8cd716e95be980b86442842e0e0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2000000000000000000000000000000000000000000000000000000000000053925a0414867f13ac63d663e84099d52c8215615666ea37c969c69aa58a0fad26a3f6ea01a7160c6274969083b2316eb8ca6011b4bf6b00972159a78bf64d06fa40c1402").into() -} - -fn add_logs_to_bloom(bloom: &mut [u8; 256], logs: &Vec) { - for log in logs { - add_to_bloom(bloom, log.address.as_bytes()); - for topic in &log.topics { - add_to_bloom(bloom, topic.as_bytes()); - } - } -} - -fn add_to_bloom(bloom: &mut [u8; 256], bloom_entry: &[u8]) { - let bloom_hash = keccak(bloom_entry).to_fixed_bytes(); - - for idx in 0..3 { - let bit_pair = u16::from_be_bytes(bloom_hash[2 * idx..2 * (idx + 1)].try_into().unwrap()); - let bit_to_set = 0x07FF - (bit_pair & 0x07FF); - let byte_index = bit_to_set / 8; - let bit_value = 1 << (7 - bit_to_set % 8); - bloom[byte_index as usize] |= bit_value; - } -} diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs deleted file mode 100644 index a95473fcb0..0000000000 --- a/evm/tests/log_opcode.rs +++ /dev/null @@ -1,771 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; - -use bytes::Bytes; -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::PoseidonGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::transaction_testing::{AddressOption, LegacyTransactionRlp}; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp, LogRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = PoseidonGoldilocksConfig; - -/// Variation of `add11_yml` testing LOG opcodes. -#[test] -#[ignore] // Too slow to run on CI. -fn test_log_opcodes() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender = hex!("af1276cbb260bb13deddb4209ae99ae6e497f446"); - // Private key: DCDFF53B4F013DBCDC717F89FE3BF4D8B10512AAE282B48E01D7530470382701 - let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - - // For the first code transaction code, we consider two LOG opcodes. The first deals with 0 topics and empty data. The second deals with two topics, and data of length 5, stored in memory. - let code = [ - 0x64, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0x60, 0x0, 0x52, // MSTORE(0x0, 0xA1B2C3D4E5) - 0x60, 0x0, 0x60, 0x0, 0xA0, // LOG0(0x0, 0x0) - 0x60, 99, 0x60, 98, 0x60, 5, 0x60, 27, 0xA2, // LOG2(27, 5, 98, 99) - 0x00, - ]; - println!("contract: {:02x?}", code); - let code_gas = 3 + 3 + 3 // PUSHs and MSTORE - + 3 + 3 + 375 // PUSHs and LOG0 - + 3 + 3 + 3 + 3 + 375 + 375*2 + 8*5 + 3// PUSHs, LOG2 and memory expansion - ; - let gas_used = 21_000 + code_gas; - - let code_hash = keccak(code); - - // Set accounts before the transaction. - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - - let sender_balance_before = 5000000000000000u64; - let sender_account_before = AccountRlp { - balance: sender_balance_before.into(), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - balance: 9000000000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - // Initialize the state trie with three accounts. - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - // We now add two receipts with logs and data. This updates the receipt trie as well. - let log_0 = LogRlp { - address: hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into(), - topics: vec![ - hex!("8a22ee899102a366ac8ad0495127319cb1ff2403cfae855f83a89cda1266674d").into(), - hex!("000000000000000000000000000000000000000000000000000000000000002a").into(), - hex!("0000000000000000000000000000000000000000000000000000000000bd9fe6").into(), - ], - data: hex!("f7af1cc94b1aef2e0fa15f1b4baefa86eb60e78fa4bd082372a0a446d197fb58") - .to_vec() - .into(), - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 0x016e5bu64.into(), - bloom: hex!("00000000000000000000000000000000000000000000000000800000000000000040000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000080008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000020000000000008000000000000000000000000").to_vec().into(), - logs: vec![log_0], - }; - - // Insert the first receipt into the initial receipt trie. The initial receipts trie has an initial node with a random nibble. - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x1337").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: receipts_trie.clone(), - storage_tries: vec![(to_hashed, Node::Empty.into())], - }; - - // Prove a transaction which carries out two LOG opcodes. - let txn_gas_price = 10; - let txn = hex!("f860800a830186a094095e7baea6a6c7c4c2dfeb977efac326af552d87808026a0c3040cb042c541f9440771879b6bbf3f91464b265431de87eea1ec3206350eb8a046f5f3d06b8816f19f24ee919fd84bfb736db71df10a72fba4495f479e96f678"); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xffffffffu32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 0.into(), - block_bloom: [0.into(); 8], - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - // Update the state and receipt tries after the transaction, so that we have the correct expected tries: - // Update accounts - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - - let sender_balance_after = sender_balance_before - gas_used * txn_gas_price; - let sender_account_after = AccountRlp { - balance: sender_balance_after.into(), - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - balance: 9000000000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - // Update the receipt trie. - let first_log = LogRlp { - address: to.into(), - topics: vec![], - data: Bytes::new(), - }; - - let second_log = LogRlp { - address: to.into(), - topics: vec![ - hex!("0000000000000000000000000000000000000000000000000000000000000062").into(), // dec: 98 - hex!("0000000000000000000000000000000000000000000000000000000000000063").into(), // dec: 99 - ], - data: hex!("a1b2c3d4e5").to_vec().into(), - }; - - let receipt = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used.into(), - bloom: hex!("00000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000004000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000400000000000040000000000000000000000000002000000000000000000000000000").to_vec().into(), - logs: vec![first_log, second_log], - }; - - let receipt_nibbles = Nibbles::from_str("0x80").unwrap(); // RLP(0) = 0x80 - - receipts_trie.insert(receipt_nibbles, rlp::encode(&receipt).to_vec()); - - // Update the state trie. - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used.into(), - - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - // Assert that the proof leads to the correct state and receipt roots. - assert_eq!( - proof.public_values.trie_roots_after.state_root, - expected_state_trie_after.hash() - ); - - assert_eq!( - proof.public_values.trie_roots_after.receipts_root, - receipts_trie.hash() - ); - - verify_proof(&all_stark, proof, &config) -} - -// Tests proving two transactions, one of which with logs, and aggregating them. -#[test] -#[ignore] // Too slow to run on CI. -fn test_log_with_aggreg() -> anyhow::Result<()> { - init_logger(); - - let code = [ - 0x64, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0x60, 0x0, 0x52, // MSTORE(0x0, 0xA1B2C3D4E5) - 0x60, 0x0, 0x60, 0x0, 0xA0, // LOG0(0x0, 0x0) - 0x60, 99, 0x60, 98, 0x60, 5, 0x60, 27, 0xA2, // LOG2(27, 5, 98, 99) - 0x00, - ]; - - let code_gas = 3 + 3 + 3 // PUSHs and MSTORE - + 3 + 3 + 375 // PUSHs and LOG0 - + 3 + 3 + 3 + 3 + 375 + 375*2 + 8*5 // PUSHs and LOG2 - + 3 // Memory expansion - ; - - let gas_used = 21_000 + code_gas; - - let code_hash = keccak(code); - - // First transaction. - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender_first = hex!("af1276cbb260bb13deddb4209ae99ae6e497f446"); - let to_first = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552e89"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender_first); - let to_hashed = keccak(to_first); - let to_hashed_2 = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - let to_second_nibbles = Nibbles::from_bytes_be(to_hashed_2.as_bytes()).unwrap(); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_balance_before = 1000000000000000000u64.into(); - let sender_account_before = AccountRlp { - balance: sender_balance_before, - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - ..AccountRlp::default() - }; - let to_account_second_before = AccountRlp { - code_hash, - ..AccountRlp::default() - }; - - // In the first transaction, the sender account sends `txn_value` to `to_account`. - let gas_price = 10; - let txn_value = 0xau64; - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - state_trie_before.insert( - to_second_nibbles, - rlp::encode(&to_account_second_before).to_vec(), - ); - let checkpoint_state_trie_root = state_trie_before.hash(); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![], - }; - - let txn = hex!("f85f800a82520894095e7baea6a6c7c4c2dfeb977efac326af552d870a8026a0122f370ed4023a6c253350c6bfb87d7d7eb2cd86447befee99e0a26b70baec20a07100ab1b3977f2b4571202b9f4b68850858caf5469222794600b5ce1cfb348ad"); - - let block_1_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_gaslimit: 0x445566u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: (22570 + 21000).into(), - block_bloom: [ - 0.into(), - 0.into(), - U256::from_dec_str( - "55213970774324510299479508399853534522527075462195808724319849722937344", - ) - .unwrap(), - U256::from_dec_str("1361129467683753853853498429727072845824").unwrap(), - 33554432.into(), - U256::from_dec_str("9223372036854775808").unwrap(), - U256::from_dec_str( - "3618502788666131106986593281521497120414687020801267626233049500247285563392", - ) - .unwrap(), - U256::from_dec_str("2722259584404615024560450425766186844160").unwrap(), - ], - block_random: Default::default(), - }; - - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - - let sender_balance_after = sender_balance_before - gas_price * 21000 - txn_value; - let sender_account_after = AccountRlp { - balance: sender_balance_after, - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - balance: txn_value.into(), - ..AccountRlp::default() - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after.insert( - to_second_nibbles, - rlp::encode(&to_account_second_before).to_vec(), - ); - - // Compute new receipt trie. - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21000u64.into(), - bloom: [0x00; 256].to_vec().into(), - logs: vec![], - }; - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - - let mut transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let tries_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.clone().hash(), - }; - - let block_1_hash = - H256::from_str("0x0101010101010101010101010101010101010101010101010101010101010101")?; - let mut block_hashes = vec![H256::default(); 256]; - - let inputs_first = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after: tries_after, - contract_code, - checkpoint_state_trie_root, - block_metadata: block_1_metadata.clone(), - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 21000u64.into(), - block_hashes: BlockHashes { - prev_hashes: block_hashes.clone(), - cur_hash: block_1_hash, - }, - }; - - // Preprocess all circuits. - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &[16..17, 12..15, 14..18, 14..15, 9..10, 12..13, 17..20], - &config, - ); - - let mut timing = TimingTree::new("prove root first", log::Level::Info); - let (root_proof_first, public_values_first) = - all_circuits.prove_root(&all_stark, &config, inputs_first, &mut timing, None)?; - - timing.filter(Duration::from_millis(100)).print(); - all_circuits.verify_root(root_proof_first.clone())?; - - // The gas used and transaction number are fed to the next transaction, so the two proofs can be correctly aggregated. - let gas_used_second = public_values_first.extra_block_data.gas_used_after; - - // Prove second transaction. In this second transaction, the code with logs is executed. - - let state_trie_before = expected_state_trie_after; - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: transactions_trie.clone(), - receipts_trie: receipts_trie.clone(), - storage_tries: vec![], - }; - - // Prove a transaction which carries out two LOG opcodes. - let txn_gas_price = 10; - let txn_2 = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b"); - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - // Update the state and receipt tries after the transaction, so that we have the correct expected tries: - // Update accounts. - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - - let sender_balance_after = sender_balance_after - gas_used * txn_gas_price; - let sender_account_after = AccountRlp { - balance: sender_balance_after, - nonce: 2.into(), - ..AccountRlp::default() - }; - let balance_after = to_account_after.balance; - let to_account_after = AccountRlp { - balance: balance_after, - ..AccountRlp::default() - }; - let to_account_second_after = AccountRlp { - balance: to_account_second_before.balance, - code_hash, - ..AccountRlp::default() - }; - - // Update the receipt trie. - let first_log = LogRlp { - address: to.into(), - topics: vec![], - data: Bytes::new(), - }; - - let second_log = LogRlp { - address: to.into(), - topics: vec![ - hex!("0000000000000000000000000000000000000000000000000000000000000062").into(), // dec: 98 - hex!("0000000000000000000000000000000000000000000000000000000000000063").into(), // dec: 99 - ], - data: hex!("a1b2c3d4e5").to_vec().into(), - }; - - let receipt = LegacyReceiptRlp { - status: true, - cum_gas_used: (22570 + 21000).into(), - bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000001000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000800000000000000008000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000800002000000000000000000000000000").to_vec().into(), - logs: vec![first_log, second_log], - }; - - let receipt_nibbles = Nibbles::from_str("0x01").unwrap(); // RLP(1) = 0x1 - - receipts_trie.insert(receipt_nibbles, rlp::encode(&receipt).to_vec()); - - // Update the state trie. - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after.insert( - to_second_nibbles, - rlp::encode(&to_account_second_after).to_vec(), - ); - - transactions_trie.insert(Nibbles::from_str("0x01").unwrap(), txn_2.to_vec()); - - let block_1_state_root = expected_state_trie_after.hash(); - - let trie_roots_after = TrieRoots { - state_root: block_1_state_root, - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let inputs = GenerationInputs { - signed_txn: Some(txn_2.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after: trie_roots_after.clone(), - contract_code, - checkpoint_state_trie_root, - block_metadata: block_1_metadata, - txn_number_before: 1.into(), - gas_used_before: gas_used_second, - gas_used_after: receipt.cum_gas_used, - block_hashes: BlockHashes { - prev_hashes: block_hashes.clone(), - cur_hash: block_1_hash, - }, - }; - - let mut timing = TimingTree::new("prove root second", log::Level::Info); - let (root_proof_second, public_values_second) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None.clone())?; - timing.filter(Duration::from_millis(100)).print(); - - all_circuits.verify_root(root_proof_second.clone())?; - - let (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( - false, - &root_proof_first, - public_values_first, - false, - &root_proof_second, - public_values_second, - )?; - all_circuits.verify_aggregation(&agg_proof)?; - let (first_block_proof, _block_public_values) = - all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?; - all_circuits.verify_block(&first_block_proof)?; - - // Prove the next, empty block. - - let block_2_hash = - H256::from_str("0x0123456789101112131415161718192021222324252627282930313233343536")?; - block_hashes[255] = block_1_hash; - - let block_2_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 2.into(), - block_difficulty: 0x020000.into(), - block_gaslimit: 0x445566u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - ..Default::default() - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - let inputs = GenerationInputs { - signed_txn: None, - withdrawals: vec![], - tries: TrieInputs { - state_trie: expected_state_trie_after, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![], - }, - trie_roots_after: TrieRoots { - state_root: trie_roots_after.state_root, - transactions_root: HashedPartialTrie::from(Node::Empty).hash(), - receipts_root: HashedPartialTrie::from(Node::Empty).hash(), - }, - contract_code, - checkpoint_state_trie_root: block_1_state_root, // We use block 1 as new checkpoint. - block_metadata: block_2_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0.into(), - block_hashes: BlockHashes { - prev_hashes: block_hashes, - cur_hash: block_2_hash, - }, - }; - - let (root_proof, public_values) = - all_circuits.prove_root(&all_stark, &config, inputs, &mut timing, None)?; - all_circuits.verify_root(root_proof.clone())?; - - // We can just duplicate the initial proof as the state didn't change. - let (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( - false, - &root_proof, - public_values.clone(), - false, - &root_proof, - public_values, - )?; - all_circuits.verify_aggregation(&agg_proof)?; - - let (second_block_proof, _block_public_values) = all_circuits.prove_block( - None, // We don't specify a previous proof, considering block 1 as the new checkpoint. - &agg_proof, - updated_agg_public_values, - )?; - all_circuits.verify_block(&second_block_proof) -} - -/// Values taken from the block 1000000 of Goerli: https://goerli.etherscan.io/txs?block=1000000 -#[test] -fn test_txn_and_receipt_trie_hash() -> anyhow::Result<()> { - // This test checks that inserting into the transaction and receipt `HashedPartialTrie`s works as expected. - let mut example_txn_trie = HashedPartialTrie::from(Node::Empty); - - // We consider two transactions, with one log each. - let transaction_0 = LegacyTransactionRlp { - nonce: 157823u64.into(), - gas_price: 1000000000u64.into(), - gas: 250000u64.into(), - to: AddressOption(Some(hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into())), - value: 0u64.into(), - data: hex!("e9c6c176000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000bd9fe6f7af1cc94b1aef2e0fa15f1b4baefa86eb60e78fa4bd082372a0a446d197fb58") - .to_vec() - .into(), - v: 0x1c.into(), - r: hex!("d0eeac4841caf7a894dd79e6e633efc2380553cdf8b786d1aa0b8a8dee0266f4").into(), - s: hex!("740710eed9696c663510b7fb71a553112551121595a54ec6d2ec0afcec72a973").into(), - }; - - // Insert the first transaction into the transaction trie. - example_txn_trie.insert( - Nibbles::from_str("0x80").unwrap(), // RLP(0) = 0x80 - rlp::encode(&transaction_0).to_vec(), - ); - - let transaction_1 = LegacyTransactionRlp { - nonce: 157824u64.into(), - gas_price: 1000000000u64.into(), - gas: 250000u64.into(), - to: AddressOption(Some(hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into())), - value: 0u64.into(), - data: hex!("e9c6c176000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000004920eaa814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .to_vec() - .into(), - v: 0x1b.into(), - r: hex!("a3ff39967683fc684dc7b857d6f62723e78804a14b091a058ad95cc1b8a0281f").into(), - s: hex!("51b156e05f21f499fa1ae47ebf536b15a237208f1d4a62e33956b6b03cf47742").into(), - }; - - // Insert the second transaction into the transaction trie. - example_txn_trie.insert( - Nibbles::from_str("0x01").unwrap(), - rlp::encode(&transaction_1).to_vec(), - ); - - // Receipts: - let mut example_receipt_trie = HashedPartialTrie::from(Node::Empty); - - let log_0 = LogRlp { - address: hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into(), - topics: vec![ - hex!("8a22ee899102a366ac8ad0495127319cb1ff2403cfae855f83a89cda1266674d").into(), - hex!("000000000000000000000000000000000000000000000000000000000000002a").into(), - hex!("0000000000000000000000000000000000000000000000000000000000bd9fe6").into(), - ], - data: hex!("f7af1cc94b1aef2e0fa15f1b4baefa86eb60e78fa4bd082372a0a446d197fb58") - .to_vec() - .into(), - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 0x016e5bu64.into(), - bloom: hex!("00000000000000000000000000000000000000000000000000800000000000000040000000005000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000080008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000020000000000008000000000000000000000000").to_vec().into(), - logs: vec![log_0], - }; - - // Insert the first receipt into the receipt trie. - example_receipt_trie.insert( - Nibbles::from_str("0x80").unwrap(), // RLP(0) is 0x80 - rlp::encode(&receipt_0).to_vec(), - ); - - let log_1 = LogRlp { - address: hex!("7ef66b77759e12Caf3dDB3E4AFF524E577C59D8D").into(), - topics: vec![ - hex!("8a22ee899102a366ac8ad0495127319cb1ff2403cfae855f83a89cda1266674d").into(), - hex!("0000000000000000000000000000000000000000000000000000000000000004").into(), - hex!("00000000000000000000000000000000000000000000000000000000004920ea").into(), - ], - data: hex!("a814f7df6a2203dc0e472e8828be95957c6b329fee8e2b1bb6f044c1eb4fc243") - .to_vec() - .into(), - }; - - let receipt_1 = LegacyReceiptRlp { - status: true, - cum_gas_used: 0x02dcb6u64.into(), - bloom: hex!("00000000000000000000000000000000000000000000000000800000000000000040000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000008000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000400000000000000000000000000000002000040000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000008000000000000000000000000").to_vec().into(), - logs: vec![log_1], - }; - - // Insert the second receipt into the receipt trie. - example_receipt_trie.insert( - Nibbles::from_str("0x01").unwrap(), - rlp::encode(&receipt_1).to_vec(), - ); - - // Check that the trie hashes are correct. - assert_eq!( - example_txn_trie.hash(), - hex!("3ab7120d12e1fc07303508542602beb7eecfe8f262b83fd71eefe7d6205242ce").into() - ); - - assert_eq!( - example_receipt_trie.hash(), - hex!("da46cdd329bfedace32da95f2b344d314bc6f55f027d65f9f4ac04ee425e1f98").into() - ); - - Ok(()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs deleted file mode 100644 index d759387cb0..0000000000 --- a/evm/tests/self_balance_gas_cost.rs +++ /dev/null @@ -1,196 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// The `selfBalanceGasCost` test case from https://github.com/ethereum/tests -#[test] -#[ignore] // Too slow to run on CI. -fn self_balance_gas_cost() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); - let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); - let to = hex!("1000000000000000000000000000000000000000"); - - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - - let code = [ - 0x5a, 0x47, 0x5a, 0x90, 0x50, 0x90, 0x03, 0x60, 0x02, 0x90, 0x03, 0x60, 0x01, 0x55, 0x00, - ]; - let code_gas = 2 // GAS - + 5 // SELFBALANCE - + 2 // GAS - + 3 // SWAP1 - + 2 // POP - + 3 // SWAP1 - + 3 // SUB - + 3 // PUSH1 - + 3 // SWAP1 - + 3 // SUB - + 3 // PUSH1 - + 22100; // SSTORE - let code_hash = keccak(code); - - let beneficiary_account_before = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_before = AccountRlp { - balance: 0x3635c9adc5dea00000u128.into(), - ..AccountRlp::default() - }; - let to_account_before = AccountRlp { - code_hash, - ..AccountRlp::default() - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: Node::Empty.into(), - receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], - }; - - let txn = hex!("f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"); - - let gas_used = 21_000 + code_gas; - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_difficulty: 0x20000.into(), - block_number: 1.into(), - block_chain_id: 1.into(), - block_timestamp: 0x03e8.into(), - block_gaslimit: 0xff112233u32.into(), - block_gas_used: gas_used.into(), - block_bloom: [0.into(); 8], - block_base_fee: 0xa.into(), - block_random: Default::default(), - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - contract_code.insert(code_hash, code.to_vec()); - - let expected_state_trie_after = { - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - U256::from(gas_used) * U256::from(10), - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - code_hash, - // Storage map: { 1 => 5 } - storage_root: HashedPartialTrie::from(Node::Leaf { - // TODO: Could do keccak(pad32(1)) - nibbles: Nibbles::from_str( - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", - ) - .unwrap(), - value: vec![5], - }) - .hash(), - ..AccountRlp::default() - }; - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); - expected_state_trie_after - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: gas_used.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: gas_used.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/selfdestruct.rs b/evm/tests/selfdestruct.rs deleted file mode 100644 index 87b39e3076..0000000000 --- a/evm/tests/selfdestruct.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// Test a simple selfdestruct. -#[test] -fn test_selfdestruct() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("5eb96AA102a29fAB267E12A40a5bc6E9aC088759"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let code = vec![ - 0x32, // ORIGIN - 0xFF, // SELFDESTRUCT - ]; - let to_account_before = AccountRlp { - nonce: 12.into(), - balance: eth_to_wei(10_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak(&code), - }; - - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], - }; - - // Generated using a little py-evm script. - let txn = hex!("f868050a831e848094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0880de0b6b3a76400008025a09bab8db7d72e4b42cba8b117883e16872966bae8e4570582de6ed0065e8c36a1a01256d44d982c75e0ab7a19f61ab78afa9e089d51c8686fdfbee085a5ed5d8ff8"); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 26002.into(), - block_bloom: [0.into(); 8], - }; - - let contract_code = [(keccak(&code), code), (keccak([]), vec![])].into(); - - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); - let sender_account_after = AccountRlp { - nonce: 6.into(), - balance: eth_to_wei(110_000.into()) - 26_002 * 0xa, - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); - state_trie_after - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 26002.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 26002.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs deleted file mode 100644 index cd17fdaeda..0000000000 --- a/evm/tests/simple_transfer.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use hex_literal::hex; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::KeccakGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; - -type F = GoldilocksField; -const D: usize = 2; -type C = KeccakGoldilocksConfig; - -/// Test a simple token transfer to a new address. -#[test] -fn test_simple_transfer() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let to_account_before = AccountRlp::default(); - - let state_trie_before = Node::Leaf { - nibbles: sender_nibbles, - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], - }; - - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); - - let block_metadata = BlockMetadata { - block_beneficiary: Address::from(beneficiary), - block_timestamp: 0x03e8.into(), - block_number: 1.into(), - block_difficulty: 0x020000.into(), - block_random: H256::from_uint(&0x020000.into()), - block_gaslimit: 0xff112233u32.into(), - block_chain_id: 1.into(), - block_base_fee: 0xa.into(), - block_gas_used: 21032.into(), - block_bloom: [0.into(); 8], - }; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - let expected_state_trie_after: HashedPartialTrie = { - let txdata_gas = 2 * 16; - let gas_used = 21_000 + txdata_gas; - - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - .into() - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21032.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - ); - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - let inputs = GenerationInputs { - signed_txn: Some(txn.to_vec()), - withdrawals: vec![], - tries: tries_before, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 21032.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} diff --git a/evm/tests/withdrawals.rs b/evm/tests/withdrawals.rs deleted file mode 100644 index ef40b52987..0000000000 --- a/evm/tests/withdrawals.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::collections::HashMap; -use std::time::Duration; - -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::nibbles::Nibbles; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; -use ethereum_types::{H160, H256, U256}; -use keccak_hash::keccak; -use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::plonk::config::PoseidonGoldilocksConfig; -use plonky2::util::timing::TimingTree; -use plonky2_evm::generation::mpt::AccountRlp; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; -use plonky2_evm::{AllStark, Node, StarkConfig}; -use rand::random; - -type F = GoldilocksField; -const D: usize = 2; -type C = PoseidonGoldilocksConfig; - -/// Execute 0 txns and 1 withdrawal. -#[test] -fn test_withdrawals() -> anyhow::Result<()> { - init_logger(); - - let all_stark = AllStark::::default(); - let config = StarkConfig::standard_fast_config(); - - let block_metadata = BlockMetadata::default(); - - let state_trie_before = HashedPartialTrie::from(Node::Empty); - let transactions_trie = HashedPartialTrie::from(Node::Empty); - let receipts_trie = HashedPartialTrie::from(Node::Empty); - let storage_tries = vec![]; - - let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - // Just one withdrawal. - let withdrawals = vec![(H160(random()), U256(random()))]; - - let state_trie_after = { - let mut trie = HashedPartialTrie::from(Node::Empty); - let addr_state_key = keccak(withdrawals[0].0); - let addr_nibbles = Nibbles::from_bytes_be(addr_state_key.as_bytes()).unwrap(); - let account = AccountRlp { - balance: withdrawals[0].1, - ..AccountRlp::default() - }; - trie.insert(addr_nibbles, rlp::encode(&account).to_vec()); - trie - }; - - let trie_roots_after = TrieRoots { - state_root: state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; - - let inputs = GenerationInputs { - signed_txn: None, - withdrawals, - tries: TrieInputs { - state_trie: state_trie_before, - transactions_trie, - receipts_trie, - storage_tries, - }, - trie_roots_after, - contract_code, - checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), - block_metadata, - txn_number_before: 0.into(), - gas_used_before: 0.into(), - gas_used_after: 0.into(), - block_hashes: BlockHashes { - prev_hashes: vec![H256::default(); 256], - cur_hash: H256::default(), - }, - }; - - let mut timing = TimingTree::new("prove", log::Level::Debug); - let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; - timing.filter(Duration::from_millis(100)).print(); - - verify_proof(&all_stark, proof, &config) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} From cfe4448341cf3e06d3f1aa327e691002bae9f35c Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Sun, 18 Feb 2024 09:34:00 -0500 Subject: [PATCH 148/175] Implement `Default` for `DefaultGateSerializer` and `DefaultGeneratorSerializer` (#1531) * Implement Default for DefaultGateSerializer and DefaultGeneratorSerializer * Apply comments * Update doc * Clippy --- plonky2/examples/square_root.rs | 5 ++--- plonky2/src/plonk/config.rs | 4 ++-- .../src/util/serialization/gate_serialization.rs | 9 +++++++++ .../util/serialization/generator_serialization.rs | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/plonky2/examples/square_root.rs b/plonky2/examples/square_root.rs index 91b7b79053..fb970a67c5 100644 --- a/plonky2/examples/square_root.rs +++ b/plonky2/examples/square_root.rs @@ -66,6 +66,7 @@ impl, const D: usize> SimpleGenerator } } +#[derive(Default)] pub struct CustomGeneratorSerializer, const D: usize> { pub _phantom: PhantomData, } @@ -131,9 +132,7 @@ fn main() -> Result<()> { // Test serialization { let gate_serializer = DefaultGateSerializer; - let generator_serializer = CustomGeneratorSerializer { - _phantom: PhantomData::, - }; + let generator_serializer = CustomGeneratorSerializer::::default(); let data_bytes = data .to_bytes(&gate_serializer, &generator_serializer) diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index ee5b69ffa8..217c889761 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -106,7 +106,7 @@ pub trait GenericConfig: } /// Configuration using Poseidon over the Goldilocks field. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize)] pub struct PoseidonGoldilocksConfig; impl GenericConfig<2> for PoseidonGoldilocksConfig { type F = GoldilocksField; @@ -116,7 +116,7 @@ impl GenericConfig<2> for PoseidonGoldilocksConfig { } /// Configuration using truncated Keccak over the Goldilocks field. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub struct KeccakGoldilocksConfig; impl GenericConfig<2> for KeccakGoldilocksConfig { type F = GoldilocksField; diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index 8b10da07b0..c26e60d3f2 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -114,6 +114,15 @@ pub mod default { use crate::hash::hash_types::RichField; use crate::util::serialization::GateSerializer; + /// A gate serializer that can be used to serialize all default gates supported + /// by the `plonky2` library. + /// Being a unit struct, it can be simply called as + /// ```rust + /// use plonky2::util::serialization::DefaultGateSerializer; + /// let gate_serializer = DefaultGateSerializer; + /// ``` + /// Applications using custom gates should define their own serializer implementing + /// the `GateSerializer` trait. This can be easily done through the `impl_gate_serializer` macro. pub struct DefaultGateSerializer; impl, const D: usize> GateSerializer for DefaultGateSerializer { impl_gate_serializer! { diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index 6ede002007..15bae9acd1 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -127,6 +127,20 @@ pub mod default { use crate::recursion::dummy_circuit::DummyProofGenerator; use crate::util::serialization::WitnessGeneratorSerializer; + /// A generator serializer that can be used to serialize all default generators supported + /// by the `plonky2` library. It can simply be called as + /// ```rust + /// use plonky2::util::serialization::DefaultGeneratorSerializer; + /// use plonky2::plonk::config::PoseidonGoldilocksConfig; + /// + /// const D: usize = 2; + /// type C = PoseidonGoldilocksConfig; + /// let generator_serializer = DefaultGeneratorSerializer::::default(); + /// ``` + /// Applications using custom generators should define their own serializer implementing + /// the `WitnessGeneratorSerializer` trait. This can be easily done through the + /// `impl_generator_serializer` macro. + #[derive(Default)] pub struct DefaultGeneratorSerializer, const D: usize> { pub _phantom: PhantomData, } From da85f1be3692aab6e847d702f02344c247b6536e Mon Sep 17 00:00:00 2001 From: Ford <153042616+guerrierindien@users.noreply.github.com> Date: Sun, 18 Feb 2024 16:25:16 +0100 Subject: [PATCH 149/175] typo (#1533) --- starky/src/cross_table_lookup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starky/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs index f6f958a2db..26e1576e48 100644 --- a/starky/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -167,7 +167,7 @@ pub struct CtlZData<'a, F: Field> { } impl<'a, F: Field> CtlZData<'a, F> { - /// Returs new CTL data from the provided arguments. + /// Returns new CTL data from the provided arguments. pub fn new( helper_columns: Vec>, z: PolynomialValues, From 6c9588aaea58f6565ddec4ca859b36fa423048fa Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 19 Feb 2024 07:35:51 -0500 Subject: [PATCH 150/175] Update licenses and dependencies (#1534) --- Cargo.toml | 20 +++ field/LICENSE-APACHE => LICENSE-APACHE | 0 field/LICENSE-MIT => LICENSE-MIT | 0 README.md | 7 +- field/Cargo.toml | 25 +-- maybe_rayon/Cargo.toml | 8 +- maybe_rayon/LICENSE-APACHE | 202 ------------------------- maybe_rayon/LICENSE-MIT | 21 --- plonky2/Cargo.toml | 42 ++--- plonky2/LICENSE-APACHE | 202 ------------------------- plonky2/LICENSE-MIT | 21 --- starky/Cargo.toml | 25 +-- starky/LICENSE-APACHE | 202 ------------------------- starky/LICENSE-MIT | 21 --- 14 files changed, 85 insertions(+), 711 deletions(-) rename field/LICENSE-APACHE => LICENSE-APACHE (100%) rename field/LICENSE-MIT => LICENSE-MIT (100%) delete mode 100644 maybe_rayon/LICENSE-APACHE delete mode 100644 maybe_rayon/LICENSE-MIT delete mode 100644 plonky2/LICENSE-APACHE delete mode 100644 plonky2/LICENSE-MIT delete mode 100644 starky/LICENSE-APACHE delete mode 100644 starky/LICENSE-MIT diff --git a/Cargo.toml b/Cargo.toml index ede92a6228..ae7534b515 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,18 @@ members = ["field", "maybe_rayon", "plonky2", "starky", "util"] resolver = "2" +[workspace.dependencies] +ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. +anyhow = { version = "1.0.40", default-features = false } +hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. +itertools = { version = "0.11.0", default-features = false } +log = { version = "0.4.14", default-features = false } +num = { version = "0.4", default-features = false, features = ["rand"] } +rand = { version = "0.8.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive"] } +static_assertions = { version = "1.1.0", default-features = false } +unroll = { version = "0.1.5", default-features = false } + [profile.release] opt-level = 3 incremental = true @@ -10,3 +22,11 @@ incremental = true [profile.bench] opt-level = 3 + +[workspace.package] +edition = "2021" +license = "MIT OR Apache-2.0" +homepage = "https://github.com/0xPolygonZero/plonky2" +repository = "https://github.com/0xPolygonZero/plonky2" +keywords = ["cryptography", "SNARK", "PLONK", "FRI", "plonky2"] +categories = ["cryptography"] diff --git a/field/LICENSE-APACHE b/LICENSE-APACHE similarity index 100% rename from field/LICENSE-APACHE rename to LICENSE-APACHE diff --git a/field/LICENSE-MIT b/LICENSE-MIT similarity index 100% rename from field/LICENSE-MIT rename to LICENSE-MIT diff --git a/README.md b/README.md index f6d4e7f0e9..f4bdfc5892 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,12 @@ description for a performance improvement must clearly identify ## Licenses -As this is a monorepo, see the individual crates within for license information. +All crates of this monorepo are licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. ## Security diff --git a/field/Cargo.toml b/field/Cargo.toml index 72408c4946..86c681a7fb 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -2,19 +2,26 @@ name = "plonky2_field" description = "Finite field arithmetic" version = "0.1.1" -license = "MIT OR Apache-2.0" authors = ["Daniel Lubarov ", "William Borgeaud ", "Jacqueline Nabaglo ", "Hamish Ivey-Law "] -edition = "2021" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [dependencies] -anyhow = { version = "1.0.40", default-features = false } -itertools = { version = "0.11.0", default-features = false, features = ["use_alloc"] } -num = { version = "0.4", default-features = false, features = ["alloc", "rand"] } +anyhow = { workspace = true } +itertools = { workspace = true, features = ["use_alloc"] } +num = { workspace = true, features = ["alloc"] } +rand = { workspace = true, features = ["getrandom"] } +serde = { workspace = true, features = ["alloc"] } +static_assertions = { workspace = true } +unroll = { workspace = true } + +# Local dependencies plonky2_util = { path = "../util", default-features = false } -rand = { version = "0.8.5", default-features = false, features = ["getrandom"] } -serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } -static_assertions = { version = "1.1.0", default-features = false } -unroll = { version = "0.1.5", default-features = false } + # Display math equations properly in documentation [package.metadata.docs.rs] diff --git a/maybe_rayon/Cargo.toml b/maybe_rayon/Cargo.toml index e436563215..59e09349ab 100644 --- a/maybe_rayon/Cargo.toml +++ b/maybe_rayon/Cargo.toml @@ -1,9 +1,13 @@ [package] name = "plonky2_maybe_rayon" description = "Feature-gated wrapper around rayon" -license = "MIT OR Apache-2.0" version = "0.1.1" -edition = "2021" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [features] parallel = ["rayon"] diff --git a/maybe_rayon/LICENSE-APACHE b/maybe_rayon/LICENSE-APACHE deleted file mode 100644 index 1e5006dc14..0000000000 --- a/maybe_rayon/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/maybe_rayon/LICENSE-MIT b/maybe_rayon/LICENSE-MIT deleted file mode 100644 index 86d690b220..0000000000 --- a/maybe_rayon/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 The Plonky2 Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index f20932a594..2185462b6d 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -2,13 +2,14 @@ name = "plonky2" description = "Recursive SNARKs based on PLONK and FRI" version = "0.1.4" -license = "MIT OR Apache-2.0" authors = ["Daniel Lubarov ", "William Borgeaud ", "Nicholas Ward "] readme = "README.md" -repository = "https://github.com/0xPolygonZero/plonky2" -keywords = ["cryptography", "SNARK", "PLONK", "FRI"] -categories = ["cryptography"] -edition = "2021" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [features] default = ["gate_testing", "parallel", "rand_chacha", "std", "timing"] @@ -18,23 +19,26 @@ std = ["anyhow/std", "rand/std", "itertools/use_std"] timing = ["std", "dep:web-time"] [dependencies] -ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. -anyhow = { version = "1.0.40", default-features = false } -hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. -itertools = { version = "0.11.0", default-features = false } +ahash = { workspace = true } +anyhow = { workspace = true } +hashbrown = { workspace = true } +itertools = { workspace = true } keccak-hash = { version = "0.8.0", default-features = false } -log = { version = "0.4.14", default-features = false } -plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } -num = { version = "0.4", default-features = false, features = ["rand"] } -plonky2_field = { path = "../field", default-features = false } -plonky2_util = { path = "../util", default-features = false } -rand = { version = "0.8.4", default-features = false } +log = { workspace = true } +num = { workspace = true } +rand = { workspace = true } rand_chacha = { version = "0.3.1", optional = true, default-features = false } -serde = { version = "1.0", default-features = false, features = ["derive", "rc"] } -static_assertions = { version = "1.1.0", default-features = false } -unroll = { version = "0.1.5", default-features = false } +serde = { workspace = true, features = ["rc"] } +static_assertions = { workspace = true } +unroll = { workspace = true } web-time = { version = "1.0.0", optional = true } +# Local dependencies +plonky2_field = { path = "../field", default-features = false } +plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } +plonky2_util = { path = "../util", default-features = false } + + [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] getrandom = { version = "0.2", default-features = false, features = ["js"] } @@ -42,7 +46,7 @@ getrandom = { version = "0.2", default-features = false, features = ["js"] } criterion = { version = "0.5.1", default-features = false } env_logger = { version = "0.9.0", default-features = false } num_cpus = { version = "1.14.0", default-features = false } -rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } +rand = { workspace = true, features = ["getrandom"] } rand_chacha = { version = "0.3.1", default-features = false } serde_cbor = { version = "0.11.2" } serde_json = { version = "1.0" } diff --git a/plonky2/LICENSE-APACHE b/plonky2/LICENSE-APACHE deleted file mode 100644 index 1e5006dc14..0000000000 --- a/plonky2/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/plonky2/LICENSE-MIT b/plonky2/LICENSE-MIT deleted file mode 100644 index 86d690b220..0000000000 --- a/plonky2/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 The Plonky2 Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/starky/Cargo.toml b/starky/Cargo.toml index fe64413f9f..6ad554f9d4 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -2,13 +2,14 @@ name = "starky" description = "Implementation of STARKs" version = "0.1.2" -license = "MIT OR Apache-2.0" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" -repository = "https://github.com/0xPolygonZero/plonky2" -keywords = ["cryptography", "STARK", "FRI"] -categories = ["cryptography"] -edition = "2021" +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true [features] default = ["parallel", "std", "timing"] @@ -17,14 +18,16 @@ std = ["anyhow/std", "plonky2/std"] timing = ["plonky2/timing"] [dependencies] -ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. -anyhow = { version = "1.0.40", default-features = false } -hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. -itertools = { version = "0.11.0", default-features = false } -log = { version = "0.4.14", default-features = false } +ahash = { workspace = true } +anyhow = { workspace = true } +hashbrown = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } num-bigint = { version = "0.4.3", default-features = false } -plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } + +# Local dependencies plonky2 = { path = "../plonky2", default-features = false } +plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } plonky2_util = { path = "../util", default-features = false } [dev-dependencies] diff --git a/starky/LICENSE-APACHE b/starky/LICENSE-APACHE deleted file mode 100644 index 1e5006dc14..0000000000 --- a/starky/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/starky/LICENSE-MIT b/starky/LICENSE-MIT deleted file mode 100644 index 86d690b220..0000000000 --- a/starky/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 The Plonky2 Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. From 4a620f4d79efe9233d0e7682df5a2fc625b5420e Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:13:32 -0500 Subject: [PATCH 151/175] Fix latest clippy for `redundant_imports` (#1535) * Fix clippy for maybe_rayon * Remove reimports --- field/src/lib.rs | 2 +- maybe_rayon/src/lib.rs | 2 +- plonky2/src/gates/coset_interpolation.rs | 2 +- plonky2/src/hash/merkle_proofs.rs | 2 -- plonky2/src/recursion/conditional_recursive_verifier.rs | 2 +- plonky2/src/recursion/recursive_verifier.rs | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/field/src/lib.rs b/field/src/lib.rs index c35441bdb7..d2fd5832b9 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -5,7 +5,7 @@ #![allow(clippy::needless_range_loop)] #![feature(specialization)] #![cfg_attr(not(test), no_std)] - +#![cfg(not(test))] extern crate alloc; pub(crate) mod arch; diff --git a/maybe_rayon/src/lib.rs b/maybe_rayon/src/lib.rs index 8e8d071862..942898492f 100644 --- a/maybe_rayon/src/lib.rs +++ b/maybe_rayon/src/lib.rs @@ -16,7 +16,7 @@ use rayon::{ prelude::*, slice::{ Chunks as ParChunks, ChunksExact as ParChunksExact, ChunksExactMut as ParChunksExactMut, - ChunksMut as ParChunksMut, ParallelSlice, ParallelSliceMut, + ChunksMut as ParChunksMut, }, }; #[cfg(not(feature = "parallel"))] diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index 9911e92793..a1ebd214a4 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -644,7 +644,7 @@ mod tests { use super::*; use crate::field::goldilocks_field::GoldilocksField; - use crate::field::types::{Field, Sample}; + use crate::field::types::Sample; use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; use crate::hash::hash_types::HashOut; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index c8fd2492fb..d773b11aaa 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -171,7 +171,6 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { - use anyhow::Result; use rand::rngs::OsRng; use rand::Rng; @@ -179,7 +178,6 @@ mod tests { use crate::field::types::Field; use crate::hash::merkle_tree::MerkleTree; use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::verifier::verify; diff --git a/plonky2/src/recursion/conditional_recursive_verifier.rs b/plonky2/src/recursion/conditional_recursive_verifier.rs index 43bef5892b..2dc6966a20 100644 --- a/plonky2/src/recursion/conditional_recursive_verifier.rs +++ b/plonky2/src/recursion/conditional_recursive_verifier.rs @@ -348,7 +348,7 @@ mod tests { use crate::gates::noop::NoopGate; use crate::iop::witness::{PartialWitness, WitnessWrite}; use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use crate::plonk::config::PoseidonGoldilocksConfig; use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof}; #[test] diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index 2da2440844..82d1e81389 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -208,7 +208,7 @@ mod tests { use crate::gates::noop::NoopGate; use crate::iop::witness::{PartialWitness, WitnessWrite}; use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData}; - use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig}; + use crate::plonk::config::{KeccakGoldilocksConfig, PoseidonGoldilocksConfig}; use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs}; use crate::plonk::prover::prove; use crate::util::timing::TimingTree; From 598ac876aea2cae5252ed3c2760bc441b7e8b661 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:32:07 -0500 Subject: [PATCH 152/175] Do some additional cleanup pre-release (#1532) * Add Debug impl for types * Remove outdated clippy lint exceptions * Hide internal custom gate methods and make some const --- field/src/lib.rs | 4 ++-- field/src/types.rs | 2 +- field/src/zero_poly_coset.rs | 1 + plonky2/src/fri/structure.rs | 10 +++++++++- plonky2/src/gadgets/arithmetic.rs | 2 +- plonky2/src/gadgets/arithmetic_extension.rs | 4 ++-- plonky2/src/gadgets/polynomial.rs | 1 + plonky2/src/gates/arithmetic_base.rs | 8 ++++---- plonky2/src/gates/arithmetic_extension.rs | 8 ++++---- plonky2/src/gates/base_sum.rs | 6 +++--- plonky2/src/gates/constant.rs | 4 ++-- plonky2/src/gates/coset_interpolation.rs | 12 ++++++------ plonky2/src/gates/exponentiation.rs | 6 +++--- plonky2/src/gates/gate.rs | 2 +- plonky2/src/gates/multiplication_extension.rs | 6 +++--- plonky2/src/gates/noop.rs | 1 + plonky2/src/gates/poseidon.rs | 14 +++++++------- plonky2/src/gates/poseidon_mds.rs | 4 ++-- plonky2/src/gates/public_input.rs | 3 ++- plonky2/src/gates/random_access.rs | 10 +++++----- plonky2/src/gates/reducing.rs | 8 ++++---- plonky2/src/gates/reducing_extension.rs | 10 +++++----- plonky2/src/gates/util.rs | 1 + plonky2/src/iop/challenger.rs | 3 ++- plonky2/src/lib.rs | 2 ++ plonky2/src/plonk/circuit_builder.rs | 2 ++ plonky2/src/plonk/circuit_data.rs | 1 + plonky2/src/plonk/copy_constraint.rs | 1 + plonky2/src/plonk/proof.rs | 1 + plonky2/src/plonk/vars.rs | 4 +++- plonky2/src/util/context_tree.rs | 1 + .../src/util/serialization/gate_serialization.rs | 2 +- .../util/serialization/generator_serialization.rs | 2 +- plonky2/src/util/timing.rs | 2 ++ 34 files changed, 87 insertions(+), 61 deletions(-) diff --git a/field/src/lib.rs b/field/src/lib.rs index d2fd5832b9..1531a47db2 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -1,8 +1,8 @@ #![allow(incomplete_features)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::type_complexity)] #![allow(clippy::len_without_is_empty)] #![allow(clippy::needless_range_loop)] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_debug_implementations)] #![feature(specialization)] #![cfg_attr(not(test), no_std)] #![cfg(not(test))] diff --git a/field/src/types.rs b/field/src/types.rs index 646dff7358..b2e43667c7 100644 --- a/field/src/types.rs +++ b/field/src/types.rs @@ -563,7 +563,7 @@ pub trait PrimeField64: PrimeField + Field64 { } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Powers { base: F, current: F, diff --git a/field/src/zero_poly_coset.rs b/field/src/zero_poly_coset.rs index 53b66a75d4..9773552852 100644 --- a/field/src/zero_poly_coset.rs +++ b/field/src/zero_poly_coset.rs @@ -4,6 +4,7 @@ use crate::packed::PackedField; use crate::types::Field; /// Precomputations of the evaluation of `Z_H(X) = X^n - 1` on a coset `gK` with `H <= K`. +#[derive(Debug)] pub struct ZeroPolyOnCoset { /// `n = |H|`. n: F, diff --git a/plonky2/src/fri/structure.rs b/plonky2/src/fri/structure.rs index 81e462da5c..7a580e50c6 100644 --- a/plonky2/src/fri/structure.rs +++ b/plonky2/src/fri/structure.rs @@ -10,6 +10,7 @@ use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; /// Describes an instance of a FRI-based batch opening. +#[derive(Debug)] pub struct FriInstanceInfo, const D: usize> { /// The oracles involved, not counting oracles created during the commit phase. pub oracles: Vec, @@ -18,6 +19,7 @@ pub struct FriInstanceInfo, const D: usize> { } /// Describes an instance of a FRI-based batch opening. +#[derive(Debug)] pub struct FriInstanceInfoTarget { /// The oracles involved, not counting oracles created during the commit phase. pub oracles: Vec, @@ -25,19 +27,21 @@ pub struct FriInstanceInfoTarget { pub batches: Vec>, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct FriOracleInfo { pub num_polys: usize, pub blinding: bool, } /// A batch of openings at a particular point. +#[derive(Debug)] pub struct FriBatchInfo, const D: usize> { pub point: F::Extension, pub polynomials: Vec, } /// A batch of openings at a particular point. +#[derive(Debug)] pub struct FriBatchInfoTarget { pub point: ExtensionTarget, pub polynomials: Vec, @@ -66,21 +70,25 @@ impl FriPolynomialInfo { } /// Opened values of each polynomial. +#[derive(Debug)] pub struct FriOpenings, const D: usize> { pub batches: Vec>, } /// Opened values of each polynomial that's opened at a particular point. +#[derive(Debug)] pub struct FriOpeningBatch, const D: usize> { pub values: Vec, } /// Opened values of each polynomial. +#[derive(Debug)] pub struct FriOpeningsTarget { pub batches: Vec>, } /// Opened values of each polynomial that's opened at a particular point. +#[derive(Debug)] pub struct FriOpeningBatchTarget { pub values: Vec>, } diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index 0e6806ffb0..e162f1116f 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -424,7 +424,7 @@ impl, const D: usize> SimpleGenerator for Equ } /// Represents a base arithmetic operation in the circuit. Used to memoize results. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub(crate) struct BaseArithmeticOperation { const_0: F, const_1: F, diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 649f4082ec..afea71df39 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -545,7 +545,7 @@ impl, const D: usize> SimpleGenerator } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PowersTarget { base: ExtensionTarget, current: ExtensionTarget, @@ -584,7 +584,7 @@ impl, const D: usize> CircuitBuilder { } /// Represents an extension arithmetic operation in the circuit. Used to memoize results. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub(crate) struct ExtensionArithmeticOperation, const D: usize> { const_0: F, const_1: F, diff --git a/plonky2/src/gadgets/polynomial.rs b/plonky2/src/gadgets/polynomial.rs index 94fbe3b1c6..f10f25312a 100644 --- a/plonky2/src/gadgets/polynomial.rs +++ b/plonky2/src/gadgets/polynomial.rs @@ -40,6 +40,7 @@ impl PolynomialCoeffsExtTarget { } } +#[derive(Debug)] pub struct PolynomialCoeffsExtAlgebraTarget(pub Vec>); impl PolynomialCoeffsExtAlgebraTarget { diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index 754895790a..ab3ac0bd57 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -44,16 +44,16 @@ impl ArithmeticGate { config.num_routed_wires / wires_per_op } - pub const fn wire_ith_multiplicand_0(i: usize) -> usize { + pub(crate) const fn wire_ith_multiplicand_0(i: usize) -> usize { 4 * i } - pub const fn wire_ith_multiplicand_1(i: usize) -> usize { + pub(crate) const fn wire_ith_multiplicand_1(i: usize) -> usize { 4 * i + 1 } - pub const fn wire_ith_addend(i: usize) -> usize { + pub(crate) const fn wire_ith_addend(i: usize) -> usize { 4 * i + 2 } - pub const fn wire_ith_output(i: usize) -> usize { + pub(crate) const fn wire_ith_output(i: usize) -> usize { 4 * i + 3 } } diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 60eb912b61..e549f97045 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -40,16 +40,16 @@ impl ArithmeticExtensionGate { config.num_routed_wires / wires_per_op } - pub const fn wires_ith_multiplicand_0(i: usize) -> Range { + pub(crate) const fn wires_ith_multiplicand_0(i: usize) -> Range { 4 * D * i..4 * D * i + D } - pub const fn wires_ith_multiplicand_1(i: usize) -> Range { + pub(crate) const fn wires_ith_multiplicand_1(i: usize) -> Range { 4 * D * i + D..4 * D * i + 2 * D } - pub const fn wires_ith_addend(i: usize) -> Range { + pub(crate) const fn wires_ith_addend(i: usize) -> Range { 4 * D * i + 2 * D..4 * D * i + 3 * D } - pub const fn wires_ith_output(i: usize) -> Range { + pub(crate) const fn wires_ith_output(i: usize) -> Range { 4 * D * i + 3 * D..4 * D * i + 4 * D } } diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 0f38415d4e..a169aa170a 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -40,11 +40,11 @@ impl BaseSumGate { Self::new(num_limbs) } - pub const WIRE_SUM: usize = 0; - pub const START_LIMBS: usize = 1; + pub(crate) const WIRE_SUM: usize = 0; + pub(crate) const START_LIMBS: usize = 1; /// Returns the index of the `i`th limb wire. - pub const fn limbs(&self) -> Range { + pub(crate) const fn limbs(&self) -> Range { Self::START_LIMBS..Self::START_LIMBS + self.num_limbs } } diff --git a/plonky2/src/gates/constant.rs b/plonky2/src/gates/constant.rs index cc62de7fe4..cbbcae37fd 100644 --- a/plonky2/src/gates/constant.rs +++ b/plonky2/src/gates/constant.rs @@ -30,12 +30,12 @@ impl ConstantGate { Self { num_consts } } - pub fn const_input(&self, i: usize) -> usize { + const fn const_input(&self, i: usize) -> usize { debug_assert!(i < self.num_consts); i } - pub fn wire_output(&self, i: usize) -> usize { + const fn wire_output(&self, i: usize) -> usize { debug_assert!(i < self.num_consts); i } diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index a1ebd214a4..eb133f173a 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -141,31 +141,31 @@ impl, const D: usize> CosetInterpolationGate self.start_intermediates() } - fn num_intermediates(&self) -> usize { - (self.num_points() - 2) / (self.degree() - 1) + const fn num_intermediates(&self) -> usize { + (self.num_points() - 2) / (self.degree - 1) } /// The wires corresponding to the i'th intermediate evaluation. - fn wires_intermediate_eval(&self, i: usize) -> Range { + const fn wires_intermediate_eval(&self, i: usize) -> Range { debug_assert!(i < self.num_intermediates()); let start = self.start_intermediates() + D * i; start..start + D } /// The wires corresponding to the i'th intermediate product. - fn wires_intermediate_prod(&self, i: usize) -> Range { + const fn wires_intermediate_prod(&self, i: usize) -> Range { debug_assert!(i < self.num_intermediates()); let start = self.start_intermediates() + D * (self.num_intermediates() + i); start..start + D } /// End of wire indices, exclusive. - fn end(&self) -> usize { + const fn end(&self) -> usize { self.start_intermediates() + D * (2 * self.num_intermediates() + 1) } /// Wire indices of the shifted point to evaluate the interpolant at. - fn wires_shifted_evaluation_point(&self) -> Range { + const fn wires_shifted_evaluation_point(&self) -> Range { let start = self.start_intermediates() + D * 2 * self.num_intermediates(); start..start + D } diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 2b7164e1d2..aab9322db4 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -55,12 +55,12 @@ impl, const D: usize> ExponentiationGate { max_for_routed_wires.min(max_for_wires) } - pub const fn wire_base(&self) -> usize { + pub(crate) const fn wire_base(&self) -> usize { 0 } /// The `i`th bit of the exponent, in little-endian order. - pub fn wire_power_bit(&self, i: usize) -> usize { + pub(crate) const fn wire_power_bit(&self, i: usize) -> usize { debug_assert!(i < self.num_power_bits); 1 + i } @@ -69,7 +69,7 @@ impl, const D: usize> ExponentiationGate { 1 + self.num_power_bits } - pub fn wire_intermediate_value(&self, i: usize) -> usize { + pub(crate) const fn wire_intermediate_value(&self, i: usize) -> usize { debug_assert!(i < self.num_power_bits); 2 + self.num_power_bits + i } diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 07de4fa33b..9bc8d1dcad 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -309,7 +309,7 @@ pub struct CurrentSlot, const D: usize> { } /// A gate along with any constants used to configure it. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct GateInstance, const D: usize> { pub gate_ref: GateRef, pub constants: Vec, diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 143c854c66..633a4b21ca 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -40,13 +40,13 @@ impl MulExtensionGate { config.num_routed_wires / wires_per_op } - pub const fn wires_ith_multiplicand_0(i: usize) -> Range { + pub(crate) const fn wires_ith_multiplicand_0(i: usize) -> Range { 3 * D * i..3 * D * i + D } - pub const fn wires_ith_multiplicand_1(i: usize) -> Range { + pub(crate) const fn wires_ith_multiplicand_1(i: usize) -> Range { 3 * D * i + D..3 * D * i + 2 * D } - pub const fn wires_ith_output(i: usize) -> Range { + pub(crate) const fn wires_ith_output(i: usize) -> Range { 3 * D * i + 2 * D..3 * D * i + 3 * D } } diff --git a/plonky2/src/gates/noop.rs b/plonky2/src/gates/noop.rs index 54cb6422ef..f2387f6bc9 100644 --- a/plonky2/src/gates/noop.rs +++ b/plonky2/src/gates/noop.rs @@ -12,6 +12,7 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBa use crate::util::serialization::{Buffer, IoResult}; /// A gate which does nothing. +#[derive(Debug)] pub struct NoopGate; impl, const D: usize> Gate for NoopGate { diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index be9a064e43..fa880753ae 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -39,23 +39,23 @@ impl, const D: usize> PoseidonGate { } /// The wire index for the `i`th input to the permutation. - pub const fn wire_input(i: usize) -> usize { + pub(crate) const fn wire_input(i: usize) -> usize { i } /// The wire index for the `i`th output to the permutation. - pub const fn wire_output(i: usize) -> usize { + pub(crate) const fn wire_output(i: usize) -> usize { SPONGE_WIDTH + i } /// If this is set to 1, the first four inputs will be swapped with the next four inputs. This /// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0. - pub const WIRE_SWAP: usize = 2 * SPONGE_WIDTH; + pub(crate) const WIRE_SWAP: usize = 2 * SPONGE_WIDTH; const START_DELTA: usize = 2 * SPONGE_WIDTH + 1; /// A wire which stores `swap * (input[i + 4] - input[i])`; used to compute the swapped inputs. - fn wire_delta(i: usize) -> usize { + const fn wire_delta(i: usize) -> usize { assert!(i < 4); Self::START_DELTA + i } @@ -64,7 +64,7 @@ impl, const D: usize> PoseidonGate { /// A wire which stores the input of the `i`-th S-box of the `round`-th round of the first set /// of full rounds. - fn wire_full_sbox_0(round: usize, i: usize) -> usize { + const fn wire_full_sbox_0(round: usize, i: usize) -> usize { debug_assert!( round != 0, "First round S-box inputs are not stored as wires" @@ -78,7 +78,7 @@ impl, const D: usize> PoseidonGate { Self::START_FULL_0 + SPONGE_WIDTH * (poseidon::HALF_N_FULL_ROUNDS - 1); /// A wire which stores the input of the S-box of the `round`-th round of the partial rounds. - fn wire_partial_sbox(round: usize) -> usize { + const fn wire_partial_sbox(round: usize) -> usize { debug_assert!(round < poseidon::N_PARTIAL_ROUNDS); Self::START_PARTIAL + round } @@ -87,7 +87,7 @@ impl, const D: usize> PoseidonGate { /// A wire which stores the input of the `i`-th S-box of the `round`-th round of the second set /// of full rounds. - fn wire_full_sbox_1(round: usize, i: usize) -> usize { + const fn wire_full_sbox_1(round: usize, i: usize) -> usize { debug_assert!(round < poseidon::HALF_N_FULL_ROUNDS); debug_assert!(i < SPONGE_WIDTH); Self::START_FULL_1 + SPONGE_WIDTH * round + i diff --git a/plonky2/src/gates/poseidon_mds.rs b/plonky2/src/gates/poseidon_mds.rs index 9bd37e5e40..b692e2834e 100644 --- a/plonky2/src/gates/poseidon_mds.rs +++ b/plonky2/src/gates/poseidon_mds.rs @@ -33,12 +33,12 @@ impl + Poseidon, const D: usize> PoseidonMdsGate Range { + pub(crate) const fn wires_input(i: usize) -> Range { assert!(i < SPONGE_WIDTH); i * D..(i + 1) * D } - pub fn wires_output(i: usize) -> Range { + pub(crate) const fn wires_output(i: usize) -> Range { assert!(i < SPONGE_WIDTH); (SPONGE_WIDTH + i) * D..(SPONGE_WIDTH + i + 1) * D } diff --git a/plonky2/src/gates/public_input.rs b/plonky2/src/gates/public_input.rs index 3a754c7c91..e066e45e6e 100644 --- a/plonky2/src/gates/public_input.rs +++ b/plonky2/src/gates/public_input.rs @@ -19,10 +19,11 @@ use crate::plonk::vars::{ use crate::util::serialization::{Buffer, IoResult}; /// A gate whose first four wires will be equal to a hash of public inputs. +#[derive(Debug)] pub struct PublicInputGate; impl PublicInputGate { - pub const fn wires_public_inputs_hash() -> Range { + pub(crate) const fn wires_public_inputs_hash() -> Range { 0..4 } } diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 086b119a5e..24abb58837 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -80,19 +80,19 @@ impl, const D: usize> RandomAccessGate { } /// For each copy, a wire containing the claimed index of the element. - pub fn wire_access_index(&self, copy: usize) -> usize { + pub(crate) const fn wire_access_index(&self, copy: usize) -> usize { debug_assert!(copy < self.num_copies); (2 + self.vec_size()) * copy } /// For each copy, a wire containing the element claimed to be at the index. - pub fn wire_claimed_element(&self, copy: usize) -> usize { + pub(crate) const fn wire_claimed_element(&self, copy: usize) -> usize { debug_assert!(copy < self.num_copies); (2 + self.vec_size()) * copy + 1 } /// For each copy, wires containing the entire list. - pub fn wire_list_item(&self, i: usize, copy: usize) -> usize { + pub(crate) const fn wire_list_item(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size()); debug_assert!(copy < self.num_copies); (2 + self.vec_size()) * copy + 2 + i @@ -102,7 +102,7 @@ impl, const D: usize> RandomAccessGate { (2 + self.vec_size()) * self.num_copies } - fn wire_extra_constant(&self, i: usize) -> usize { + const fn wire_extra_constant(&self, i: usize) -> usize { debug_assert!(i < self.num_extra_constants); self.start_extra_constants() + i } @@ -114,7 +114,7 @@ impl, const D: usize> RandomAccessGate { /// An intermediate wire where the prover gives the (purported) binary decomposition of the /// index. - pub fn wire_bit(&self, i: usize, copy: usize) -> usize { + pub(crate) const fn wire_bit(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.bits); debug_assert!(copy < self.num_copies); self.num_routed_wires() + copy * self.bits + i diff --git a/plonky2/src/gates/reducing.rs b/plonky2/src/gates/reducing.rs index 5e0fc9f473..0030550514 100644 --- a/plonky2/src/gates/reducing.rs +++ b/plonky2/src/gates/reducing.rs @@ -35,17 +35,17 @@ impl ReducingGate { (num_routed_wires - 3 * D).min((num_wires - 2 * D) / (D + 1)) } - pub const fn wires_output() -> Range { + pub(crate) const fn wires_output() -> Range { 0..D } - pub const fn wires_alpha() -> Range { + pub(crate) const fn wires_alpha() -> Range { D..2 * D } - pub const fn wires_old_acc() -> Range { + pub(crate) const fn wires_old_acc() -> Range { 2 * D..3 * D } const START_COEFFS: usize = 3 * D; - pub const fn wires_coeffs(&self) -> Range { + pub(crate) const fn wires_coeffs(&self) -> Range { Self::START_COEFFS..Self::START_COEFFS + self.num_coeffs } const fn start_accs(&self) -> usize { diff --git a/plonky2/src/gates/reducing_extension.rs b/plonky2/src/gates/reducing_extension.rs index 0ac3d9fd77..3b7bc9e26c 100644 --- a/plonky2/src/gates/reducing_extension.rs +++ b/plonky2/src/gates/reducing_extension.rs @@ -37,23 +37,23 @@ impl ReducingExtensionGate { ((num_routed_wires - 3 * D) / D).min((num_wires - 2 * D) / (D * 2)) } - pub const fn wires_output() -> Range { + pub(crate) const fn wires_output() -> Range { 0..D } - pub const fn wires_alpha() -> Range { + pub(crate) const fn wires_alpha() -> Range { D..2 * D } - pub const fn wires_old_acc() -> Range { + pub(crate) const fn wires_old_acc() -> Range { 2 * D..3 * D } const START_COEFFS: usize = 3 * D; - pub const fn wires_coeff(i: usize) -> Range { + pub(crate) const fn wires_coeff(i: usize) -> Range { Self::START_COEFFS + i * D..Self::START_COEFFS + (i + 1) * D } const fn start_accs(&self) -> usize { Self::START_COEFFS + self.num_coeffs * D } - fn wires_accs(&self, i: usize) -> Range { + const fn wires_accs(&self, i: usize) -> Range { debug_assert!(i < self.num_coeffs); if i == self.num_coeffs - 1 { // The last accumulator is the output. diff --git a/plonky2/src/gates/util.rs b/plonky2/src/gates/util.rs index 88d77471ee..bd5c0fee9c 100644 --- a/plonky2/src/gates/util.rs +++ b/plonky2/src/gates/util.rs @@ -6,6 +6,7 @@ use crate::field::packed::PackedField; /// Permits us to abstract the underlying memory layout. In particular, we can make a matrix of /// constraints where every column is an evaluation point and every row is a constraint index, with /// the matrix stored in row-contiguous form. +#[derive(Debug)] pub struct StridedConstraintConsumer<'a, P: PackedField> { // This is a particularly neat way of doing this, more so than a slice. We increase start by // stride at every step and terminate when it equals end. diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index 2daa7fdc4f..57660fd487 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -12,7 +12,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::config::{AlgebraicHasher, GenericHashOut, Hasher}; /// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Challenger> { pub(crate) sponge_state: H::Permutation, pub(crate) input_buffer: Vec, @@ -161,6 +161,7 @@ impl> Default for Challenger { /// A recursive version of `Challenger`. The main difference is that `RecursiveChallenger`'s input /// buffer can grow beyond `H::Permutation::RATE`. This is so that `observe_element` etc do not need access /// to the `CircuitBuilder`. +#[derive(Debug)] pub struct RecursiveChallenger, H: AlgebraicHasher, const D: usize> { sponge_state: H::AlgebraicPermutation, diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index b0b6bfb4d9..8955194fc5 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -1,5 +1,7 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::needless_range_loop)] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_debug_implementations)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index b9c13f8147..de3ab16fdf 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -63,6 +63,7 @@ pub const NUM_COINS_LOOKUP: usize = 4; /// `ChallengeB` is used for the linear combination of input and output pairs in the polynomial RE. /// `ChallengeAlpha` is used for the running sums: 1/(alpha - combo_i). /// `ChallengeDelta` is a challenge on which to evaluate the interpolated LUT function. +#[derive(Debug)] pub enum LookupChallenges { ChallengeA = 0, ChallengeB = 1, @@ -135,6 +136,7 @@ pub struct LookupWire { /// // Verify the proof /// assert!(circuit_data.verify(proof).is_ok()); /// ``` +#[derive(Debug)] pub struct CircuitBuilder, const D: usize> { /// Circuit configuration to be used by this [`CircuitBuilder`]. pub config: CircuitConfig, diff --git a/plonky2/src/plonk/circuit_data.rs b/plonky2/src/plonk/circuit_data.rs index e4afb5680d..413de54c6e 100644 --- a/plonky2/src/plonk/circuit_data.rs +++ b/plonky2/src/plonk/circuit_data.rs @@ -252,6 +252,7 @@ impl, C: GenericConfig, const D: usize> /// structure as succinct as we can. Thus we include various precomputed data which isn't strictly /// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could /// construct a more minimal prover structure and convert back and forth. +#[derive(Debug)] pub struct ProverCircuitData< F: RichField + Extendable, C: GenericConfig, diff --git a/plonky2/src/plonk/copy_constraint.rs b/plonky2/src/plonk/copy_constraint.rs index cf7a6a19ac..309f207d8b 100644 --- a/plonky2/src/plonk/copy_constraint.rs +++ b/plonky2/src/plonk/copy_constraint.rs @@ -4,6 +4,7 @@ use alloc::string::String; use crate::iop::target::Target; /// A named copy constraint. +#[derive(Debug)] pub struct CopyConstraint { pub pair: (Target, Target), pub name: String, diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index a9151a9fc1..8cb8911a52 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -256,6 +256,7 @@ impl, C: GenericConfig, const D: usize> } } +#[derive(Debug)] pub struct ProofChallenges, const D: usize> { /// Random values used in Plonk's permutation argument. pub plonk_betas: Vec, diff --git a/plonky2/src/plonk/vars.rs b/plonky2/src/plonk/vars.rs index b9d6d790ff..6cffc45c80 100644 --- a/plonky2/src/plonk/vars.rs +++ b/plonky2/src/plonk/vars.rs @@ -132,6 +132,7 @@ impl<'a, F: Field> EvaluationVarsBase<'a, F> { } /// Iterator of views (`EvaluationVarsBase`) into a `EvaluationVarsBaseBatch`. +#[derive(Debug)] pub struct EvaluationVarsBaseBatchIter<'a, F: Field> { i: usize, vars_batch: EvaluationVarsBaseBatch<'a, F>, @@ -159,6 +160,7 @@ impl<'a, F: Field> Iterator for EvaluationVarsBaseBatchIter<'a, F> { /// Iterator of packed views (`EvaluationVarsBasePacked`) into a `EvaluationVarsBaseBatch`. /// Note: if the length of `EvaluationVarsBaseBatch` is not a multiple of `P::WIDTH`, then the /// leftovers at the end are ignored. +#[derive(Debug)] pub struct EvaluationVarsBaseBatchIterPacked<'a, P: PackedField> { /// Index to yield next, in units of `P::Scalar`. E.g. if `P::WIDTH == 4`, then we will yield /// the vars for points `i`, `i + 1`, `i + 2`, and `i + 3`, packed. @@ -219,7 +221,7 @@ impl<'a, const D: usize> EvaluationTargets<'a, D> { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct EvaluationTargets<'a, const D: usize> { pub local_constants: &'a [ExtensionTarget], pub local_wires: &'a [ExtensionTarget], diff --git a/plonky2/src/util/context_tree.rs b/plonky2/src/util/context_tree.rs index 2e70fd61ca..f3ba5b7282 100644 --- a/plonky2/src/util/context_tree.rs +++ b/plonky2/src/util/context_tree.rs @@ -8,6 +8,7 @@ use alloc::{ use log::{log, Level}; /// The hierarchy of contexts, and the gate count contributed by each one. Useful for debugging. +#[derive(Debug)] pub(crate) struct ContextTree { /// The name of this scope. name: String, diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index c26e60d3f2..f7880f8365 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -113,7 +113,6 @@ pub mod default { use crate::gates::reducing_extension::ReducingExtensionGate; use crate::hash::hash_types::RichField; use crate::util::serialization::GateSerializer; - /// A gate serializer that can be used to serialize all default gates supported /// by the `plonky2` library. /// Being a unit struct, it can be simply called as @@ -123,6 +122,7 @@ pub mod default { /// ``` /// Applications using custom gates should define their own serializer implementing /// the `GateSerializer` trait. This can be easily done through the `impl_gate_serializer` macro. + #[derive(Debug)] pub struct DefaultGateSerializer; impl, const D: usize> GateSerializer for DefaultGateSerializer { impl_gate_serializer! { diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index 15bae9acd1..527d8b4f6f 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -140,7 +140,7 @@ pub mod default { /// Applications using custom generators should define their own serializer implementing /// the `WitnessGeneratorSerializer` trait. This can be easily done through the /// `impl_generator_serializer` macro. - #[derive(Default)] + #[derive(Debug, Default)] pub struct DefaultGeneratorSerializer, const D: usize> { pub _phantom: PhantomData, } diff --git a/plonky2/src/util/timing.rs b/plonky2/src/util/timing.rs index 0ab721be52..f5c8f1763a 100644 --- a/plonky2/src/util/timing.rs +++ b/plonky2/src/util/timing.rs @@ -4,6 +4,7 @@ use web_time::{Duration, Instant}; /// The hierarchy of scopes, and the time consumed by each one. Useful for profiling. #[cfg(feature = "timing")] +#[derive(Debug)] pub struct TimingTree { /// The name of this scope. name: String, @@ -18,6 +19,7 @@ pub struct TimingTree { } #[cfg(not(feature = "timing"))] +#[derive(Debug)] pub struct TimingTree(Level); #[cfg(feature = "timing")] From c94dc6f858756157b3273c7451e1e667cfb484c9 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:50:43 -0500 Subject: [PATCH 153/175] Version bump pre-release (#1536) * Bump versions * Bump hashbrown and ahash accordingly * Update changelog --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 4 ++-- field/Cargo.toml | 2 +- maybe_rayon/Cargo.toml | 2 +- plonky2/Cargo.toml | 2 +- starky/Cargo.toml | 2 +- 6 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..e327ab779b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - 2024-02-20 +* Initial CHANGELOG tracking. diff --git a/Cargo.toml b/Cargo.toml index ae7534b515..6b9de8db33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,9 @@ members = ["field", "maybe_rayon", "plonky2", "starky", "util"] resolver = "2" [workspace.dependencies] -ahash = { version = "0.8.3", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. +ahash = { version = "0.8.7", default-features = false, features = ["compile-time-rng"] } # NOTE: Be sure to keep this version the same as the dependency in `hashbrown`. anyhow = { version = "1.0.40", default-features = false } -hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. +hashbrown = { version = "0.14.3", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. itertools = { version = "0.11.0", default-features = false } log = { version = "0.4.14", default-features = false } num = { version = "0.4", default-features = false, features = ["rand"] } diff --git a/field/Cargo.toml b/field/Cargo.toml index 86c681a7fb..2f893ea52d 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2_field" description = "Finite field arithmetic" -version = "0.1.1" +version = "0.2.0" authors = ["Daniel Lubarov ", "William Borgeaud ", "Jacqueline Nabaglo ", "Hamish Ivey-Law "] edition.workspace = true license.workspace = true diff --git a/maybe_rayon/Cargo.toml b/maybe_rayon/Cargo.toml index 59e09349ab..ec1987ed4b 100644 --- a/maybe_rayon/Cargo.toml +++ b/maybe_rayon/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2_maybe_rayon" description = "Feature-gated wrapper around rayon" -version = "0.1.1" +version = "0.2.0" edition.workspace = true license.workspace = true homepage.workspace = true diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 2185462b6d..ca5d63fb8f 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2" description = "Recursive SNARKs based on PLONK and FRI" -version = "0.1.4" +version = "0.2.0" authors = ["Daniel Lubarov ", "William Borgeaud ", "Nicholas Ward "] readme = "README.md" edition.workspace = true diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 6ad554f9d4..9c9df10dfa 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "starky" description = "Implementation of STARKs" -version = "0.1.2" +version = "0.2.0" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" edition.workspace = true From 7445ec911b0c5a1d94062ef2d5cae4ec08ee9a3f Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 20 Feb 2024 18:50:21 -0500 Subject: [PATCH 154/175] Bump plonky2-util version (#1538) --- util/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/Cargo.toml b/util/Cargo.toml index 758391c3b9..441111afe6 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2_util" description = "Utilities used by Plonky2" -version = "0.1.1" +version = "0.2.0" license = "MIT OR Apache-2.0" edition = "2021" From 16746f1ed70df1ab6dd40f897f092daec166af72 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 23 Feb 2024 15:39:10 +0100 Subject: [PATCH 155/175] chore: remove conditional compilation for debug_utils (#1540) * chore: remove conditional compilation for debug_utils * chore: update CHANGELOG --- CHANGELOG.md | 5 +++++ starky/src/cross_table_lookup.rs | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e327ab779b..973d29d8c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,5 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed +Always compile cross_table_lookups::debug_utils ([#1540](https://github.com/0xPolygonZero/plonky2/pull/1540)) + ## [0.2.0] - 2024-02-20 * Initial CHANGELOG tracking. diff --git a/starky/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs index 26e1576e48..a4b3cef688 100644 --- a/starky/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -1041,12 +1041,10 @@ pub fn verify_cross_table_lookups_circuit< debug_assert!(ctl_zs_openings.iter_mut().all(|iter| iter.next().is_none())); } -/// Debugging module, to assert correctness of the different CTLs of a multi-STARK system, +/// Debugging module used to assert correctness of the different CTLs of a multi-STARK system, /// that can be used during the proof generation process. /// -/// **Note**: this is an expensive check, hence is only available when the `debug_assertions` -/// flag is activated, to not hinder performances with regular `release` build. -#[cfg(debug_assertions)] +/// **Note**: This is an expensive check. pub mod debug_utils { #[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; From 75c746e77f60697d40a44ac1fe6c5cbdf4345054 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 27 Feb 2024 22:10:13 +0000 Subject: [PATCH 156/175] Create CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..cd2051c110 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @muursh @wborgeaud @Nashtare From 0817fd8e983d82aa0332726cf633584da495cb24 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:21:34 +0900 Subject: [PATCH 157/175] Pacify clippy (#1547) --- field/src/extension/mod.rs | 8 ++++---- plonky2/src/hash/poseidon.rs | 4 ++-- plonky2/src/iop/witness.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/field/src/extension/mod.rs b/field/src/extension/mod.rs index 3586055e3f..bcf8537ec5 100644 --- a/field/src/extension/mod.rs +++ b/field/src/extension/mod.rs @@ -125,9 +125,9 @@ impl FieldExtension<1> for F { } /// Flatten the slice by sending every extension field element to its D-sized canonical representation. -pub fn flatten(l: &[F::Extension]) -> Vec +pub fn flatten(l: &[F::Extension]) -> Vec where - F: Extendable, + F: Field + Extendable, { l.iter() .flat_map(|x| x.to_basefield_array().to_vec()) @@ -135,9 +135,9 @@ where } /// Batch every D-sized chunks into extension field elements. -pub fn unflatten(l: &[F]) -> Vec +pub fn unflatten(l: &[F]) -> Vec where - F: Extendable, + F: Field + Extendable, { debug_assert_eq!(l.len() % D, 0); l.chunks_exact(D) diff --git a/plonky2/src/hash/poseidon.rs b/plonky2/src/hash/poseidon.rs index ae5a26c14e..a7c763252e 100644 --- a/plonky2/src/hash/poseidon.rs +++ b/plonky2/src/hash/poseidon.rs @@ -923,7 +923,7 @@ impl AlgebraicHasher for PoseidonHash { pub(crate) mod test_helpers { use super::*; - pub(crate) fn check_test_vectors( + pub(crate) fn check_test_vectors( test_vectors: Vec<([u64; SPONGE_WIDTH], [u64; SPONGE_WIDTH])>, ) where F: Poseidon, @@ -941,7 +941,7 @@ pub(crate) mod test_helpers { } } - pub(crate) fn check_consistency() + pub(crate) fn check_consistency() where F: Poseidon, { diff --git a/plonky2/src/iop/witness.rs b/plonky2/src/iop/witness.rs index 40377aa3e4..85af6ca41b 100644 --- a/plonky2/src/iop/witness.rs +++ b/plonky2/src/iop/witness.rs @@ -14,7 +14,7 @@ use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::{BoolTarget, Target}; use crate::iop::wire::Wire; use crate::plonk::circuit_data::{VerifierCircuitTarget, VerifierOnlyCircuitData}; -use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use crate::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::plonk::proof::{Proof, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget}; pub trait WitnessWrite { @@ -222,7 +222,7 @@ pub trait Witness: WitnessWrite { } } - fn get_merkle_cap_target>(&self, cap_target: MerkleCapTarget) -> MerkleCap + fn get_merkle_cap_target(&self, cap_target: MerkleCapTarget) -> MerkleCap where F: RichField, H: AlgebraicHasher, From 44dc0f96ff63b5c1fa503d44d2bd1f1b3f8cf3a7 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:27:21 +0900 Subject: [PATCH 158/175] Add mention to versions in local dependencies (#1546) --- field/Cargo.toml | 2 +- plonky2/Cargo.toml | 6 +++--- starky/Cargo.toml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/field/Cargo.toml b/field/Cargo.toml index 2f893ea52d..a15e375619 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -20,7 +20,7 @@ static_assertions = { workspace = true } unroll = { workspace = true } # Local dependencies -plonky2_util = { path = "../util", default-features = false } +plonky2_util = { version = "0.2.0", path = "../util", default-features = false } # Display math equations properly in documentation diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index ca5d63fb8f..88d108395f 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -34,9 +34,9 @@ unroll = { workspace = true } web-time = { version = "1.0.0", optional = true } # Local dependencies -plonky2_field = { path = "../field", default-features = false } -plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } -plonky2_util = { path = "../util", default-features = false } +plonky2_field = { version = "0.2.0", path = "../field", default-features = false } +plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } +plonky2_util = { version = "0.2.0", path = "../util", default-features = false } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 9c9df10dfa..69b89902b2 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -26,9 +26,9 @@ log = { workspace = true } num-bigint = { version = "0.4.3", default-features = false } # Local dependencies -plonky2 = { path = "../plonky2", default-features = false } -plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false } -plonky2_util = { path = "../util", default-features = false } +plonky2 = { version = "0.2.0", path = "../plonky2", default-features = false } +plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } +plonky2_util = { version = "0.2.0", path = "../util", default-features = false } [dev-dependencies] env_logger = { version = "0.9.0", default-features = false } From 66127bcf7396786510bb84a0708b20352561fe1c Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 1 Mar 2024 07:54:08 +0900 Subject: [PATCH 159/175] Bump starky (#1549) --- CHANGELOG.md | 2 ++ starky/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 973d29d8c6..7fc92fb2da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.2.1] - 2024-03-01 (`starky` crate only) + ### Changed Always compile cross_table_lookups::debug_utils ([#1540](https://github.com/0xPolygonZero/plonky2/pull/1540)) diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 69b89902b2..cd7a21a35a 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "starky" description = "Implementation of STARKs" -version = "0.2.0" +version = "0.2.1" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" edition.workspace = true From 316e13ea4b8b3a2cc660ff9b8edc2701d6f83eac Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 4 Mar 2024 14:05:16 -0800 Subject: [PATCH 160/175] example documentation fix --- plonky2/examples/range_check.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plonky2/examples/range_check.rs b/plonky2/examples/range_check.rs index 20b551b7f4..d9351e1b1c 100644 --- a/plonky2/examples/range_check.rs +++ b/plonky2/examples/range_check.rs @@ -16,6 +16,8 @@ fn main() -> Result<()> { // The secret value. let value = builder.add_virtual_target(); + + // Registered as a public input (even though it's secret) so we can print out the value later. builder.register_public_input(value); let log_max = 6; From a137b64ac12d6640560d4098d2bb2be26aa0edac Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:01:23 +0900 Subject: [PATCH 161/175] Add SECURITY.md and move contribution guidance to CONTRIBUTING.md (#1556) --- CONTRIBUTING.md | 100 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 103 +----------------------------------------------- SECURITY.md | 17 ++++++++ 3 files changed, 119 insertions(+), 101 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..7bfabd1c02 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,100 @@ +# Guidance for external contributors + +Do you feel keen and able to help with Plonky2? That's great! We +encourage external contributions! + +We want to make it easy for you to contribute, but at the same time we +must manage the burden of reviewing external contributions. We are a +small team, and the time we spend reviewing external contributions is +time we are not developing ourselves. + +We also want to help you to avoid inadvertently duplicating work that +is already underway, or building something that we will not +want to incorporate. + +First and foremost, please keep in mind that this is a highly +technical piece of software and contributing is only suitable for +experienced mathematicians, cryptographers and software engineers. + +The Polygon Zero Team reserves the right to accept or reject any +external contribution for any reason, including a simple lack of time +to maintain it (now or in the future); we may even decline to review +something that is not considered a sufficiently high priority for us. + +To avoid disappointment, please communicate your intention to +contribute openly, while respecting the limited time and availability +we have to review and provide guidance for external contributions. It +is a good idea to drop a note in our public Discord #development +channel of your intention to work on something, whether an issue, a +new feature, or a performance improvement. This is probably all that's +really required to avoid duplication of work with other contributors. + +What follows are some more specific requests for how to write PRs in a +way that will make them easy for us to review. Deviating from these +guidelines may result in your PR being rejected, ignored or forgotten. + + +## General guidance for your PR + +Obviously PRs will not be considered unless they pass our Github +CI. The Github CI is not executed for PRs from forks, but you can +simulate the Github CI by running the commands in +`.github/workflows/ci.yml`. + +Under no circumstances should a single PR mix different purposes: Your +PR is either a bug fix, a new feature, or a performance improvement, +never a combination. Nor should you include, for example, two +unrelated performance improvements in one PR. Please just submit +separate PRs. The goal is to make reviewing your PR as simple as +possible, and you should be thinking about how to compose the PR to +minimise the burden on the reviewer. + +Also note that any PR that depends on unstable features will be +automatically rejected. The Polygon Zero Team may enable a small +number of unstable features in the future for our exclusive use; +nevertheless we aim to minimise the number of such features, and the +number of uses of them, to the greatest extent possible. + +Here are a few specific guidelines for the three main categories of +PRs that we expect: + + +### The PR fixes a bug + +In the PR description, please clearly but briefly describe + +1. the bug (could be a reference to a GH issue; if it is from a + discussion (on Discord/email/etc. for example), please copy in the + relevant parts of the discussion); +2. what turned out to the cause the bug; and +3. how the PR fixes the bug. + +Wherever possible, PRs that fix bugs should include additional tests +that (i) trigger the original bug and (ii) pass after applying the PR. + + +### The PR implements a new feature + +If you plan to contribute an implementation of a new feature, please +double-check with the Polygon Zero team that it is a sufficient +priority for us that it will be reviewed and integrated. + +In the PR description, please clearly but briefly describe + +1. what the feature does +2. the approach taken to implement it + +All PRs for new features must include a suitable test suite. + + +### The PR improves performance + +Performance improvements are particularly welcome! Please note that it +can be quite difficult to establish true improvements for the +workloads we care about. To help filter out false positives, the PR +description for a performance improvement must clearly identify + +1. the target bottleneck (only one per PR to avoid confusing things!) +2. how performance is measured +3. characteristics of the machine used (CPU, OS, #threads if appropriate) +4. performance before and after the PR diff --git a/README.md b/README.md index f4bdfc5892..a022ac2908 100644 --- a/README.md +++ b/README.md @@ -60,108 +60,9 @@ static GLOBAL: Jemalloc = Jemalloc; Jemalloc is known to cause crashes when a binary compiled for x86 is run on an Apple silicon-based Mac under [Rosetta 2](https://support.apple.com/en-us/HT211861). If you are experiencing crashes on your Apple silicon Mac, run `rustc --print target-libdir`. The output should contain `aarch64-apple-darwin`. If the output contains `x86_64-apple-darwin`, then you are running the Rust toolchain for x86; we recommend switching to the native ARM version. +## Contributing guidelines -## Guidance for external contributors - -Do you feel keen and able to help with Plonky2? That's great! We -encourage external contributions! - -We want to make it easy for you to contribute, but at the same time we -must manage the burden of reviewing external contributions. We are a -small team, and the time we spend reviewing external contributions is -time we are not developing ourselves. - -We also want to help you to avoid inadvertently duplicating work that -is already underway, or building something that we will not -want to incorporate. - -First and foremost, please keep in mind that this is a highly -technical piece of software and contributing is only suitable for -experienced mathematicians, cryptographers and software engineers. - -The Polygon Zero Team reserves the right to accept or reject any -external contribution for any reason, including a simple lack of time -to maintain it (now or in the future); we may even decline to review -something that is not considered a sufficiently high priority for us. - -To avoid disappointment, please communicate your intention to -contribute openly, while respecting the limited time and availability -we have to review and provide guidance for external contributions. It -is a good idea to drop a note in our public Discord #development -channel of your intention to work on something, whether an issue, a -new feature, or a performance improvement. This is probably all that's -really required to avoid duplication of work with other contributors. - -What follows are some more specific requests for how to write PRs in a -way that will make them easy for us to review. Deviating from these -guidelines may result in your PR being rejected, ignored or forgotten. - - -### General guidance for your PR - -Obviously PRs will not be considered unless they pass our Github -CI. The Github CI is not executed for PRs from forks, but you can -simulate the Github CI by running the commands in -`.github/workflows/ci.yml`. - -Under no circumstances should a single PR mix different purposes: Your -PR is either a bug fix, a new feature, or a performance improvement, -never a combination. Nor should you include, for example, two -unrelated performance improvements in one PR. Please just submit -separate PRs. The goal is to make reviewing your PR as simple as -possible, and you should be thinking about how to compose the PR to -minimise the burden on the reviewer. - -Also note that any PR that depends on unstable features will be -automatically rejected. The Polygon Zero Team may enable a small -number of unstable features in the future for our exclusive use; -nevertheless we aim to minimise the number of such features, and the -number of uses of them, to the greatest extent possible. - -Here are a few specific guidelines for the three main categories of -PRs that we expect: - - -#### The PR fixes a bug - -In the PR description, please clearly but briefly describe - -1. the bug (could be a reference to a GH issue; if it is from a - discussion (on Discord/email/etc. for example), please copy in the - relevant parts of the discussion); -2. what turned out to the cause the bug; and -3. how the PR fixes the bug. - -Wherever possible, PRs that fix bugs should include additional tests -that (i) trigger the original bug and (ii) pass after applying the PR. - - -#### The PR implements a new feature - -If you plan to contribute an implementation of a new feature, please -double-check with the Polygon Zero team that it is a sufficient -priority for us that it will be reviewed and integrated. - -In the PR description, please clearly but briefly describe - -1. what the feature does -2. the approach taken to implement it - -All PRs for new features must include a suitable test suite. - - -#### The PR improves performance - -Performance improvements are particularly welcome! Please note that it -can be quite difficult to establish true improvements for the -workloads we care about. To help filter out false positives, the PR -description for a performance improvement must clearly identify - -1. the target bottleneck (only one per PR to avoid confusing things!) -2. how performance is measured -3. characteristics of the machine used (CPU, OS, #threads if appropriate) -4. performance before and after the PR - +See [CONTRIBUTING.md](./CONTRIBUTING.md). ## Licenses diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..d8e87e74ee --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Polygon Technology Security Information + +## Link to vulnerability disclosure details (Bug Bounty). +- Websites and Applications: https://hackerone.com/polygon-technology +- Smart Contracts: https://immunefi.com/bounty/polygon + +## Languages that our team speaks and understands. +Preferred-Languages: en + +## Security-related job openings at Polygon. +https://polygon.technology/careers + +## Polygon security contact details. +security@polygon.technology + +## The URL for accessing the security.txt file. +Canonical: https://polygon.technology/security.txt From 2a2becc415f03c0b3c74bf5e3df1177dd5d52859 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Thu, 14 Mar 2024 23:30:18 +0900 Subject: [PATCH 162/175] Fix CTLs with exactly two looking tables (#1555) --- CHANGELOG.md | 6 ++++-- starky/src/cross_table_lookup.rs | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc92fb2da..c363318fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Fix CTLs with exactly two looking tables ([#1555](https://github.com/0xPolygonZero/plonky2/pull/1555)) + ## [0.2.1] - 2024-03-01 (`starky` crate only) ### Changed -Always compile cross_table_lookups::debug_utils ([#1540](https://github.com/0xPolygonZero/plonky2/pull/1540)) +- Always compile cross_table_lookups::debug_utils ([#1540](https://github.com/0xPolygonZero/plonky2/pull/1540)) ## [0.2.0] - 2024-02-20 -* Initial CHANGELOG tracking. +- Initial CHANGELOG tracking. diff --git a/starky/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs index a4b3cef688..da50c24c17 100644 --- a/starky/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -123,7 +123,7 @@ impl CrossTableLookup { for (i, ctl) in ctls.iter().enumerate() { let all_tables = once(&ctl.looked_table).chain(&ctl.looking_tables); let num_appearances = all_tables.filter(|twc| twc.table == table).count(); - let is_helpers = num_appearances > 2; + let is_helpers = num_appearances > 1; if is_helpers { num_helpers_by_ctl[i] = ceil_div_usize(num_appearances, constraint_degree - 1); num_helpers += num_helpers_by_ctl[i]; @@ -290,8 +290,8 @@ pub(crate) fn num_ctl_helper_columns_by_table( for (table, group) in grouped_lookups.into_iter() { let sum = group.count(); - if sum > 2 { - // We only need helper columns if there are more than 2 columns. + if sum > 1 { + // We only need helper columns if there are at least 2 columns. num_by_table[table] = ceil_div_usize(sum, constraint_degree - 1); } } @@ -426,7 +426,7 @@ fn ctl_helper_zs_cols( /// The initial sum `s` is 0. /// For each row, if the `filter_column` evaluates to 1, then the row is selected. All the column linear combinations are evaluated at said row. /// The evaluations of each elements of `columns` are then combined together to form a value `v`. -/// The values `v`` are grouped together, in groups of size `constraint_degree - 1` (2 in our case). For each group, we construct a helper +/// The values `v`` are grouped together, in groups of size `constraint_degree - 1`. For each group, we construct a helper /// column: h = \sum_i 1/(v_i). /// /// The sum is updated: `s += \sum h_i`, and is pushed to the vector of partial sums `z``. @@ -455,7 +455,7 @@ fn partial_sums( z.push(z[z.len() - 1] + x); } z.reverse(); - if columns_filters.len() > 2 { + if columns_filters.len() > 1 { helper_columns.push(z.into()); } else { helper_columns = vec![z.into()]; From 4f8e63155071e7b01f50504cff7b47c8f889c5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alonso=20Gonz=C3=A1lez?= Date: Fri, 15 Mar 2024 12:43:45 +0100 Subject: [PATCH 163/175] Prove Starks without constraints (#1552) * Enable starks without constraints * Clippy * Add test stark without constraints * Missing file * Missing changes in the recursive side * Fix bug with recursion * Missing import * Clippy * Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> * Address reviews * Fix TODO * Apply suggestions from code review Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> * More reviews * Fix bug in eval_helper_columns * Apply suggestions from code review Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> * Address reviews * Allow <= blowup_factor + 1 constraints + reviews * Add unconstrined Stark * Missing file * Remove asserts --------- Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> --- CHANGELOG.md | 1 + field/src/polynomial/mod.rs | 4 +- plonky2/src/recursion/recursive_verifier.rs | 10 +- starky/src/fibonacci_stark.rs | 193 ++-------------- starky/src/get_challenges.rs | 17 +- starky/src/lib.rs | 4 + starky/src/lookup.rs | 48 ++-- starky/src/permutation_stark.rs | 236 ++++++++++++++++++++ starky/src/proof.rs | 38 +++- starky/src/prover.rs | 95 ++++---- starky/src/recursive_verifier.rs | 41 ++-- starky/src/stark.rs | 37 ++- starky/src/stark_testing.rs | 2 +- starky/src/unconstrained_stark.rs | 201 +++++++++++++++++ starky/src/verifier.rs | 17 +- 15 files changed, 647 insertions(+), 297 deletions(-) create mode 100644 starky/src/permutation_stark.rs create mode 100644 starky/src/unconstrained_stark.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c363318fb1..dd901fda49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Fix CTLs with exactly two looking tables ([#1555](https://github.com/0xPolygonZero/plonky2/pull/1555)) +- Make Starks without constraints provable ([#1552](https://github.com/0xPolygonZero/plonky2/pull/1552)) ## [0.2.1] - 2024-03-01 (`starky` crate only) diff --git a/field/src/polynomial/mod.rs b/field/src/polynomial/mod.rs index f61ad41983..c13bbca272 100644 --- a/field/src/polynomial/mod.rs +++ b/field/src/polynomial/mod.rs @@ -88,9 +88,7 @@ impl PolynomialValues { } pub fn degree(&self) -> usize { - self.degree_plus_one() - .checked_sub(1) - .expect("deg(0) is undefined") + self.degree_plus_one().saturating_sub(1) } pub fn degree_plus_one(&self) -> usize { diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index 82d1e81389..4635d56de2 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -1,3 +1,6 @@ +#[cfg(not(feature = "std"))] +use alloc::vec; + use crate::field::extension::Extendable; use crate::hash::hash_types::{HashOutTarget, RichField}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -149,13 +152,16 @@ impl, const D: usize> CircuitBuilder { let cap_height = fri_params.config.cap_height; let salt = salt_size(common_data.fri_params.hiding); - let num_leaves_per_oracle = &[ + let num_leaves_per_oracle = &mut vec![ common_data.num_preprocessed_polys(), config.num_wires + salt, common_data.num_zs_partial_products_polys() + common_data.num_all_lookup_polys() + salt, - common_data.num_quotient_polys() + salt, ]; + if common_data.num_quotient_polys() > 0 { + num_leaves_per_oracle.push(common_data.num_quotient_polys() + salt); + } + ProofTarget { wires_cap: self.add_virtual_cap(cap_height), plonk_zs_partial_products_cap: self.add_virtual_cap(cap_height), diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 4bfbf40443..7aa40b6ed9 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -15,7 +15,6 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::lookup::{Column, Lookup}; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; @@ -132,135 +131,6 @@ impl, const D: usize> Stark for FibonacciStar } } -/// Similar system than above, but with extra columns to illustrate the permutation argument. -/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition -/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. -/// Note: The `i, j` columns are the columns used to test the permutation argument. -#[derive(Copy, Clone)] -struct FibonacciWithPermutationStark, const D: usize> { - num_rows: usize, - _phantom: PhantomData, -} - -impl, const D: usize> FibonacciWithPermutationStark { - // The first public input is `x0`. - const PI_INDEX_X0: usize = 0; - // The second public input is `x1`. - const PI_INDEX_X1: usize = 1; - // The third public input is the second element of the last row, which should be equal to the - // `num_rows`-th Fibonacci number. - const PI_INDEX_RES: usize = 2; - - const fn new(num_rows: usize) -> Self { - Self { - num_rows, - _phantom: PhantomData, - } - } - - /// Generate the trace using `x0, x1, 0, 1, 1` as initial state values. - fn generate_trace(&self, x0: F, x1: F) -> Vec> { - let mut trace_rows = (0..self.num_rows) - .scan([x0, x1, F::ZERO, F::ONE, F::ONE], |acc, _| { - let tmp = *acc; - acc[0] = tmp[1]; - acc[1] = tmp[0] + tmp[1]; - acc[2] = tmp[2] + F::ONE; - acc[3] = tmp[3] + F::ONE; - // acc[4] (i.e. frequency column) remains unchanged, as we're permuting a strictly monotonous sequence. - Some(tmp) - }) - .collect::>(); - trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another. - trace_rows_to_poly_values(trace_rows) - } -} - -const FIBONACCI_PERM_COLUMNS: usize = 5; -const FIBONACCI_PERM_PUBLIC_INPUTS: usize = 3; - -impl, const D: usize> Stark - for FibonacciWithPermutationStark -{ - type EvaluationFrame = StarkFrame - where - FE: FieldExtension, - P: PackedField; - - type EvaluationFrameTarget = StarkFrame< - ExtensionTarget, - ExtensionTarget, - FIBONACCI_PERM_COLUMNS, - FIBONACCI_PERM_PUBLIC_INPUTS, - >; - - fn eval_packed_generic( - &self, - vars: &Self::EvaluationFrame, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - let public_inputs = vars.get_public_inputs(); - - // Check public inputs. - yield_constr.constraint_first_row(local_values[0] - public_inputs[Self::PI_INDEX_X0]); - yield_constr.constraint_first_row(local_values[1] - public_inputs[Self::PI_INDEX_X1]); - yield_constr.constraint_last_row(local_values[1] - public_inputs[Self::PI_INDEX_RES]); - - // x0' <- x1 - yield_constr.constraint_transition(next_values[0] - local_values[1]); - // x1' <- x0 + x1 - yield_constr.constraint_transition(next_values[1] - local_values[0] - local_values[1]); - } - - fn eval_ext_circuit( - &self, - builder: &mut CircuitBuilder, - vars: &Self::EvaluationFrameTarget, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - let local_values = vars.get_local_values(); - let next_values = vars.get_next_values(); - let public_inputs = vars.get_public_inputs(); - // Check public inputs. - let pis_constraints = [ - builder.sub_extension(local_values[0], public_inputs[Self::PI_INDEX_X0]), - builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_X1]), - builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_RES]), - ]; - yield_constr.constraint_first_row(builder, pis_constraints[0]); - yield_constr.constraint_first_row(builder, pis_constraints[1]); - yield_constr.constraint_last_row(builder, pis_constraints[2]); - - // x0' <- x1 - let first_col_constraint = builder.sub_extension(next_values[0], local_values[1]); - yield_constr.constraint_transition(builder, first_col_constraint); - // x1' <- x0 + x1 - let second_col_constraint = { - let tmp = builder.sub_extension(next_values[1], local_values[0]); - builder.sub_extension(tmp, local_values[1]) - }; - yield_constr.constraint_transition(builder, second_col_constraint); - } - - fn constraint_degree(&self) -> usize { - 2 - } - - fn lookups(&self) -> Vec> { - vec![Lookup { - columns: vec![Column::single(2)], - table_column: Column::single(3), - frequencies_column: Column::single(4), - filter_columns: vec![None; 1], - }] - } -} - #[cfg(test)] mod tests { use anyhow::Result; @@ -274,7 +144,7 @@ mod tests { use plonky2::util::timing::TimingTree; use crate::config::StarkConfig; - use crate::fibonacci_stark::{FibonacciStark, FibonacciWithPermutationStark}; + use crate::fibonacci_stark::FibonacciStark; use crate::proof::StarkProofWithPublicInputs; use crate::prover::prove; use crate::recursive_verifier::{ @@ -294,30 +164,15 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S1 = FibonacciStark; - type S2 = FibonacciWithPermutationStark; + type S = FibonacciStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - // Test first STARK - let stark = S1::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - &public_inputs, - &mut TimingTree::default(), - )?; - - verify_stark_proof(stark, proof, &config)?; - - // Test second STARK - let stark = S2::new(num_rows); + let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( + let proof = prove::( stark, &config, trace, @@ -333,14 +188,10 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S1 = FibonacciStark; - type S2 = FibonacciWithPermutationStark; + type S = FibonacciStark; let num_rows = 1 << 5; - let stark = S1::new(num_rows); - test_stark_low_degree(stark)?; - - let stark = S2::new(num_rows); + let stark = S::new(num_rows); test_stark_low_degree(stark) } @@ -349,14 +200,11 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S1 = FibonacciStark; - type S2 = FibonacciWithPermutationStark; + type S = FibonacciStark; let num_rows = 1 << 5; - let stark = S1::new(num_rows); - test_stark_circuit_constraints::(stark)?; - let stark = S2::new(num_rows); - test_stark_circuit_constraints::(stark) + let stark = S::new(num_rows); + test_stark_circuit_constraints::(stark) } #[test] @@ -365,31 +213,16 @@ mod tests { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; - type S1 = FibonacciStark; - type S2 = FibonacciWithPermutationStark; + type S = FibonacciStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; // Test first STARK - let stark = S1::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - &public_inputs, - &mut TimingTree::default(), - )?; - verify_stark_proof(stark, proof.clone(), &config)?; - - recursive_proof::(stark, proof, &config, true)?; - - // Test second STARK - let stark = S2::new(num_rows); + let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( + let proof = prove::( stark, &config, trace, @@ -398,7 +231,7 @@ mod tests { )?; verify_stark_proof(stark, proof.clone(), &config)?; - recursive_proof::(stark, proof, &config, true) + recursive_proof::(stark, proof, &config, true) } fn recursive_proof< diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index be75b0e010..8000a9ef90 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -28,7 +28,7 @@ fn get_challenges( challenges: Option<&GrandProductChallengeSet>, trace_cap: Option<&MerkleCap>, auxiliary_polys_cap: Option<&MerkleCap>, - quotient_polys_cap: &MerkleCap, + quotient_polys_cap: Option<&MerkleCap>, openings: &StarkOpeningSet, commit_phase_merkle_caps: &[MerkleCap], final_poly: &PolynomialCoeffs, @@ -60,7 +60,9 @@ where let stark_alphas = challenger.get_n_challenges(num_challenges); - challenger.observe_cap(quotient_polys_cap); + if let Some(quotient_polys_cap) = quotient_polys_cap { + challenger.observe_cap(quotient_polys_cap); + } let stark_zeta = challenger.get_extension_challenge::(); challenger.observe_openings(&openings.to_fri_openings()); @@ -125,7 +127,7 @@ where challenges, trace_cap, auxiliary_polys_cap.as_ref(), - quotient_polys_cap, + quotient_polys_cap.as_ref(), openings, commit_phase_merkle_caps, final_poly, @@ -168,7 +170,7 @@ fn get_challenges_target( challenges: Option<&GrandProductChallengeSet>, trace_cap: Option<&MerkleCapTarget>, auxiliary_polys_cap: Option<&MerkleCapTarget>, - quotient_polys_cap: &MerkleCapTarget, + quotient_polys_cap: Option<&MerkleCapTarget>, openings: &StarkOpeningSetTarget, commit_phase_merkle_caps: &[MerkleCapTarget], final_poly: &PolynomialCoeffsExtTarget, @@ -200,7 +202,10 @@ where let stark_alphas = challenger.get_n_challenges(builder, num_challenges); - challenger.observe_cap(quotient_polys_cap); + if let Some(cap) = quotient_polys_cap { + challenger.observe_cap(cap); + } + let stark_zeta = challenger.get_extension_challenge(builder); challenger.observe_openings(&openings.to_fri_openings(builder.zero())); @@ -266,7 +271,7 @@ impl StarkProofTarget { challenges, trace_cap, auxiliary_polys_cap.as_ref(), - quotient_polys_cap, + quotient_polys_cap.as_ref(), openings, commit_phase_merkle_caps, final_poly, diff --git a/starky/src/lib.rs b/starky/src/lib.rs index 63777fbaf2..24bea760f1 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -340,3 +340,7 @@ pub mod verifier; #[cfg(test)] pub mod fibonacci_stark; +#[cfg(test)] +pub mod permutation_stark; +#[cfg(test)] +pub mod unconstrained_stark; diff --git a/starky/src/lookup.rs b/starky/src/lookup.rs index 80a01b0859..16383cd690 100644 --- a/starky/src/lookup.rs +++ b/starky/src/lookup.rs @@ -431,7 +431,10 @@ impl Lookup { pub fn num_helper_columns(&self, constraint_degree: usize) -> usize { // One helper column for each column batch of size `constraint_degree-1`, // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. - ceil_div_usize(self.columns.len(), constraint_degree - 1) + 1 + ceil_div_usize( + self.columns.len(), + constraint_degree.checked_sub(1).unwrap_or(1), + ) + 1 } } @@ -576,11 +579,6 @@ pub(crate) fn lookup_helper_columns( challenge: F, constraint_degree: usize, ) -> Vec> { - assert!( - constraint_degree == 2 || constraint_degree == 3, - "TODO: Allow other constraint degrees." - ); - assert_eq!(lookup.columns.len(), lookup.filter_columns.len()); let num_total_logup_entries = trace_poly_values[0].values.len() * lookup.columns.len(); @@ -666,11 +664,11 @@ pub(crate) fn eval_helper_columns( P: PackedField, { if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - + let chunk_size = constraint_degree.checked_sub(1).unwrap_or(1); + for (chunk, (fs, &h)) in columns + .chunks(chunk_size) + .zip(filter.chunks(chunk_size).zip(helper_columns)) + { match chunk.len() { 2 => { let combin0 = challenges.combine(&chunk[0]); @@ -719,11 +717,11 @@ pub(crate) fn eval_helper_columns_circuit, const D: consumer: &mut RecursiveConstraintConsumer, ) { if !helper_columns.is_empty() { - for (j, chunk) in columns.chunks(constraint_degree - 1).enumerate() { - let fs = - &filter[(constraint_degree - 1) * j..(constraint_degree - 1) * j + chunk.len()]; - let h = helper_columns[j]; - + let chunk_size = constraint_degree.checked_sub(1).unwrap_or(1); + for (chunk, (fs, &h)) in columns + .chunks(chunk_size) + .zip(filter.chunks(chunk_size).zip(helper_columns)) + { let one = builder.one_extension(); match chunk.len() { 2 => { @@ -774,11 +772,17 @@ pub(crate) fn get_helper_cols( challenge: GrandProductChallenge, constraint_degree: usize, ) -> Vec> { - let num_helper_columns = ceil_div_usize(columns_filters.len(), constraint_degree - 1); + let num_helper_columns = ceil_div_usize( + columns_filters.len(), + constraint_degree.checked_sub(1).unwrap_or(1), + ); let mut helper_columns = Vec::with_capacity(num_helper_columns); - for mut cols_filts in &columns_filters.iter().chunks(constraint_degree - 1) { + for mut cols_filts in &columns_filters + .iter() + .chunks(constraint_degree.checked_sub(1).unwrap_or(1)) + { let (first_col, first_filter) = cols_filts.next().unwrap(); let mut filter_col = Vec::with_capacity(degree); @@ -885,10 +889,6 @@ pub(crate) fn eval_packed_lookups_generic, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> PermutationStark { + const fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using `x0, x0+1, 1` as initial state values. + fn generate_trace(&self, x0: F) -> Vec> { + let mut trace_rows = (0..self.num_rows) + .scan([x0, x0 + F::ONE, F::ONE], |acc, _| { + let tmp = *acc; + acc[0] = tmp[0] + F::ONE; + acc[1] = tmp[1] + F::ONE; + // acc[2] (i.e. frequency column) remains unchanged, as we're permuting a strictly monotonous sequence. + Some(tmp) + }) + .collect::>(); + trace_rows[self.num_rows - 1][1] = x0; // So that column 0 and 1 are permutation of one another. + trace_rows_to_poly_values(trace_rows) + } +} + +const PERM_COLUMNS: usize = 3; +const PERM_PUBLIC_INPUTS: usize = 1; + +impl, const D: usize> Stark for PermutationStark { + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = + StarkFrame, ExtensionTarget, PERM_COLUMNS, PERM_PUBLIC_INPUTS>; + + fn constraint_degree(&self) -> usize { + 0 + } + + fn lookups(&self) -> Vec> { + vec![Lookup { + columns: vec![Column::single(0)], + table_column: Column::single(1), + frequencies_column: Column::single(2), + filter_columns: vec![None; 1], + }] + } + + // We don't constrain any register, for the sake of highlighting the permutation argument only. + fn eval_packed_generic( + &self, + _vars: &Self::EvaluationFrame, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + } + + // We don't constrain any register, for the sake of highlighting the permutation argument only. + fn eval_ext_circuit( + &self, + _builder: &mut CircuitBuilder, + _vars: &Self::EvaluationFrameTarget, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::extension::Extendable; + use plonky2::field::types::Field; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; + use plonky2::util::timing::TimingTree; + + use crate::config::StarkConfig; + use crate::permutation_stark::PermutationStark; + use crate::proof::StarkProofWithPublicInputs; + use crate::prover::prove; + use crate::recursive_verifier::{ + add_virtual_stark_proof_with_pis, set_stark_proof_with_pis_target, + verify_stark_proof_circuit, + }; + use crate::stark::Stark; + use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; + use crate::verifier::verify_stark_proof; + + #[test] + fn test_pemutations_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PermutationStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + + let public_input = F::ZERO; + + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_input); + let proof = prove::( + stark, + &config, + trace, + &[public_input], + &mut TimingTree::default(), + )?; + + verify_stark_proof(stark, proof, &config) + } + + #[test] + fn test_permutation_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PermutationStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_low_degree(stark) + } + + #[test] + fn test_permutation_stark_circuit() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PermutationStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_circuit_constraints::(stark) + } + + #[test] + fn test_recursive_stark_verifier() -> Result<()> { + init_logger(); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PermutationStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_input = F::ZERO; + + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_input); + let proof = prove::( + stark, + &config, + trace, + &[public_input], + &mut TimingTree::default(), + )?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true) + } + + fn recursive_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark + Copy, + InnerC: GenericConfig, + const D: usize, + >( + stark: S, + inner_proof: StarkProofWithPublicInputs, + inner_config: &StarkConfig, + print_gate_counts: bool, + ) -> Result<()> + where + InnerC::Hasher: AlgebraicHasher, + { + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let pt = + add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero()); + + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + + if print_gate_counts { + builder.print_gate_counts(0); + } + + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof) + } + + fn init_logger() { + let _ = env_logger::builder().format_timestamp(None).try_init(); + } +} diff --git a/starky/src/proof.rs b/starky/src/proof.rs index b6ea53efc9..d521be027a 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -33,7 +33,7 @@ pub struct StarkProof, C: GenericConfig, /// Optional merkle cap of LDEs of permutation Z values, if any. pub auxiliary_polys_cap: Option>, /// Merkle cap of LDEs of trace values. - pub quotient_polys_cap: MerkleCap, + pub quotient_polys_cap: Option>, /// Purported values of each polynomial at the challenge point. pub openings: StarkOpeningSet, /// A batch FRI argument for all openings. @@ -61,7 +61,7 @@ pub struct StarkProofTarget { /// Optional `Target` for the Merkle cap of lookup helper and CTL columns LDEs, if any. pub auxiliary_polys_cap: Option, /// `Target` for the Merkle cap of quotient polynomial evaluations LDEs. - pub quotient_polys_cap: MerkleCapTarget, + pub quotient_polys_cap: Option, /// `Target`s for the purported values of each polynomial at the challenge point. pub openings: StarkOpeningSetTarget, /// `Target`s for the batch FRI argument for all openings. @@ -76,7 +76,10 @@ impl StarkProofTarget { if let Some(poly) = &self.auxiliary_polys_cap { buffer.write_target_merkle_cap(poly)?; } - buffer.write_target_merkle_cap(&self.quotient_polys_cap)?; + buffer.write_bool(self.quotient_polys_cap.is_some())?; + if let Some(poly) = &self.quotient_polys_cap { + buffer.write_target_merkle_cap(poly)?; + } buffer.write_target_fri_proof(&self.opening_proof)?; self.openings.to_buffer(buffer)?; Ok(()) @@ -90,7 +93,11 @@ impl StarkProofTarget { } else { None }; - let quotient_polys_cap = buffer.read_target_merkle_cap()?; + let quotient_polys_cap = if buffer.read_bool()? { + Some(buffer.read_target_merkle_cap()?) + } else { + None + }; let opening_proof = buffer.read_target_fri_proof()?; let openings = StarkOpeningSetTarget::from_buffer(buffer)?; @@ -253,7 +260,7 @@ pub struct StarkOpeningSet, const D: usize> { /// Openings of cross-table lookups `Z` polynomials at `1`. pub ctl_zs_first: Option>, /// Openings of quotient polynomials at `zeta`. - pub quotient_polys: Vec, + pub quotient_polys: Option>, } impl, const D: usize> StarkOpeningSet { @@ -266,7 +273,7 @@ impl, const D: usize> StarkOpeningSet { g: F, trace_commitment: &PolynomialBatch, auxiliary_polys_commitment: Option<&PolynomialBatch>, - quotient_commitment: &PolynomialBatch, + quotient_commitment: Option<&PolynomialBatch>, num_lookup_columns: usize, requires_ctl: bool, num_ctl_polys: &[usize], @@ -298,7 +305,7 @@ impl, const D: usize> StarkOpeningSet { let total_num_helper_cols: usize = num_ctl_polys.iter().sum(); auxiliary_first.unwrap()[num_lookup_columns + total_num_helper_cols..].to_vec() }), - quotient_polys: eval_commitment(zeta, quotient_commitment), + quotient_polys: quotient_commitment.map(|c| eval_commitment(zeta, c)), } } @@ -310,7 +317,7 @@ impl, const D: usize> StarkOpeningSet { .local_values .iter() .chain(self.auxiliary_polys.iter().flatten()) - .chain(&self.quotient_polys) + .chain(self.quotient_polys.iter().flatten()) .copied() .collect_vec(), }; @@ -360,7 +367,7 @@ pub struct StarkOpeningSetTarget { /// `ExtensionTarget`s for the opening of lookups and cross-table lookups `Z` polynomials at 1. pub ctl_zs_first: Option>, /// `ExtensionTarget`s for the opening of quotient polynomials at `zeta`. - pub quotient_polys: Vec>, + pub quotient_polys: Option>>, } impl StarkOpeningSetTarget { @@ -386,7 +393,10 @@ impl StarkOpeningSetTarget { } else { buffer.write_bool(false)?; } - buffer.write_target_ext_vec(&self.quotient_polys)?; + buffer.write_bool(self.quotient_polys.is_some())?; + if let Some(quotient_polys) = &self.quotient_polys { + buffer.write_target_ext_vec(quotient_polys)?; + } Ok(()) } @@ -409,7 +419,11 @@ impl StarkOpeningSetTarget { } else { None }; - let quotient_polys = buffer.read_target_ext_vec::()?; + let quotient_polys = if buffer.read_bool()? { + Some(buffer.read_target_ext_vec::()?) + } else { + None + }; Ok(Self { local_values, @@ -428,7 +442,7 @@ impl StarkOpeningSetTarget { .local_values .iter() .chain(self.auxiliary_polys.iter().flatten()) - .chain(&self.quotient_polys) + .chain(self.quotient_polys.iter().flatten()) .copied() .collect_vec(), }; diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 7014bdd34d..c7b77b9336 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -119,6 +119,12 @@ where "FRI total reduction arity is too large.", ); + let constraint_degree = stark.constraint_degree(); + assert!( + constraint_degree <= (1 << rate_bits) + 1, + "The degree of the Stark constraints must be <= blowup_factor + 1" + ); + // Permutation arguments. let constraint_degree = stark.constraint_degree(); @@ -238,38 +244,43 @@ where config, ) ); - let all_quotient_chunks = timed!( - timing, - "split quotient polys", - quotient_polys - .into_par_iter() - .flat_map(|mut quotient_poly| { - quotient_poly - .trim_to_len(degree * stark.quotient_degree_factor()) - .expect( - "Quotient has failed, the vanishing polynomial is not divisible by Z_H", - ); - // Split quotient into degree-n chunks. - quotient_poly.chunks(degree) - }) - .collect() - ); - // Commit to the quotient polynomials. - let quotient_commitment = timed!( - timing, - "compute quotient commitment", - PolynomialBatch::from_coeffs( - all_quotient_chunks, - rate_bits, - false, - config.fri_config.cap_height, + let (quotient_commitment, quotient_polys_cap) = if let Some(quotient_polys) = quotient_polys { + let all_quotient_chunks = timed!( timing, - None, - ) - ); - // Observe the quotient polynomials Merkle cap. - let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); - challenger.observe_cap("ient_polys_cap); + "split quotient polys", + quotient_polys + .into_par_iter() + .flat_map(|mut quotient_poly| { + quotient_poly + .trim_to_len(degree * stark.quotient_degree_factor()) + .expect( + "Quotient has failed, the vanishing polynomial is not divisible by Z_H", + ); + // Split quotient into degree-n chunks. + quotient_poly.chunks(degree) + }) + .collect() + ); + // Commit to the quotient polynomials. + let quotient_commitment = timed!( + timing, + "compute quotient commitment", + PolynomialBatch::from_coeffs( + all_quotient_chunks, + rate_bits, + false, + config.fri_config.cap_height, + timing, + None, + ) + ); + // Observe the quotient polynomials Merkle cap. + let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); + challenger.observe_cap("ient_polys_cap); + (Some(quotient_commitment), Some(quotient_polys_cap)) + } else { + (None, None) + }; let zeta = challenger.get_extension_challenge::(); @@ -288,7 +299,7 @@ where g, trace_commitment, auxiliary_polys_commitment.as_ref(), - "ient_commitment, + quotient_commitment.as_ref(), stark.num_lookup_helper_columns(config), stark.requires_ctls(), &num_ctl_polys, @@ -298,7 +309,7 @@ where let initial_merkle_trees = once(trace_commitment) .chain(&auxiliary_polys_commitment) - .chain(once("ient_commitment)) + .chain("ient_commitment) .collect_vec(); let opening_proof = timed!( @@ -342,13 +353,17 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( num_lookup_columns: usize, num_ctl_columns: &[usize], config: &StarkConfig, -) -> Vec> +) -> Option>> where F: RichField + Extendable, P: PackedField, C: GenericConfig, S: Stark, { + if stark.quotient_degree_factor() == 0 { + return None; + } + let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; let total_num_helper_cols: usize = num_ctl_columns.iter().sum(); @@ -501,11 +516,13 @@ where }) .collect::>(); - transpose("ient_values) - .into_par_iter() - .map(PolynomialValues::new) - .map(|values| values.coset_ifft(F::coset_shift())) - .collect() + Some( + transpose("ient_values) + .into_par_iter() + .map(PolynomialValues::new) + .map(|values| values.coset_ifft(F::coset_shift())) + .collect(), + ) } /// Check that all constraints evaluate to zero on `H`. diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 9bc62e6b5c..83e39398b3 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -162,18 +162,20 @@ pub fn verify_stark_proof_with_challenges_circuit< // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let mut scale = ReducingFactorTarget::new(zeta_pow_deg); - for (i, chunk) in quotient_polys - .chunks(stark.quotient_degree_factor()) - .enumerate() - { - let recombined_quotient = scale.reduce(chunk, builder); - let computed_vanishing_poly = builder.mul_extension(z_h_zeta, recombined_quotient); - builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly); + if let Some(quotient_polys) = quotient_polys { + for (i, chunk) in quotient_polys + .chunks(stark.quotient_degree_factor()) + .enumerate() + { + let recombined_quotient = scale.reduce(chunk, builder); + let computed_vanishing_poly = builder.mul_extension(z_h_zeta, recombined_quotient); + builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly); + } } let merkle_caps = once(proof.trace_cap.clone()) .chain(proof.auxiliary_polys_cap.clone()) - .chain(once(proof.quotient_polys_cap.clone())) + .chain(proof.quotient_polys_cap.clone()) .collect_vec(); let fri_instance = stark.fri_instance_target( @@ -258,16 +260,22 @@ pub fn add_virtual_stark_proof, S: Stark, con (stark.uses_lookups() || stark.requires_ctls()) .then(|| stark.num_lookup_helper_columns(config) + num_ctl_helper_zs), ) - .chain(once(stark.quotient_degree_factor() * config.num_challenges)) + .chain( + (stark.quotient_degree_factor() > 0) + .then(|| stark.quotient_degree_factor() * config.num_challenges), + ) .collect_vec(); let auxiliary_polys_cap = (stark.uses_lookups() || stark.requires_ctls()) .then(|| builder.add_virtual_cap(cap_height)); + let quotient_polys_cap = + (stark.constraint_degree() > 0).then(|| builder.add_virtual_cap(cap_height)); + StarkProofTarget { trace_cap: builder.add_virtual_cap(cap_height), auxiliary_polys_cap, - quotient_polys_cap: builder.add_virtual_cap(cap_height), + quotient_polys_cap, openings: add_virtual_stark_opening_set::( builder, stark, @@ -302,8 +310,11 @@ fn add_virtual_stark_opening_set, S: Stark, c ctl_zs_first: stark .requires_ctls() .then(|| builder.add_virtual_targets(num_ctl_zs)), - quotient_polys: builder - .add_virtual_extension_targets(stark.quotient_degree_factor() * config.num_challenges), + quotient_polys: (stark.constraint_degree() > 0).then(|| { + builder.add_virtual_extension_targets( + stark.quotient_degree_factor() * config.num_challenges, + ) + }), } } @@ -349,7 +360,11 @@ pub fn set_stark_proof_target, W, const D: usize>( W: Witness, { witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap); - witness.set_cap_target(&proof_target.quotient_polys_cap, &proof.quotient_polys_cap); + if let (Some(quotient_polys_cap_target), Some(quotient_polys_cap)) = + (&proof_target.quotient_polys_cap, &proof.quotient_polys_cap) + { + witness.set_cap_target(quotient_polys_cap_target, quotient_polys_cap); + } witness.set_fri_openings( &proof_target.openings.to_fri_openings(zero), diff --git a/starky/src/stark.rs b/starky/src/stark.rs index 0e2b3bd7b9..c47f969245 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -84,7 +84,10 @@ pub trait Stark, const D: usize>: Sync { /// Outputs the maximum quotient polynomial's degree factor of this [`Stark`]. fn quotient_degree_factor(&self) -> usize { - 1.max(self.constraint_degree() - 1) + match self.constraint_degree().checked_sub(1) { + Some(v) => 1.max(v), + None => 0, + } } /// Outputs the number of quotient polynomials this [`Stark`] would require with @@ -123,11 +126,17 @@ pub trait Stark, const D: usize>: Sync { }; let num_quotient_polys = self.num_quotient_polys(config); - let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); - oracles.push(FriOracleInfo { - num_polys: num_quotient_polys, - blinding: false, - }); + let quotient_info = if num_quotient_polys > 0 { + let quotient_polys = + FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); + oracles.push(FriOracleInfo { + num_polys: num_quotient_polys, + blinding: false, + }); + quotient_polys + } else { + vec![] + }; let zeta_batch = FriBatchInfo { point: zeta, @@ -192,11 +201,17 @@ pub trait Stark, const D: usize>: Sync { }; let num_quotient_polys = self.num_quotient_polys(config); - let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); - oracles.push(FriOracleInfo { - num_polys: num_quotient_polys, - blinding: false, - }); + let quotient_info = if num_quotient_polys > 0 { + let quotient_polys = + FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys); + oracles.push(FriOracleInfo { + num_polys: num_quotient_polys, + blinding: false, + }); + quotient_polys + } else { + vec![] + }; let zeta_batch = FriBatchInfoTarget { point: zeta, diff --git a/starky/src/stark_testing.rs b/starky/src/stark_testing.rs index cc73284490..bbe1c840c9 100644 --- a/starky/src/stark_testing.rs +++ b/starky/src/stark_testing.rs @@ -58,7 +58,7 @@ pub fn test_stark_low_degree, S: Stark, const .collect::>(); let constraint_eval_degree = PolynomialValues::new(constraint_evals).degree(); - let maximum_degree = WITNESS_SIZE * stark.constraint_degree() - 1; + let maximum_degree = (WITNESS_SIZE * stark.constraint_degree()).saturating_sub(1); ensure!( constraint_eval_degree <= maximum_degree, diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs new file mode 100644 index 0000000000..2f93c25556 --- /dev/null +++ b/starky/src/unconstrained_stark.rs @@ -0,0 +1,201 @@ +//! An example of proving and verifying an empty STARK (that is, +//! a proof of knowledge of the trace) + +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; +use core::marker::PhantomData; + +use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::field::packed::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::evaluation_frame::StarkFrame; +use crate::stark::Stark; +use crate::util::trace_rows_to_poly_values; + +/// A trace wirh arbitrary values +#[derive(Copy, Clone)] +struct UnconstrainedStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> UnconstrainedStark { + const fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using two columns of random values + fn generate_trace(&self) -> Vec> { + let trace_rows = (0..self.num_rows) + .map(|_| [F::rand(), F::rand()]) + .collect::>(); + trace_rows_to_poly_values(trace_rows) + } +} + +const COLUMNS: usize = 2; +const PUBLIC_INPUTS: usize = 0; + +impl, const D: usize> Stark for UnconstrainedStark { + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = + StarkFrame, ExtensionTarget, COLUMNS, PUBLIC_INPUTS>; + + fn constraint_degree(&self) -> usize { + 0 + } + + // We don't constrain any register. + fn eval_packed_generic( + &self, + _vars: &Self::EvaluationFrame, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + } + + // We don't constrain any register. + fn eval_ext_circuit( + &self, + _builder: &mut CircuitBuilder, + _vars: &Self::EvaluationFrameTarget, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::extension::Extendable; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; + use plonky2::util::timing::TimingTree; + + use crate::config::StarkConfig; + use crate::proof::StarkProofWithPublicInputs; + use crate::prover::prove; + use crate::recursive_verifier::{ + add_virtual_stark_proof_with_pis, set_stark_proof_with_pis_target, + verify_stark_proof_circuit, + }; + use crate::stark::Stark; + use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; + use crate::unconstrained_stark::UnconstrainedStark; + use crate::verifier::verify_stark_proof; + + #[test] + fn test_unconstrained_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = UnconstrainedStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + + let stark = S::new(num_rows); + let trace = stark.generate_trace(); + let proof = prove::(stark, &config, trace, &[], &mut TimingTree::default())?; + + verify_stark_proof(stark, proof, &config) + } + + #[test] + fn test_unconstrained_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = UnconstrainedStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_low_degree(stark) + } + + #[test] + fn test_unconstrained_stark_circuit() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = UnconstrainedStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_circuit_constraints::(stark) + } + + #[test] + fn test_recursive_stark_verifier() -> Result<()> { + init_logger(); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = UnconstrainedStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + + let stark = S::new(num_rows); + let trace = stark.generate_trace(); + let proof = prove::(stark, &config, trace, &[], &mut TimingTree::default())?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true) + } + + fn recursive_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark + Copy, + InnerC: GenericConfig, + const D: usize, + >( + stark: S, + inner_proof: StarkProofWithPublicInputs, + inner_config: &StarkConfig, + print_gate_counts: bool, + ) -> Result<()> + where + InnerC::Hasher: AlgebraicHasher, + { + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let pt = + add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero()); + + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + + if print_gate_counts { + builder.print_gate_counts(0); + } + + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof) + } + + fn init_logger() { + let _ = env_logger::builder().format_timestamp(None).try_init(); + } +} diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 7959ae0f2e..d56072ad3a 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -164,8 +164,10 @@ where // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each // `quotient_degree_factor`-sized chunk of the original evaluations. + for (i, chunk) in quotient_polys - .chunks(stark.quotient_degree_factor()) + .iter() + .flat_map(|x| x.chunks(stark.quotient_degree_factor())) .enumerate() { ensure!( @@ -176,7 +178,7 @@ where let merkle_caps = once(proof.trace_cap.clone()) .chain(proof.auxiliary_polys_cap.clone()) - .chain(once(proof.quotient_polys_cap.clone())) + .chain(proof.quotient_polys_cap.clone()) .collect_vec(); let num_ctl_zs = ctl_vars @@ -245,11 +247,18 @@ where let cap_height = fri_params.config.cap_height; ensure!(trace_cap.height() == cap_height); - ensure!(quotient_polys_cap.height() == cap_height); + ensure!( + quotient_polys_cap.is_none() + || quotient_polys_cap.as_ref().map(|q| q.height()) == Some(cap_height) + ); ensure!(local_values.len() == S::COLUMNS); ensure!(next_values.len() == S::COLUMNS); - ensure!(quotient_polys.len() == stark.num_quotient_polys(config)); + ensure!(if let Some(quotient_polys) = quotient_polys { + quotient_polys.len() == stark.num_quotient_polys(config) + } else { + stark.num_quotient_polys(config) == 0 + }); check_lookup_options::( stark, From 54cee24b3683ec56551f08edef72470018eb4ba8 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Thu, 21 Mar 2024 14:34:41 -0400 Subject: [PATCH 164/175] Bump versions pre-release (#1557) --- CHANGELOG.md | 5 ++++- field/Cargo.toml | 2 +- plonky2/Cargo.toml | 4 ++-- starky/Cargo.toml | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd901fda49..ca39f03713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## [Unreleased] +## [0.2.2] - 2024-03-21 + +### Changed - Fix CTLs with exactly two looking tables ([#1555](https://github.com/0xPolygonZero/plonky2/pull/1555)) - Make Starks without constraints provable ([#1552](https://github.com/0xPolygonZero/plonky2/pull/1552)) diff --git a/field/Cargo.toml b/field/Cargo.toml index a15e375619..c7af0d42e5 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2_field" description = "Finite field arithmetic" -version = "0.2.0" +version = "0.2.1" authors = ["Daniel Lubarov ", "William Borgeaud ", "Jacqueline Nabaglo ", "Hamish Ivey-Law "] edition.workspace = true license.workspace = true diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 88d108395f..050adcb989 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2" description = "Recursive SNARKs based on PLONK and FRI" -version = "0.2.0" +version = "0.2.1" authors = ["Daniel Lubarov ", "William Borgeaud ", "Nicholas Ward "] readme = "README.md" edition.workspace = true @@ -34,7 +34,7 @@ unroll = { workspace = true } web-time = { version = "1.0.0", optional = true } # Local dependencies -plonky2_field = { version = "0.2.0", path = "../field", default-features = false } +plonky2_field = { version = "0.2.1", path = "../field", default-features = false } plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } plonky2_util = { version = "0.2.0", path = "../util", default-features = false } diff --git a/starky/Cargo.toml b/starky/Cargo.toml index cd7a21a35a..92aaaa1aed 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "starky" description = "Implementation of STARKs" -version = "0.2.1" +version = "0.3.0" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" edition.workspace = true @@ -26,7 +26,7 @@ log = { workspace = true } num-bigint = { version = "0.4.3", default-features = false } # Local dependencies -plonky2 = { version = "0.2.0", path = "../plonky2", default-features = false } +plonky2 = { version = "0.2.1", path = "../plonky2", default-features = false } plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } plonky2_util = { version = "0.2.0", path = "../util", default-features = false } From 3e89c7c24ce0e01131c4af1379190dd0357158b8 Mon Sep 17 00:00:00 2001 From: Bob Niu Date: Fri, 22 Mar 2024 16:22:05 +0800 Subject: [PATCH 165/175] refactor code --- field/src/fft.rs | 6 +----- starky/src/prover.rs | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/field/src/fft.rs b/field/src/fft.rs index 0a0b5dd8ef..5fe29b31e0 100644 --- a/field/src/fft.rs +++ b/field/src/fft.rs @@ -38,11 +38,7 @@ fn fft_dispatch( zero_factor: Option, root_table: Option<&FftRootTable>, ) { - let computed_root_table = if root_table.is_some() { - None - } else { - Some(fft_root_table(input.len())) - }; + let computed_root_table = root_table.is_none().then(|| fft_root_table(input.len())); let used_root_table = root_table.or(computed_root_table.as_ref()).unwrap(); fft_classic(input, zero_factor.unwrap_or(0), used_root_table); diff --git a/starky/src/prover.rs b/starky/src/prover.rs index c7b77b9336..29b72521b0 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -126,8 +126,6 @@ where ); // Permutation arguments. - - let constraint_degree = stark.constraint_degree(); let lookup_challenges = stark.uses_lookups().then(|| { if let Some(c) = ctl_challenges { c.challenges.iter().map(|ch| ch.beta).collect::>() From 240839e8a60ddc4648f7e1b02e219e39aded6e01 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 22 Mar 2024 15:28:56 +0000 Subject: [PATCH 166/175] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca39f03713..a324d95d2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Code refactoring ([#1558](https://github.com/0xPolygonZero/plonky2/pull/1558)) ## [0.2.2] - 2024-03-21 From 692a9e35d0acbd76ac3c2f7d7adb2fcfd52bc161 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Sun, 7 Apr 2024 01:42:04 -0400 Subject: [PATCH 167/175] Add stdarch_x86_avx512 feature --- field/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/field/src/lib.rs b/field/src/lib.rs index 1531a47db2..e21f042428 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -4,6 +4,7 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(missing_debug_implementations)] #![feature(specialization)] +#![cfg_attr(target_arch = "x86_64", feature(stdarch_x86_avx512))] #![cfg_attr(not(test), no_std)] #![cfg(not(test))] extern crate alloc; From 0731fec251bb8c5618e342a4b8342d638b217cd0 Mon Sep 17 00:00:00 2001 From: Matthias Goergens Date: Mon, 15 Apr 2024 17:02:48 +0800 Subject: [PATCH 168/175] Simplify types: remove option from CTL filters CTL filters can already express perfectly well the behaviour that we gave for `None`. No need to complicated anything. Plus some random lint fixes that clippy demanded.. --- plonky2/src/gates/lookup.rs | 1 - plonky2/src/gates/lookup_table.rs | 1 - plonky2/src/plonk/copy_constraint.rs | 1 + starky/src/cross_table_lookup.rs | 58 ++++++----------------- starky/src/lookup.rs | 69 ++++++++++------------------ starky/src/permutation_stark.rs | 2 +- 6 files changed, 40 insertions(+), 92 deletions(-) diff --git a/plonky2/src/gates/lookup.rs b/plonky2/src/gates/lookup.rs index 23a0fd8742..498221708e 100644 --- a/plonky2/src/gates/lookup.rs +++ b/plonky2/src/gates/lookup.rs @@ -5,7 +5,6 @@ use alloc::{ vec, vec::Vec, }; -use core::usize; use itertools::Itertools; use keccak_hash::keccak; diff --git a/plonky2/src/gates/lookup_table.rs b/plonky2/src/gates/lookup_table.rs index 9a4d08c83b..28f47c4d8a 100644 --- a/plonky2/src/gates/lookup_table.rs +++ b/plonky2/src/gates/lookup_table.rs @@ -6,7 +6,6 @@ use alloc::{ vec, vec::Vec, }; -use core::usize; #[cfg(feature = "std")] use std::sync::Arc; diff --git a/plonky2/src/plonk/copy_constraint.rs b/plonky2/src/plonk/copy_constraint.rs index 309f207d8b..58fb1e89e0 100644 --- a/plonky2/src/plonk/copy_constraint.rs +++ b/plonky2/src/plonk/copy_constraint.rs @@ -7,6 +7,7 @@ use crate::iop::target::Target; #[derive(Debug)] pub struct CopyConstraint { pub pair: (Target, Target), + #[allow(dead_code)] pub name: String, } diff --git a/starky/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs index da50c24c17..d0b020a6a0 100644 --- a/starky/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -67,12 +67,12 @@ pub type TableIdx = usize; pub struct TableWithColumns { table: TableIdx, columns: Vec>, - filter: Option>, + filter: Filter, } impl TableWithColumns { /// Generates a new `TableWithColumns` given a `table` index, a linear combination of columns `columns` and a `filter`. - pub fn new(table: TableIdx, columns: Vec>, filter: Option>) -> Self { + pub fn new(table: TableIdx, columns: Vec>, filter: Filter) -> Self { Self { table, columns, @@ -163,7 +163,7 @@ pub struct CtlZData<'a, F: Field> { pub(crate) columns: Vec<&'a [Column]>, /// Vector of filter columns for the current table. /// Each filter evaluates to either 1 or 0. - pub(crate) filter: Vec>>, + pub(crate) filter: Vec>, } impl<'a, F: Field> CtlZData<'a, F> { @@ -173,7 +173,7 @@ impl<'a, F: Field> CtlZData<'a, F> { z: PolynomialValues, challenge: GrandProductChallenge, columns: Vec<&'a [Column]>, - filter: Vec>>, + filter: Vec>, ) -> Self { Self { helper_columns, @@ -404,7 +404,7 @@ fn ctl_helper_zs_cols( .map(|(table, group)| { let columns_filters = group .map(|table| (&table.columns[..], &table.filter)) - .collect::], &Option>)>>(); + .collect::], &Filter)>>(); ( table, partial_sums( @@ -484,7 +484,7 @@ where /// Column linear combinations of the `CrossTableLookup`s. pub(crate) columns: Vec<&'a [Column]>, /// Filter that evaluates to either 1 or 0. - pub(crate) filter: Vec>>, + pub(crate) filter: Vec>, } impl<'a, F: RichField + Extendable, const D: usize> @@ -682,16 +682,8 @@ pub(crate) fn eval_cross_table_lookup_checks { /// Column linear combinations of the `CrossTableLookup`s. pub(crate) columns: Vec>>, /// Filter that evaluates to either 1 or 0. - pub(crate) filter: Vec>>, + pub(crate) filter: Vec>, } impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { @@ -856,8 +844,6 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< let local_values = vars.get_local_values(); let next_values = vars.get_next_values(); - let one = builder.one_extension(); - for lookup_vars in ctl_vars { let CtlCheckVarsTarget { helper_columns, @@ -906,16 +892,8 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< let combin0 = challenges.combine_circuit(builder, &evals[0]); let combin1 = challenges.combine_circuit(builder, &evals[1]); - let f0 = if let Some(filter0) = &filter[0] { - filter0.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let f1 = if let Some(filter1) = &filter[1] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; + let f0 = filter[0].eval_filter_circuit(builder, local_values, next_values); + let f1 = filter[1].eval_filter_circuit(builder, local_values, next_values); let combined = builder.mul_sub_extension(combin1, *local_z, f1); let combined = builder.mul_extension(combined, combin0); @@ -928,11 +906,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< consumer.constraint_last_row(builder, constr); } else { let combin0 = challenges.combine_circuit(builder, &evals[0]); - let f0 = if let Some(filter0) = &filter[0] { - filter0.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; + let f0 = filter[0].eval_filter_circuit(builder, local_values, next_values); let constr = builder.mul_sub_extension(combin0, *local_z, f0); consumer.constraint_last_row(builder, constr); @@ -1121,11 +1095,7 @@ pub mod debug_utils { ) { let trace = &trace_poly_values[table.table]; for i in 0..trace[0].len() { - let filter = if let Some(combin) = &table.filter { - combin.eval_table(trace, i) - } else { - F::ONE - }; + let filter = table.filter.eval_table(trace, i); if filter.is_one() { let row = table .columns diff --git a/starky/src/lookup.rs b/starky/src/lookup.rs index 16383cd690..f63c4bd76c 100644 --- a/starky/src/lookup.rs +++ b/starky/src/lookup.rs @@ -39,6 +39,16 @@ pub struct Filter { constants: Vec>, } +/// The default filter is always on. +impl Default for Filter { + fn default() -> Self { + Self { + products: vec![], + constants: vec![Column::constant(F::ONE)], + } + } +} + impl Filter { /// Returns a filter from the provided `products` and `constants` vectors. pub fn new(products: Vec<(Column, Column)>, constants: Vec>) -> Self { @@ -396,7 +406,7 @@ impl Column { } } -pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Option>); +pub(crate) type ColumnFilter<'a, F> = (&'a [Column], &'a Filter); /// A [`Lookup`] defines a set of `columns`` whose values should appear in a /// `table_column` (i.e. the lookup table associated to these looking columns), @@ -423,7 +433,7 @@ pub struct Lookup { /// Columns to filter some elements. There is at most one filter /// column per column to lookup. - pub filter_columns: Vec>>, + pub filter_columns: Vec>, } impl Lookup { @@ -650,7 +660,7 @@ pub(crate) fn lookup_helper_columns( /// Given data associated to a lookup, check the associated helper polynomials. pub(crate) fn eval_helper_columns( - filter: &[Option>], + filter: &[Filter], columns: &[Vec

], local_values: &[P], next_values: &[P], @@ -674,26 +684,14 @@ pub(crate) fn eval_helper_columns( let combin0 = challenges.combine(&chunk[0]); let combin1 = challenges.combine(chunk[1].iter()); - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter(local_values, next_values) - } else { - P::ONES - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; + let f0 = fs[0].eval_filter(local_values, next_values); + let f1 = fs[1].eval_filter(local_values, next_values); consumer.constraint(combin1 * combin0 * h - f0 * combin1 - f1 * combin0); } 1 => { let combin = challenges.combine(&chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter(local_values, next_values) - } else { - P::ONES - }; + let f0 = fs[0].eval_filter(local_values, next_values); consumer.constraint(combin * h - f0); } @@ -707,7 +705,7 @@ pub(crate) fn eval_helper_columns( /// Given data associated to a lookup (either a CTL or a range-check), check the associated helper polynomials. pub(crate) fn eval_helper_columns_circuit, const D: usize>( builder: &mut CircuitBuilder, - filter: &[Option>], + filter: &[Filter], columns: &[Vec>], local_values: &[ExtensionTarget], next_values: &[ExtensionTarget], @@ -722,22 +720,13 @@ pub(crate) fn eval_helper_columns_circuit, const D: .chunks(chunk_size) .zip(filter.chunks(chunk_size).zip(helper_columns)) { - let one = builder.one_extension(); match chunk.len() { 2 => { let combin0 = challenges.combine_circuit(builder, &chunk[0]); let combin1 = challenges.combine_circuit(builder, &chunk[1]); - let f0 = if let Some(filter0) = &fs[0] { - filter0.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; - let f1 = if let Some(filter1) = &fs[1] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; + let f0 = fs[0].eval_filter_circuit(builder, local_values, next_values); + let f1 = fs[1].eval_filter_circuit(builder, local_values, next_values); let constr = builder.mul_sub_extension(combin0, h, f0); let constr = builder.mul_extension(constr, combin1); @@ -748,11 +737,7 @@ pub(crate) fn eval_helper_columns_circuit, const D: } 1 => { let combin = challenges.combine_circuit(builder, &chunk[0]); - let f0 = if let Some(filter1) = &fs[0] { - filter1.eval_filter_circuit(builder, local_values, next_values) - } else { - one - }; + let f0 = fs[0].eval_filter_circuit(builder, local_values, next_values); let constr = builder.mul_sub_extension(combin, h, f0); consumer.constraint(builder, constr); } @@ -788,13 +773,10 @@ pub(crate) fn get_helper_cols( let mut filter_col = Vec::with_capacity(degree); let first_combined = (0..degree) .map(|d| { - let f = if let Some(filter) = first_filter { - let f = filter.eval_table(trace, d); + let f = { + let f = first_filter.eval_table(trace, d); filter_col.push(f); f - } else { - filter_col.push(F::ONE); - F::ONE }; if f.is_one() { let evals = first_col @@ -821,13 +803,10 @@ pub(crate) fn get_helper_cols( let mut filter_col = Vec::with_capacity(degree); let mut combined = (0..degree) .map(|d| { - let f = if let Some(filter) = filt { - let f = filter.eval_table(trace, d); + let f = { + let f = filt.eval_table(trace, d); filter_col.push(f); f - } else { - filter_col.push(F::ONE); - F::ONE }; if f.is_one() { let evals = col diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index bddf420279..62290b658d 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -72,7 +72,7 @@ impl, const D: usize> Stark for PermutationSt columns: vec![Column::single(0)], table_column: Column::single(1), frequencies_column: Column::single(2), - filter_columns: vec![None; 1], + filter_columns: vec![Default::default()], }] } From fefe94d1741c5efb5adf39378023c56a3499ca0f Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 15 Apr 2024 20:33:07 +0100 Subject: [PATCH 169/175] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a324d95d2b..33385f305d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Code refactoring ([#1558](https://github.com/0xPolygonZero/plonky2/pull/1558)) +- Simplify types: remove option from CTL filters ([#1567](https://github.com/0xPolygonZero/plonky2/pull/1567)) ## [0.2.2] - 2024-03-21 From c386a63902e870f387cfc563c1eaaf487198669d Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 15 Apr 2024 20:40:23 +0100 Subject: [PATCH 170/175] One more --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33385f305d..cba9486feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Code refactoring ([#1558](https://github.com/0xPolygonZero/plonky2/pull/1558)) - Simplify types: remove option from CTL filters ([#1567](https://github.com/0xPolygonZero/plonky2/pull/1567)) +- Add stdarch_x86_avx512 feature ([#1566](https://github.com/0xPolygonZero/plonky2/pull/1566)) ## [0.2.2] - 2024-03-21 From 76da1383384a99691506b3904dd8c2ddfd057555 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:10:19 +0900 Subject: [PATCH 171/175] Bump versions pre-release (#1569) --- CHANGELOG.md | 3 +++ field/Cargo.toml | 2 +- plonky2/Cargo.toml | 4 ++-- starky/Cargo.toml | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cba9486feb..79a203f8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [0.2.3] - 2024-04-16 + - Code refactoring ([#1558](https://github.com/0xPolygonZero/plonky2/pull/1558)) - Simplify types: remove option from CTL filters ([#1567](https://github.com/0xPolygonZero/plonky2/pull/1567)) - Add stdarch_x86_avx512 feature ([#1566](https://github.com/0xPolygonZero/plonky2/pull/1566)) diff --git a/field/Cargo.toml b/field/Cargo.toml index c7af0d42e5..7172031818 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2_field" description = "Finite field arithmetic" -version = "0.2.1" +version = "0.2.2" authors = ["Daniel Lubarov ", "William Borgeaud ", "Jacqueline Nabaglo ", "Hamish Ivey-Law "] edition.workspace = true license.workspace = true diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 050adcb989..59ae921243 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plonky2" description = "Recursive SNARKs based on PLONK and FRI" -version = "0.2.1" +version = "0.2.2" authors = ["Daniel Lubarov ", "William Borgeaud ", "Nicholas Ward "] readme = "README.md" edition.workspace = true @@ -34,7 +34,7 @@ unroll = { workspace = true } web-time = { version = "1.0.0", optional = true } # Local dependencies -plonky2_field = { version = "0.2.1", path = "../field", default-features = false } +plonky2_field = { version = "0.2.2", path = "../field", default-features = false } plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } plonky2_util = { version = "0.2.0", path = "../util", default-features = false } diff --git a/starky/Cargo.toml b/starky/Cargo.toml index 92aaaa1aed..33d90d5b4d 100644 --- a/starky/Cargo.toml +++ b/starky/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "starky" description = "Implementation of STARKs" -version = "0.3.0" +version = "0.4.0" authors = ["Daniel Lubarov ", "William Borgeaud "] readme = "README.md" edition.workspace = true @@ -26,7 +26,7 @@ log = { workspace = true } num-bigint = { version = "0.4.3", default-features = false } # Local dependencies -plonky2 = { version = "0.2.1", path = "../plonky2", default-features = false } +plonky2 = { version = "0.2.2", path = "../plonky2", default-features = false } plonky2_maybe_rayon = { version = "0.2.0", path = "../maybe_rayon", default-features = false } plonky2_util = { version = "0.2.0", path = "../util", default-features = false } From ff04ad3156c57a78cfb78a6cca25e2922a59a6f5 Mon Sep 17 00:00:00 2001 From: nikkolasg Date: Thu, 16 May 2024 13:39:42 +0200 Subject: [PATCH 172/175] using right vec --- plonky2/src/util/serialization/gate_serialization.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index f7880f8365..f45962d62b 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -1,9 +1,9 @@ //! A module to help with GateRef serialization #[cfg(not(feature = "std"))] -use alloc::vec::Vec; +type Vec = alloc::vec::Vec; #[cfg(feature = "std")] -use std::vec::Vec; // For macros below +type Vec = std::vec::Vec; // For macros below use plonky2_field::extension::Extendable; @@ -79,7 +79,7 @@ macro_rules! impl_gate_serializer { fn write_gate( &self, - buf: &mut $crate::util::serialization::gate_serialization::Vec, + buf: &mut Vec, gate: &$crate::gates::gate::GateRef, common: &$crate::plonk::circuit_data::CommonCircuitData, ) -> $crate::util::serialization::IoResult<()> { From 3e11b949679ffb2e69cbf2c7c917891b81f46c33 Mon Sep 17 00:00:00 2001 From: nicholas-mainardi Date: Thu, 11 Jul 2024 13:21:30 +0200 Subject: [PATCH 173/175] Fix EqualityGenerator when 0 constant as left operand --- plonky2/src/gadgets/arithmetic.rs | 54 +++++++++++++++++++------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index fd2442f933..8e281df8c7 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -357,31 +357,41 @@ impl, const D: usize> CircuitBuilder { let maybe_x = self.mul(b.target, x); self.mul_add(not_b.target, y, maybe_x) } - + /// Utility function employed to compute both `is_equal` and `is_not_equal` at the same time - pub(crate) fn is_equal_and_not_equal(&mut self, x: Target, y: Target) -> (BoolTarget, BoolTarget) { + pub(crate) fn is_equal_and_not_equal( + &mut self, + x: Target, + y: Target, + ) -> (BoolTarget, BoolTarget) { let zero = self.zero(); let _true = self._true(); let _false = self._false(); + // check if one of the operand is the zero constant, then we can save + // one arithmetic operation + let (x, y, diff) = match (self.target_as_constant(x), self.target_as_constant(y)) { + (Some(a), Some(b)) => { + if a == b { + return (_true, _false); + } else { + return (_false, _true); + } + } + (Some(field_zero), None) if field_zero == F::ZERO => + // swap targets to ensure that diff computed by `EqualityGenerator` is equal to y + { + (y, x, y) + } + (None, Some(field_zero)) if field_zero == F::ZERO => (x, y, x), + (_, _) => (x, y, self.sub(x, y)), + }; + let equal = self.add_virtual_bool_target_unsafe(); let not_equal = self.not(equal); let inv = self.add_virtual_target(); self.add_simple_generator(EqualityGenerator { x, y, equal, inv }); - // check if one of the operand is the zero constant, then we can save - // one arithmetic operation - let diff = match (self.target_as_constant(x), self.target_as_constant(y)) { - (Some(a), Some(b)) => if a == b { - return (_true, _false) - } else { - return (_false, _true) - }, - (Some(field_zero), None) if field_zero == F::ZERO => y, - (None, Some(field_zero)) if field_zero == F::ZERO => x, - (_, _) => self.sub(x,y) - }; - let not_equal_check = self.mul(equal.target, diff); let equal_check = self.mul_sub(diff, inv, not_equal.target); @@ -454,12 +464,15 @@ pub(crate) struct BaseArithmeticOperation { addend: Target, } - #[cfg(test)] mod tests { - use plonky2_field::{goldilocks_field::GoldilocksField, types::{Field, Sample}}; + use plonky2_field::goldilocks_field::GoldilocksField; + use plonky2_field::types::{Field, Sample}; - use crate::{iop::witness::{PartialWitness, WitnessWrite}, plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig}}; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::PoseidonGoldilocksConfig; type F = GoldilocksField; const D: usize = 2; @@ -470,6 +483,8 @@ mod tests { let y = builder.add_virtual_target(); let zero = builder.zero(); let is_zero = builder.is_equal(x, zero); + let is_zero_swap = builder.is_equal(zero, x); + builder.connect(is_zero.target, is_zero_swap.target); let is_eq = builder.is_equal(x, y); let is_not_eq = builder.is_not_equal(x, y); builder.register_public_input(is_eq.target); @@ -491,8 +506,5 @@ mod tests { assert_eq!(proof.public_inputs[1], F::ONE); assert_eq!(proof.public_inputs[0], F::ZERO); } - - - } } From 3c4cc3749683a82477dcfcea19d423fe844c3ed6 Mon Sep 17 00:00:00 2001 From: nicholas-mainardi Date: Mon, 19 Aug 2024 18:43:25 +0200 Subject: [PATCH 174/175] Introduce poseidon2 (#8) * Introduce poseidon2 crate * Make Poseidon2 generator public * Derive default for Poseidon2 Generator * Fix build on aarch64 * Fix `aarch64` build. (#9) --------- Co-authored-by: Steven --- Cargo.toml | 2 +- plonky2/src/hash/arch/aarch64/mod.rs | 2 +- .../arch/aarch64/poseidon_goldilocks_neon.rs | 2 +- plonky2/src/hash/arch/mod.rs | 2 +- plonky2/src/hash/mod.rs | 2 +- poseidon2_plonky2/Cargo.toml | 40 + poseidon2_plonky2/LICENSE-APACHE | 202 +++++ poseidon2_plonky2/LICENSE-MIT | 21 + poseidon2_plonky2/benches/allocator/mod.rs | 6 + poseidon2_plonky2/benches/base_proof.rs | 79 ++ poseidon2_plonky2/benches/circuits/mod.rs | 105 +++ poseidon2_plonky2/benches/hashing.rs | 80 ++ poseidon2_plonky2/benches/merkle.rs | 44 + poseidon2_plonky2/benches/recursion.rs | 247 ++++++ poseidon2_plonky2/src/lib.rs | 15 + poseidon2_plonky2/src/poseidon2_gate.rs | 604 +++++++++++++ poseidon2_plonky2/src/poseidon2_goldilock.rs | 170 ++++ poseidon2_plonky2/src/poseidon2_hash.rs | 802 ++++++++++++++++++ 18 files changed, 2420 insertions(+), 5 deletions(-) create mode 100644 poseidon2_plonky2/Cargo.toml create mode 100644 poseidon2_plonky2/LICENSE-APACHE create mode 100644 poseidon2_plonky2/LICENSE-MIT create mode 100644 poseidon2_plonky2/benches/allocator/mod.rs create mode 100644 poseidon2_plonky2/benches/base_proof.rs create mode 100644 poseidon2_plonky2/benches/circuits/mod.rs create mode 100644 poseidon2_plonky2/benches/hashing.rs create mode 100644 poseidon2_plonky2/benches/merkle.rs create mode 100644 poseidon2_plonky2/benches/recursion.rs create mode 100644 poseidon2_plonky2/src/lib.rs create mode 100644 poseidon2_plonky2/src/poseidon2_gate.rs create mode 100644 poseidon2_plonky2/src/poseidon2_goldilock.rs create mode 100644 poseidon2_plonky2/src/poseidon2_hash.rs diff --git a/Cargo.toml b/Cargo.toml index 6b9de8db33..4d686e29b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["field", "maybe_rayon", "plonky2", "starky", "util"] +members = ["field", "maybe_rayon", "plonky2", "poseidon2_plonky2", "starky", "util"] resolver = "2" [workspace.dependencies] diff --git a/plonky2/src/hash/arch/aarch64/mod.rs b/plonky2/src/hash/arch/aarch64/mod.rs index b8ae14afb2..549acebe2b 100644 --- a/plonky2/src/hash/arch/aarch64/mod.rs +++ b/plonky2/src/hash/arch/aarch64/mod.rs @@ -1,2 +1,2 @@ #[cfg(target_feature = "neon")] -pub(crate) mod poseidon_goldilocks_neon; +pub mod poseidon_goldilocks_neon; diff --git a/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs b/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs index 4b1d8dfb8d..0baa88da3a 100644 --- a/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs +++ b/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs @@ -174,7 +174,7 @@ unsafe fn const_layer_full( /// Full S-box. #[inline(always)] #[unroll_for_loops] -unsafe fn sbox_layer_full(state: [u64; WIDTH]) -> [u64; WIDTH] { +pub unsafe fn sbox_layer_full(state: [u64; WIDTH]) -> [u64; WIDTH] { // This is done in scalar. S-boxes in vector are only slightly slower throughput-wise but have // an insane latency (~100 cycles) on the M1. diff --git a/plonky2/src/hash/arch/mod.rs b/plonky2/src/hash/arch/mod.rs index 1de23f671f..9d25d92455 100644 --- a/plonky2/src/hash/arch/mod.rs +++ b/plonky2/src/hash/arch/mod.rs @@ -2,4 +2,4 @@ pub(crate) mod x86_64; #[cfg(target_arch = "aarch64")] -pub(crate) mod aarch64; +pub mod aarch64; diff --git a/plonky2/src/hash/mod.rs b/plonky2/src/hash/mod.rs index c98c57069c..6ff1303a96 100644 --- a/plonky2/src/hash/mod.rs +++ b/plonky2/src/hash/mod.rs @@ -1,7 +1,7 @@ //! plonky2 hashing logic for in-circuit hashing and Merkle proof verification //! as well as specific hash functions implementation. -mod arch; +pub mod arch; pub mod hash_types; pub mod hashing; pub mod keccak; diff --git a/poseidon2_plonky2/Cargo.toml b/poseidon2_plonky2/Cargo.toml new file mode 100644 index 0000000000..814bfb02f6 --- /dev/null +++ b/poseidon2_plonky2/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "poseidon2_plonky2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.69" +plonky2 = { path = "../plonky2" } +rstest = "0.17.0" +serial_test = "1.0.0" +unroll = "0.1.5" + +[dev-dependencies] +criterion = { version = "0.4.0", default-features = false } +env_logger = "0.10.0" +log = "0.4.17" +rand_chacha = "0.3.1" +serde_cbor = "0.11.2" +tynm = { version = "0.1.6", default-features = false } + +[target.'cfg(not(target_env = "msvc"))'.dev-dependencies] +jemallocator = "0.5.0" + +[[bench]] +name = "hashing" +harness = false + +[[bench]] +name = "merkle" +harness = false + +[[bench]] +name = "base_proof" +harness = false + +[[bench]] +name = "recursion" +harness = false diff --git a/poseidon2_plonky2/LICENSE-APACHE b/poseidon2_plonky2/LICENSE-APACHE new file mode 100644 index 0000000000..1e5006dc14 --- /dev/null +++ b/poseidon2_plonky2/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/poseidon2_plonky2/LICENSE-MIT b/poseidon2_plonky2/LICENSE-MIT new file mode 100644 index 0000000000..86d690b220 --- /dev/null +++ b/poseidon2_plonky2/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 The Plonky2 Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/poseidon2_plonky2/benches/allocator/mod.rs b/poseidon2_plonky2/benches/allocator/mod.rs new file mode 100644 index 0000000000..34b518f665 --- /dev/null +++ b/poseidon2_plonky2/benches/allocator/mod.rs @@ -0,0 +1,6 @@ +#[cfg(not(target_env = "msvc"))] +use jemallocator::Jemalloc; + +#[cfg(not(target_env = "msvc"))] +#[global_allocator] +static GLOBAL: Jemalloc = Jemalloc; diff --git a/poseidon2_plonky2/benches/base_proof.rs b/poseidon2_plonky2/benches/base_proof.rs new file mode 100644 index 0000000000..909ecde8a9 --- /dev/null +++ b/poseidon2_plonky2/benches/base_proof.rs @@ -0,0 +1,79 @@ +#![feature(generic_const_exprs)] + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use plonky2::field::extension::Extendable; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::hash::hash_types::RichField; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::plonk::circuit_data::CircuitConfig; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig}; +use poseidon2_plonky2::poseidon2_goldilock::Poseidon2GoldilocksConfig; +use poseidon2_plonky2::poseidon2_hash::Poseidon2Hash; +use tynm::type_name; + +use crate::circuits::BaseCircuit; + +mod circuits; + +fn bench_base_proof< + F: RichField + Extendable, + const D: usize, + C: GenericConfig, + H: Hasher + AlgebraicHasher, +>( + c: &mut Criterion, +) { + let mut group = c.benchmark_group(&format!( + "base-proof<{}, {}>", + type_name::(), + type_name::() + )); + + let config = CircuitConfig::standard_recursion_config(); + for degree in [12, 14, 16] { + group.bench_function( + format!("build circuit for degree {}", degree).as_str(), + |b| { + b.iter_with_large_drop(|| { + BaseCircuit::::build_base_circuit(config.clone(), degree); + }) + }, + ); + + let base_circuit = BaseCircuit::::build_base_circuit(config.clone(), degree); + + group.bench_function(format!("prove for degree {}", degree).as_str(), |b| { + b.iter_batched( + || F::rand(), + |init| base_circuit.generate_base_proof(init).unwrap(), + BatchSize::PerIteration, + ) + }); + + let proof = base_circuit.generate_base_proof(F::rand()).unwrap(); + + group.bench_function(format!("verify for degree {}", degree).as_str(), |b| { + b.iter_batched( + || (base_circuit.get_circuit_data(), proof.clone()), + |(data, proof)| data.verify(proof).unwrap(), + BatchSize::PerIteration, + ) + }); + } + + group.finish(); +} + +fn benchmark(c: &mut Criterion) { + const D: usize = 2; + type F = GoldilocksField; + bench_base_proof::(c); + bench_base_proof::(c); + bench_base_proof::(c); + bench_base_proof::(c); +} + +criterion_group!(name = benches; + config = Criterion::default().sample_size(10); + targets = benchmark); +criterion_main!(benches); diff --git a/poseidon2_plonky2/benches/circuits/mod.rs b/poseidon2_plonky2/benches/circuits/mod.rs new file mode 100644 index 0000000000..6cb99a7648 --- /dev/null +++ b/poseidon2_plonky2/benches/circuits/mod.rs @@ -0,0 +1,105 @@ +use std::marker::PhantomData; + +use anyhow::Result; +use plonky2::field::extension::Extendable; +use plonky2::gates::arithmetic_base::ArithmeticGate; +use plonky2::hash::hash_types::RichField; +use plonky2::hash::hashing::hash_n_to_m_no_pad; +use plonky2::iop::target::Target; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::plonk::proof::ProofWithPublicInputs; + +/// Data structure with all input/output targets and the `CircuitData` for the circuit proven +/// in base proofs. The circuit is designed to be representative of a common base circuit +/// operating on a common public state employing also some private data. +/// The computation performed on the state was chosen to employ commonly used gates, such as +/// arithmetic and hash ones +pub struct BaseCircuit< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + H: Hasher + AlgebraicHasher, +> { + private_input: Target, + public_input: Target, + public_output: Target, + circuit_data: CircuitData, + num_powers: usize, + _hasher: PhantomData, +} + +impl< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + H: Hasher + AlgebraicHasher, + > BaseCircuit +{ + pub fn build_base_circuit(config: CircuitConfig, degree: usize) -> Self { + let num_gates: usize = 1usize << degree; + let num_ops = ArithmeticGate::new_from_config(&config).num_ops; + // the number of gates in the circuit depending on `powers` is + // `N = powers + 1 + ceil((2 + powers)/num_ops) + 3`. + // Thus, to obtain a circuit with N <= num_gates gates, we set `powers` as follows + let powers = ((num_gates - 5) * num_ops - 2) / (num_ops + 1); + + let mut builder = CircuitBuilder::::new(config); + let mut res_t = builder.add_virtual_public_input(); + let init_t = res_t; + let zero = builder.zero(); + let to_be_hashed_t = builder.add_virtual_target(); + for _ in 0..powers { + res_t = builder.mul(res_t, init_t); + res_t = builder.hash_n_to_m_no_pad::(vec![res_t, to_be_hashed_t, zero, zero], 1)[0]; + } + + let out_t = builder.add_virtual_public_input(); + let is_eq_t = builder.is_equal(out_t, res_t); + builder.assert_one(is_eq_t.target); + + let data = builder.build::(); + + assert_eq!(data.common.degree_bits(), degree); + + Self { + private_input: to_be_hashed_t, + public_input: init_t, + public_output: out_t, + circuit_data: data, + num_powers: powers, + _hasher: PhantomData::, + } + } + + pub fn generate_base_proof(&self, init: F) -> Result> { + let mut pw = PartialWitness::::new(); + + pw.set_target(self.public_input, init); + let to_be_hashed = F::rand(); + pw.set_target(self.private_input, to_be_hashed); + let mut res = init; + for _ in 0..self.num_powers { + res = res.mul(init); + res = + hash_n_to_m_no_pad::<_, H::Permutation>(&[res, to_be_hashed, F::ZERO, F::ZERO], 1) + [0]; + } + + pw.set_target(self.public_output, res); + + let proof = self.circuit_data.prove(pw)?; + + self.circuit_data.verify(proof.clone())?; + + assert_eq!(proof.public_inputs[1], res); + + Ok(proof) + } + + pub fn get_circuit_data(&self) -> &CircuitData { + &self.circuit_data + } +} diff --git a/poseidon2_plonky2/benches/hashing.rs b/poseidon2_plonky2/benches/hashing.rs new file mode 100644 index 0000000000..2107b60507 --- /dev/null +++ b/poseidon2_plonky2/benches/hashing.rs @@ -0,0 +1,80 @@ +mod allocator; + +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Sample; +use plonky2::hash::hash_types::{BytesHash, RichField}; +use plonky2::hash::keccak::KeccakHash; +use plonky2::hash::poseidon::{Poseidon, SPONGE_WIDTH}; +use plonky2::plonk::config::Hasher; +use poseidon2_plonky2::poseidon2_hash::{Poseidon2, WIDTH}; +use rand_chacha::rand_core::SeedableRng; +use rand_chacha::ChaCha12Rng; +use tynm::type_name; + +pub(crate) fn bench_keccak(c: &mut Criterion) { + let mut rng = ChaCha12Rng::seed_from_u64(38u64); + c.bench_function("keccak256", |b| { + b.iter_batched( + || { + ( + BytesHash::<32>::sample(&mut rng), + BytesHash::<32>::sample(&mut rng), + ) + }, + |(left, right)| as Hasher>::two_to_one(left, right), + BatchSize::SmallInput, + ) + }); +} + +pub(crate) fn bench_poseidon(c: &mut Criterion) { + let mut rng = ChaCha12Rng::seed_from_u64(42u64); + c.bench_function( + &format!("poseidon<{}, {}>", type_name::(), SPONGE_WIDTH), + |b| { + b.iter_batched( + || { + (0..SPONGE_WIDTH) + .map(|_| F::sample(&mut rng)) + .collect::>() + .try_into() + .unwrap() + }, + |state| F::poseidon(state), + BatchSize::SmallInput, + ) + }, + ); +} + +pub(crate) fn bench_poseidon_2(c: &mut Criterion) { + let mut rng = ChaCha12Rng::seed_from_u64(42u64); + c.bench_function( + &format!("poseidon2<{}, {}>", type_name::(), WIDTH), + |b| { + b.iter_batched( + || { + (0..WIDTH) + .map(|_| F::sample(&mut rng)) + .collect::>() + .try_into() + .unwrap() + }, + |state| F::poseidon2(state), + BatchSize::SmallInput, + ) + }, + ); +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_poseidon::(c); + bench_poseidon_2::(c); + bench_keccak::(c); +} + +criterion_group!(name = benches; + config = Criterion::default().sample_size(500); + targets = criterion_benchmark); +criterion_main!(benches); diff --git a/poseidon2_plonky2/benches/merkle.rs b/poseidon2_plonky2/benches/merkle.rs new file mode 100644 index 0000000000..5118c6a944 --- /dev/null +++ b/poseidon2_plonky2/benches/merkle.rs @@ -0,0 +1,44 @@ +#![feature(generic_const_exprs)] + +mod allocator; + +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::hash::hash_types::RichField; +use plonky2::hash::keccak::KeccakHash; +use plonky2::hash::merkle_tree::MerkleTree; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::plonk::config::Hasher; +use poseidon2_plonky2::poseidon2_hash::Poseidon2Hash; +use tynm::type_name; + +const ELEMS_PER_LEAF: usize = 135; + +pub(crate) fn bench_merkle_tree>(c: &mut Criterion) { + let mut group = c.benchmark_group(&format!( + "merkle-tree<{}, {}>", + type_name::(), + type_name::() + )); + group.sample_size(30); + + for size_log in [13, 14, 15] { + let size = 1 << size_log; + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| { + b.iter_batched( + || vec![F::rand_vec(ELEMS_PER_LEAF); size], + |leaves| MerkleTree::::new(leaves, 0), + BatchSize::SmallInput, + ); + }); + } +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_merkle_tree::(c); + bench_merkle_tree::(c); + bench_merkle_tree::>(c); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/poseidon2_plonky2/benches/recursion.rs b/poseidon2_plonky2/benches/recursion.rs new file mode 100644 index 0000000000..f766e5fca0 --- /dev/null +++ b/poseidon2_plonky2/benches/recursion.rs @@ -0,0 +1,247 @@ +#![feature(generic_const_exprs)] + +use std::marker::PhantomData; + +use anyhow::Result; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use plonky2::field::extension::Extendable; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::{ + CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, +}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig}; +use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; +use poseidon2_plonky2::poseidon2_goldilock::Poseidon2GoldilocksConfig; +use tynm::type_name; + +use crate::circuits::BaseCircuit; + +mod circuits; + +macro_rules! pretty_print { + ($($arg:tt)*) => { + print!("\x1b[0;36mINFO ===========>\x1b[0m "); + println!($($arg)*); + } +} + +/// Data structure with all input/output targets and the `CircuitData` for each circuit employed +/// to recursively shrink a proof up to the recursion threshold. The data structure contains a set +/// of targets and a `CircuitData` for each shrink step +struct ShrinkCircuit< + F: RichField + Extendable, + C: GenericConfig, + InnerC: GenericConfig, + const D: usize, +> { + proof_targets: Vec>, + circuit_data: Vec>, + inner_data: Vec, + _inner_c: PhantomData, +} + +const RECURSION_THRESHOLD: usize = 12; + +impl< + F: RichField + Extendable, + C: GenericConfig, + InnerC: GenericConfig, + const D: usize, + > ShrinkCircuit +where + InnerC::Hasher: AlgebraicHasher, + C::Hasher: AlgebraicHasher, +{ + pub fn build_shrink_circuit(inner_cd: &CommonCircuitData, config: CircuitConfig) -> Self { + let mut circuit_data = inner_cd; + let mut shrink_circuit = Self { + proof_targets: Vec::new(), + circuit_data: Vec::new(), + inner_data: Vec::new(), + _inner_c: PhantomData::, + }; + while circuit_data.degree_bits() > RECURSION_THRESHOLD { + let mut builder = CircuitBuilder::::new(config.clone()); + //let mut pw = PartialWitness::new(); + let pt = builder.add_virtual_proof_with_pis(circuit_data); + + let inner_data = + builder.add_virtual_verifier_data(circuit_data.config.fri_config.cap_height); + if shrink_circuit.num_shrink_steps() > 0 { + builder.verify_proof::(&pt, &inner_data, circuit_data); + } else { + builder.verify_proof::(&pt, &inner_data, circuit_data); + } + + for &pi_t in pt.public_inputs.iter() { + let t = builder.add_virtual_public_input(); + builder.connect(pi_t, t); + } + + let data = builder.build::(); + + shrink_circuit.proof_targets.push(pt); + shrink_circuit.circuit_data.push(data); + shrink_circuit.inner_data.push(inner_data); + circuit_data = &shrink_circuit.circuit_data.last().unwrap().common; + } + + shrink_circuit + } + + fn set_witness>( + pw: &mut PartialWitness, + proof: &ProofWithPublicInputs, + pt: &ProofWithPublicInputsTarget, + inner_data: &VerifierCircuitTarget, + circuit_data: &CircuitData, + ) where + GC::Hasher: AlgebraicHasher, + { + pw.set_proof_with_pis_target(pt, proof); + pw.set_cap_target( + &inner_data.constants_sigmas_cap, + &circuit_data.verifier_only.constants_sigmas_cap, + ); + pw.set_hash_target( + inner_data.circuit_digest, + circuit_data.verifier_only.circuit_digest, + ); + } + + pub fn shrink_proof<'a>( + &'a self, + inner_proof: ProofWithPublicInputs, + inner_cd: &'a CircuitData, + ) -> Result> { + let mut proof = None; + let mut circuit_data = None; + + for ((pt, cd), inner_data) in self + .proof_targets + .iter() + .zip(self.circuit_data.iter()) + .zip(self.inner_data.iter()) + { + let mut pw = PartialWitness::new(); + match (proof, circuit_data) { + (None, None) => Self::set_witness(&mut pw, &inner_proof, pt, inner_data, inner_cd), + (Some(inner_proof), Some(inner_cd)) => { + Self::set_witness(&mut pw, &inner_proof, pt, inner_data, inner_cd); + } + _ => unreachable!(), + } + proof = Some(cd.prove(pw)?); + circuit_data = Some(cd); + } + + Ok(proof.unwrap()) + } + + pub fn num_shrink_steps(&self) -> usize { + self.circuit_data.len() + } + + pub fn get_circuit_data(&self) -> &CircuitData { + self.circuit_data.last().unwrap() + } +} + +fn bench_recursive_proof< + F: RichField + Extendable, + const D: usize, + C: GenericConfig, + InnerC: GenericConfig, +>( + c: &mut Criterion, +) where + InnerC::Hasher: AlgebraicHasher, + C::Hasher: AlgebraicHasher, +{ + let mut group = c.benchmark_group(&format!( + "recursive-proof<{}, {}>", + type_name::(), + type_name::() + )); + + let config = CircuitConfig::standard_recursion_config(); + + for degree in [13, 15] { + let base_circuit = + BaseCircuit::::build_base_circuit(config.clone(), degree); + + assert_eq!(base_circuit.get_circuit_data().common.degree_bits(), degree); + + let proof = base_circuit.generate_base_proof(F::rand()).unwrap(); + + let inner_cd = &base_circuit.get_circuit_data().common; + + group.bench_function( + format!("build circuit for degree {}", degree).as_str(), + |b| { + b.iter_with_large_drop(|| { + ShrinkCircuit::::build_shrink_circuit( + inner_cd, + config.clone(), + ); + }) + }, + ); + + let shrink_circuit = + ShrinkCircuit::::build_shrink_circuit(inner_cd, config.clone()); + + pretty_print!("shrink steps: {}", shrink_circuit.num_shrink_steps()); + + let inner_cd = base_circuit.get_circuit_data(); + + group.bench_function( + format!("shrinking proof of degree {}", degree).as_str(), + |b| { + b.iter_batched( + || proof.clone(), + |proof| shrink_circuit.shrink_proof(proof, inner_cd).unwrap(), + BatchSize::PerIteration, + ) + }, + ); + + let shrunk_proof = shrink_circuit.shrink_proof(proof, inner_cd).unwrap(); + let shrunk_cd = shrink_circuit.get_circuit_data(); + + assert_eq!(shrunk_cd.common.degree_bits(), RECURSION_THRESHOLD); + + //let proof_bytes = serde_cbor::to_vec(&shrunk_proof).unwrap(); + //fancy_print!("Proof length: {} bytes for {} gates", proof_bytes.len(), shrunk_cd.common.degree()); + + group.bench_function( + format!("verify proof for degree {}", degree).as_str(), + |b| { + b.iter_batched( + || shrunk_proof.clone(), + |proof| shrunk_cd.verify(proof).unwrap(), + BatchSize::PerIteration, + ) + }, + ); + } + + group.finish(); +} + +fn benchmark(c: &mut Criterion) { + const D: usize = 2; + type F = GoldilocksField; + bench_recursive_proof::(c); + bench_recursive_proof::(c); + bench_recursive_proof::(c); + bench_recursive_proof::(c); +} + +criterion_group!(name = benches; + config = Criterion::default().sample_size(10); + targets = benchmark); +criterion_main!(benches); diff --git a/poseidon2_plonky2/src/lib.rs b/poseidon2_plonky2/src/lib.rs new file mode 100644 index 0000000000..abfafdea6a --- /dev/null +++ b/poseidon2_plonky2/src/lib.rs @@ -0,0 +1,15 @@ +#![feature(generic_const_exprs)] +#![allow(clippy::needless_range_loop)] + +//! This crate provides an implementation of the Poseidon2 hash function as described in +//! that can be seamlessly employed in Plonky2 proving +//! system. All the necessary traits and data structures necessary for Plonky2 are already +//! implemented. +//! +//! Furthermore, this crate provides a Plonky2 gate and the necessary traits and data structures to +//! employ this novel hash function in Plonky2 circuits +extern crate alloc; + +pub mod poseidon2_gate; +pub mod poseidon2_goldilock; +pub mod poseidon2_hash; diff --git a/poseidon2_plonky2/src/poseidon2_gate.rs b/poseidon2_plonky2/src/poseidon2_gate.rs new file mode 100644 index 0000000000..641b1a62a6 --- /dev/null +++ b/poseidon2_plonky2/src/poseidon2_gate.rs @@ -0,0 +1,604 @@ +//! Implementation of a Plonky2 gate for an entire Poseidon2 permutation over a state of width 12 + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use alloc::{format, vec}; +use core::marker::PhantomData; + +use plonky2::field::extension::Extendable; +use plonky2::field::types::Field; +use plonky2::gates::gate::Gate; +use plonky2::gates::util::StridedConstraintConsumer; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef}; +use plonky2::iop::target::Target; +use plonky2::iop::wire::Wire; +use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::circuit_data::CommonCircuitData; +use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; + +use crate::poseidon2_hash::{self as poseidon2, Poseidon2, WIDTH}; + +/// Evaluates a full Poseidon2 permutation with 12 state elements. +/// +/// This also has some extra features to make it suitable for efficiently verifying Merkle proofs. +/// It has a flag which can be used to swap the first four inputs with the next four, for ordering +/// sibling digests. +#[derive(Debug, Default)] +pub struct Poseidon2Gate, const D: usize>(PhantomData); + +impl, const D: usize> Poseidon2Gate { + pub fn new() -> Self { + Self(PhantomData) + } + + /// The wire index for the `i`th input to the permutation. + pub fn wire_input(i: usize) -> usize { + i + } + + /// The wire index for the `i`th output to the permutation. + pub fn wire_output(i: usize) -> usize { + WIDTH + i + } + + /// If this is set to 1, the first four inputs will be swapped with the next four inputs. This + /// is useful for ordering hashes in Merkle proofs. Otherwise, this should be set to 0. + pub const WIRE_SWAP: usize = 2 * WIDTH; + + const START_DELTA: usize = 2 * WIDTH + 1; + + /// A wire which stores `swap * (input[i + 4] - input[i])`; used to compute the swapped inputs. + fn wire_delta(i: usize) -> usize { + assert!(i < 4); + Self::START_DELTA + i + } + + const START_FULL_0: usize = Self::START_DELTA + 4; + + /// A wire which stores the input of the `i`-th S-box of the `round`-th round of the first set + /// of full rounds. + fn wire_full_sbox_0(round: usize, i: usize) -> usize { + debug_assert!( + round != 0, + "First round S-box inputs are not stored as wires" + ); + debug_assert!(round < poseidon2::HALF_N_FULL_ROUNDS); + debug_assert!(i < WIDTH); + Self::START_FULL_0 + WIDTH * (round - 1) + i + } + + const START_PARTIAL: usize = Self::START_FULL_0 + WIDTH * (poseidon2::HALF_N_FULL_ROUNDS - 1); + + /// A wire which stores the input of the S-box of the `round`-th round of the partial rounds. + fn wire_partial_sbox(round: usize) -> usize { + debug_assert!(round < poseidon2::N_PARTIAL_ROUNDS); + Self::START_PARTIAL + round + } + + const START_FULL_1: usize = Self::START_PARTIAL + poseidon2::N_PARTIAL_ROUNDS; + + /// A wire which stores the input of the `i`-th S-box of the `round`-th round of the second set + /// of full rounds. + fn wire_full_sbox_1(round: usize, i: usize) -> usize { + debug_assert!(round < poseidon2::HALF_N_FULL_ROUNDS); + debug_assert!(i < WIDTH); + Self::START_FULL_1 + WIDTH * round + i + } + + /// End of wire indices, exclusive. + fn end() -> usize { + Self::START_FULL_1 + WIDTH * poseidon2::HALF_N_FULL_ROUNDS + } +} + +impl + Poseidon2, const D: usize> Gate for Poseidon2Gate { + fn id(&self) -> String { + format!("{self:?}", WIDTH) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + // Assert that `swap` is binary. + let swap = vars.local_wires[Self::WIRE_SWAP]; + constraints.push(swap * (swap - F::Extension::ONE)); + + // Assert that each delta wire is set properly: `delta_i = swap * (rhs - lhs)`. + for i in 0..4 { + let input_lhs = vars.local_wires[Self::wire_input(i)]; + let input_rhs = vars.local_wires[Self::wire_input(i + 4)]; + let delta_i = vars.local_wires[Self::wire_delta(i)]; + constraints.push(swap * (input_rhs - input_lhs) - delta_i); + } + + // Compute the possibly-swapped input layer. + let mut state = [F::Extension::ZERO; WIDTH]; + for i in 0..4 { + let delta_i = vars.local_wires[Self::wire_delta(i)]; + let input_lhs = Self::wire_input(i); + let input_rhs = Self::wire_input(i + 4); + state[i] = vars.local_wires[input_lhs] + delta_i; + state[i + 4] = vars.local_wires[input_rhs] - delta_i; + } + for i in 8..WIDTH { + state[i] = vars.local_wires[Self::wire_input(i)]; + } + + let mut round_ctr = 0; + + // First external matrix + ::external_matrix_field(&mut state); + + // First set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + if r != 0 { + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_0(r, i)]; + constraints.push(state[i] - sbox_in); + state[i] = sbox_in; + } + } + ::sbox_layer_field(&mut state); + ::external_matrix_field(&mut state); + round_ctr += 1; + } + + // Partial rounds. + for r in 0..(poseidon2::N_PARTIAL_ROUNDS) { + state[0] += F::Extension::from_canonical_u64( + poseidon2::ALL_ROUND_CONSTANTS[poseidon2::HALF_N_FULL_ROUNDS * WIDTH + r * WIDTH], + ); + let sbox_in = vars.local_wires[Self::wire_partial_sbox(r)]; + constraints.push(state[0] - sbox_in); + state[0] = ::sbox_monomial(sbox_in); + ::internal_matrix_field(&mut state); + } + round_ctr += poseidon2::N_PARTIAL_ROUNDS; + + // Second set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_1(r, i)]; + constraints.push(state[i] - sbox_in); + state[i] = sbox_in; + } + ::sbox_layer_field(&mut state); + ::external_matrix_field(&mut state); + round_ctr += 1; + } + + for i in 0..WIDTH { + constraints.push(state[i] - vars.local_wires[Self::wire_output(i)]); + } + + constraints + } + + fn eval_unfiltered_base_one( + &self, + vars: EvaluationVarsBase, + mut yield_constr: StridedConstraintConsumer, + ) { + // Assert that `swap` is binary. + let swap = vars.local_wires[Self::WIRE_SWAP]; + yield_constr.one(swap * swap.sub_one()); + + // Assert that each delta wire is set properly: `delta_i = swap * (rhs - lhs)`. + for i in 0..4 { + let input_lhs = vars.local_wires[Self::wire_input(i)]; + let input_rhs = vars.local_wires[Self::wire_input(i + 4)]; + let delta_i = vars.local_wires[Self::wire_delta(i)]; + yield_constr.one(swap * (input_rhs - input_lhs) - delta_i); + } + + // Compute the possibly-swapped input layer. + let mut state = [F::ZERO; WIDTH]; + for i in 0..4 { + let delta_i = vars.local_wires[Self::wire_delta(i)]; + let input_lhs = Self::wire_input(i); + let input_rhs = Self::wire_input(i + 4); + state[i] = vars.local_wires[input_lhs] + delta_i; + state[i + 4] = vars.local_wires[input_rhs] - delta_i; + } + for i in 8..WIDTH { + state[i] = vars.local_wires[Self::wire_input(i)]; + } + + let mut round_ctr = 0; + + // First external matrix + ::external_matrix(&mut state); + + // First set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer(&mut state, round_ctr); + if r != 0 { + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_0(r, i)]; + yield_constr.one(state[i] - sbox_in); + state[i] = sbox_in; + } + } + ::sbox_layer(&mut state); + ::external_matrix(&mut state); + round_ctr += 1; + } + + // Partial rounds. + let mut constant_counter = poseidon2::HALF_N_FULL_ROUNDS * WIDTH; + for r in 0..(poseidon2::N_PARTIAL_ROUNDS) { + state[0] += F::from_canonical_u64(poseidon2::ALL_ROUND_CONSTANTS[constant_counter]); + constant_counter += WIDTH; + let sbox_in = vars.local_wires[Self::wire_partial_sbox(r)]; + yield_constr.one(state[0] - sbox_in); + state[0] = ::sbox_monomial(sbox_in); + ::internal_matrix(&mut state); + } + round_ctr += poseidon2::N_PARTIAL_ROUNDS; + + // Second set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer(&mut state, round_ctr); + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_1(r, i)]; + yield_constr.one(state[i] - sbox_in); + state[i] = sbox_in; + } + ::sbox_layer(&mut state); + ::external_matrix(&mut state); + round_ctr += 1; + } + + for i in 0..WIDTH { + yield_constr.one(state[i] - vars.local_wires[Self::wire_output(i)]); + } + } + + fn eval_unfiltered_circuit( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + // Assert that `swap` is binary. + let swap = vars.local_wires[Self::WIRE_SWAP]; + constraints.push(builder.mul_sub_extension(swap, swap, swap)); + + // Assert that each delta wire is set properly: `delta_i = swap * (rhs - lhs)`. + for i in 0..4 { + let input_lhs = vars.local_wires[Self::wire_input(i)]; + let input_rhs = vars.local_wires[Self::wire_input(i + 4)]; + let delta_i = vars.local_wires[Self::wire_delta(i)]; + let diff = builder.sub_extension(input_rhs, input_lhs); + constraints.push(builder.mul_sub_extension(swap, diff, delta_i)); + } + + // Compute the possibly-swapped input layer. + let mut state = [builder.zero_extension(); WIDTH]; + for i in 0..4 { + let delta_i = vars.local_wires[Self::wire_delta(i)]; + let input_lhs = vars.local_wires[Self::wire_input(i)]; + let input_rhs = vars.local_wires[Self::wire_input(i + 4)]; + state[i] = builder.add_extension(input_lhs, delta_i); + state[i + 4] = builder.sub_extension(input_rhs, delta_i); + } + for i in 8..WIDTH { + state[i] = vars.local_wires[Self::wire_input(i)]; + } + + let mut round_ctr = 0; + + // First external matrix + ::external_matrix_circuit(builder, &mut state); + + // First set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_circuit(builder, &mut state, round_ctr); + if r != 0 { + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_0(r, i)]; + constraints.push(builder.sub_extension(state[i], sbox_in)); + state[i] = sbox_in; + } + } + ::sbox_layer_circuit(builder, &mut state); + ::external_matrix_circuit(builder, &mut state); + round_ctr += 1; + } + + // Partial rounds. + let mut constant_counter = poseidon2::HALF_N_FULL_ROUNDS * WIDTH; + for r in 0..(poseidon2::N_PARTIAL_ROUNDS) { + let c = poseidon2::ALL_ROUND_CONSTANTS[constant_counter]; + let c = F::Extension::from_canonical_u64(c); + let c = builder.constant_extension(c); + state[0] = builder.add_extension(state[0], c); + constant_counter += WIDTH; + let sbox_in = vars.local_wires[Self::wire_partial_sbox(r)]; + constraints.push(builder.sub_extension(state[0], sbox_in)); + state[0] = ::sbox_monomial_circuit(builder, sbox_in); + ::internal_matrix_circuit(builder, &mut state); + } + round_ctr += poseidon2::N_PARTIAL_ROUNDS; + + // Second set of full rounds. + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_circuit(builder, &mut state, round_ctr); + for i in 0..WIDTH { + let sbox_in = vars.local_wires[Self::wire_full_sbox_1(r, i)]; + constraints.push(builder.sub_extension(state[i], sbox_in)); + state[i] = sbox_in; + } + ::sbox_layer_circuit(builder, &mut state); + ::external_matrix_circuit(builder, &mut state); + round_ctr += 1; + } + + for i in 0..WIDTH { + constraints + .push(builder.sub_extension(state[i], vars.local_wires[Self::wire_output(i)])); + } + + constraints + } + + fn generators(&self, row: usize, _local_constants: &[F]) -> Vec> { + let gen = Poseidon2Generator:: { + row, + _phantom: PhantomData, + }; + vec![WitnessGeneratorRef::new(gen.adapter())] + } + + fn num_wires(&self) -> usize { + Self::end() + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + 7 + } + + fn num_constraints(&self) -> usize { + WIDTH * (poseidon2::N_FULL_ROUNDS_TOTAL - 1) + poseidon2::N_PARTIAL_ROUNDS + WIDTH + 1 + 4 + } + + fn serialize(&self, dst: &mut Vec, common_data: &CommonCircuitData) -> IoResult<()> { + Ok(()) + } + + fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult + where + Self: Sized, + { + Ok(Poseidon2Gate::new()) + } +} + +#[derive(Debug, Clone, Default)] +pub struct Poseidon2Generator + Poseidon2, const D: usize> { + row: usize, + _phantom: PhantomData, +} + +impl + Poseidon2, const D: usize> SimpleGenerator + for Poseidon2Generator +{ + fn dependencies(&self) -> Vec { + (0..WIDTH) + .map(|i| Poseidon2Gate::::wire_input(i)) + .chain(Some(Poseidon2Gate::::WIRE_SWAP)) + .map(|column| Target::wire(self.row, column)) + .collect() + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let local_wire = |column| Wire { + row: self.row, + column, + }; + + let mut state = (0..WIDTH) + .map(|i| witness.get_wire(local_wire(Poseidon2Gate::::wire_input(i)))) + .collect::>(); + + let swap_value = witness.get_wire(local_wire(Poseidon2Gate::::WIRE_SWAP)); + debug_assert!(swap_value == F::ZERO || swap_value == F::ONE); + + for i in 0..4 { + let delta_i = swap_value * (state[i + 4] - state[i]); + out_buffer.set_wire(local_wire(Poseidon2Gate::::wire_delta(i)), delta_i); + } + + if swap_value == F::ONE { + for i in 0..4 { + state.swap(i, 4 + i); + } + } + + let mut state: [F; WIDTH] = state.try_into().unwrap(); + let mut round_ctr = 0; + + // First external matrix + ::external_matrix_field(&mut state); + + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + if r != 0 { + for i in 0..WIDTH { + out_buffer.set_wire( + local_wire(Poseidon2Gate::::wire_full_sbox_0(r, i)), + state[i], + ); + } + } + ::sbox_layer_field(&mut state); + ::external_matrix_field(&mut state); + round_ctr += 1; + } + + let mut constant_counter = poseidon2::HALF_N_FULL_ROUNDS * WIDTH; + for r in 0..(poseidon2::N_PARTIAL_ROUNDS) { + state[0] += F::from_canonical_u64(poseidon2::ALL_ROUND_CONSTANTS[constant_counter]); + constant_counter += WIDTH; + out_buffer.set_wire( + local_wire(Poseidon2Gate::::wire_partial_sbox(r)), + state[0], + ); + state[0] = ::sbox_monomial(state[0]); + ::internal_matrix_field(&mut state); + } + round_ctr += poseidon2::N_PARTIAL_ROUNDS; + + for r in 0..poseidon2::HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + for i in 0..WIDTH { + out_buffer.set_wire( + local_wire(Poseidon2Gate::::wire_full_sbox_1(r, i)), + state[i], + ); + } + ::sbox_layer_field(&mut state); + ::external_matrix_field(&mut state); + round_ctr += 1; + } + + for i in 0..WIDTH { + out_buffer.set_wire(local_wire(Poseidon2Gate::::wire_output(i)), state[i]); + } + } + + fn id(&self) -> String { + "Poseidon2Generator".to_string() + } + + fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { + dst.write_usize(self.row) + } + + fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData) -> IoResult + where + Self: Sized, + { + let row = src.read_usize()?; + Ok(Self { + row, + _phantom: PhantomData, + }) + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::types::Field; + use plonky2::gates::gate_testing::{test_eval_fns, test_low_degree}; + use plonky2::iop::target::Target; + use plonky2::iop::wire::Wire; + use plonky2::iop::witness::{PartialWitness, WitnessWrite}; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::GenericConfig; + + use super::Poseidon2Gate; + use crate::poseidon2_goldilock::Poseidon2GoldilocksConfig; + use crate::poseidon2_hash::{Poseidon2, WIDTH}; + + #[test] + fn wire_indices() { + type F = GoldilocksField; + type Gate = Poseidon2Gate; + + assert_eq!(Gate::wire_input(0), 0); + assert_eq!(Gate::wire_input(11), 11); + assert_eq!(Gate::wire_output(0), 12); + assert_eq!(Gate::wire_output(11), 23); + assert_eq!(Gate::WIRE_SWAP, 24); + assert_eq!(Gate::wire_delta(0), 25); + assert_eq!(Gate::wire_delta(3), 28); + assert_eq!(Gate::wire_full_sbox_0(1, 0), 29); + assert_eq!(Gate::wire_full_sbox_0(3, 0), 53); + assert_eq!(Gate::wire_full_sbox_0(3, 11), 64); + assert_eq!(Gate::wire_partial_sbox(0), 65); + assert_eq!(Gate::wire_partial_sbox(21), 86); + assert_eq!(Gate::wire_full_sbox_1(0, 0), 87); + assert_eq!(Gate::wire_full_sbox_1(3, 0), 123); + assert_eq!(Gate::wire_full_sbox_1(3, 11), 134); + } + + #[test] + fn generated_output() { + const D: usize = 2; + type C = Poseidon2GoldilocksConfig; + type F = >::F; + + let config = CircuitConfig { + num_wires: 143, + ..CircuitConfig::standard_recursion_config() + }; + let mut builder = CircuitBuilder::new(config); + type Gate = Poseidon2Gate; + let gate = Gate::new(); + let row = builder.add_gate(gate, vec![]); + for i in 0..WIDTH { + builder.register_public_input(Target::wire(row, Gate::wire_output(i))); + } + let circuit = builder.build_prover::(); + + let permutation_inputs = (0..WIDTH).map(F::from_canonical_usize).collect::>(); + + let mut inputs = PartialWitness::new(); + inputs.set_wire( + Wire { + row, + column: Gate::WIRE_SWAP, + }, + F::ZERO, + ); + for i in 0..WIDTH { + inputs.set_wire( + Wire { + row, + column: Gate::wire_input(i), + }, + permutation_inputs[i], + ); + } + + let proof = circuit.prove(inputs).unwrap(); + + let expected_outputs: [F; WIDTH] = F::poseidon2(permutation_inputs.try_into().unwrap()); + expected_outputs + .iter() + .zip(proof.public_inputs.iter()) + .for_each(|(expected_out, out)| assert_eq!(expected_out, out)); + } + + #[test] + fn low_degree() { + type F = GoldilocksField; + let gate = Poseidon2Gate::::new(); + test_low_degree(gate) + } + + #[test] + fn eval_fns() -> Result<()> { + const D: usize = 2; + type C = Poseidon2GoldilocksConfig; + type F = >::F; + let gate = Poseidon2Gate::::new(); + test_eval_fns::(gate) + } +} diff --git a/poseidon2_plonky2/src/poseidon2_goldilock.rs b/poseidon2_plonky2/src/poseidon2_goldilock.rs new file mode 100644 index 0000000000..04bd304ab2 --- /dev/null +++ b/poseidon2_plonky2/src/poseidon2_goldilock.rs @@ -0,0 +1,170 @@ +//! Implementations for Poseidon2 over Goldilocks field of widths 12. + +use plonky2::field::extension::quadratic::QuadraticExtension; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::GenericConfig; + +use crate::poseidon2_hash::{Poseidon2, Poseidon2Hash, WIDTH}; + +#[rustfmt::skip] +impl Poseidon2 for GoldilocksField { + // We only need INTERNAL_MATRIX_DIAG_M_1 here, specifying the diagonal - 1 of the internal matrix + + const INTERNAL_MATRIX_DIAG_M_1: [u64; WIDTH] = [ + 0xcf6f77ac16722af9, 0x3fd4c0d74672aebc, 0x9b72bf1c1c3d08a8, 0xe4940f84b71e4ac2, + 0x61b27b077118bc72, 0x2efd8379b8e661e2, 0x858edcf353df0341, 0x2d9c20affb5c4516, + 0x5120143f0695defb, 0x62fc898ae34a5c5b, 0xa3d9560c99123ed2, 0x98fd739d8e7fc933, + ]; + + #[cfg(all(target_arch="aarch64", target_feature="neon"))] + #[inline(always)] + fn sbox_layer(state: &mut [Self; 12]) { + unsafe { + plonky2::hash::arch::aarch64::poseidon_goldilocks_neon::sbox_layer(state); + } + } +} + +/// Configuration using Poseidon2 over the Goldilocks field. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct Poseidon2GoldilocksConfig; +impl GenericConfig<2> for Poseidon2GoldilocksConfig { + type F = GoldilocksField; + type FE = QuadraticExtension; + type Hasher = Poseidon2Hash; + type InnerHasher = Poseidon2Hash; +} + +#[cfg(test)] +mod tests { + use plonky2::field::extension::Extendable; + use plonky2::field::goldilocks_field::GoldilocksField as F; + use plonky2::hash::hash_types::RichField; + use plonky2::hash::poseidon::PoseidonHash; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{ + AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, + }; + use rstest::rstest; + use serial_test::serial; + + use crate::poseidon2_goldilock::Poseidon2GoldilocksConfig; + use crate::poseidon2_hash::test_helpers::{ + check_test_vectors, prove_circuit_with_poseidon_hash, recursive_proof, + }; + use crate::poseidon2_hash::{Poseidon2, Poseidon2Hash}; + + const D: usize = 2; + + #[test] + fn test_vectors() { + // Test inputs are: + // 1. range 0..WIDTH + + #[rustfmt::skip] + let test_vectors12: Vec<([u64; 12], [u64; 12])> = vec![ + ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ], + [0xed3dbcc4ff1e8d33, 0xfb85eac6ac91a150, 0xd41e1e237ed3e2ef, 0x5e289bf0a4c11897, + 0x4398b20f93e3ba6b, 0x5659a48ffaf2901d, 0xe44d81e89a88f8ae, 0x08efdb285f8c3dbc, + 0x294ab7503297850e, 0xa11c61f4870b9904, 0xa6855c112cc08968, 0x17c6d53d2fb3e8c1, ]), + ]; + + check_test_vectors::(test_vectors12); + } + + #[test] + fn test_circuit_with_poseidon2() { + let (cd, proof) = prove_circuit_with_poseidon_hash::<_, Poseidon2GoldilocksConfig, D, _>( + CircuitConfig::standard_recursion_config(), + 1024, + Poseidon2Hash, + false, + ) + .unwrap(); + + cd.verify(proof).unwrap(); + } + + #[ignore] + #[rstest] + #[case::poseidon(PoseidonGoldilocksConfig{})] + #[case::poseidon2(Poseidon2GoldilocksConfig{})] + #[serial] + fn compare_proof_generation_with_poseidon< + F: RichField + Extendable + Poseidon2, + const D: usize, + C: GenericConfig, + >( + #[case] _c: C, + ) { + let (cd, proof) = prove_circuit_with_poseidon_hash::<_, C, D, _>( + CircuitConfig::standard_recursion_config(), + 4096, + Poseidon2Hash, + true, + ) + .unwrap(); + + cd.verify(proof).unwrap(); + } + + #[ignore] + #[rstest] + #[case::poseidon(PoseidonGoldilocksConfig, PoseidonHash{})] + #[case::poseidon2(PoseidonGoldilocksConfig, Poseidon2Hash{})] + #[serial] + fn compare_circuits_with_poseidon< + F: RichField + Extendable + Poseidon2, + const D: usize, + C: GenericConfig, + H: Hasher + AlgebraicHasher, + >( + #[case] _conf: C, + #[case] hasher: H, + ) { + let (cd, proof) = prove_circuit_with_poseidon_hash::<_, C, D, _>( + CircuitConfig::standard_recursion_config(), + 4096, + hasher, + true, + ) + .unwrap(); + + cd.verify(proof).unwrap(); + } + + #[rstest] + #[serial] + fn test_recursive_circuit_with_poseidon2< + F: RichField + Poseidon2 + Extendable, + C: GenericConfig, + InnerC: GenericConfig, + const D: usize, + >( + #[values(PoseidonGoldilocksConfig{}, Poseidon2GoldilocksConfig{})] _c: C, + #[values(PoseidonGoldilocksConfig{}, Poseidon2GoldilocksConfig{})] _inner: InnerC, + ) where + InnerC::Hasher: AlgebraicHasher, + { + let config = CircuitConfig::standard_recursion_config(); + + let (cd, proof) = prove_circuit_with_poseidon_hash::( + config, + 1024, + Poseidon2Hash {}, + false, + ) + .unwrap(); + + println!("base proof generated"); + + let (rec_cd, rec_proof) = + recursive_proof::(proof, &cd, &cd.common.config).unwrap(); + + println!("recursive proof generated"); + + rec_cd.verify(rec_proof).unwrap(); + + assert_eq!(rec_cd.common.degree_bits(), 12); + } +} diff --git a/poseidon2_plonky2/src/poseidon2_hash.rs b/poseidon2_plonky2/src/poseidon2_hash.rs new file mode 100644 index 0000000000..04aa98c818 --- /dev/null +++ b/poseidon2_plonky2/src/poseidon2_hash.rs @@ -0,0 +1,802 @@ +//! Implementation of the Poseidon2 hash function and the traits necessary to employ it in Plonky2 + +use alloc::vec; +use alloc::vec::Vec; +use std::fmt::Debug; + +use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::field::types::{Field, PrimeField64}; +use plonky2::hash::hash_types::{HashOut, RichField}; +use plonky2::hash::hashing::{compress, hash_n_to_hash_no_pad, PlonkyPermutation}; +use plonky2::hash::poseidon::{SPONGE_RATE, SPONGE_WIDTH}; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::{BoolTarget, Target}; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::{AlgebraicHasher, Hasher}; +use unroll::unroll_for_loops; + +use crate::poseidon2_gate::Poseidon2Gate; + +pub const RATE: usize = SPONGE_RATE; +pub const WIDTH: usize = SPONGE_WIDTH; + +/// The number of full rounds and partial rounds is given by the +/// calc_round_numbers.py script. They happen to be the same for both +/// width 8 and width 12 with s-box x^7. +// +// NB: Changing any of these values will require regenerating all of +// the precomputed constant arrays in this file. +pub const HALF_N_FULL_ROUNDS: usize = 4; +pub(crate) const N_FULL_ROUNDS_TOTAL: usize = 2 * HALF_N_FULL_ROUNDS; +pub const N_PARTIAL_ROUNDS: usize = 22; +pub const N_ROUNDS: usize = N_FULL_ROUNDS_TOTAL + N_PARTIAL_ROUNDS; +const MAX_WIDTH: usize = 12; // we only have width 8 and 12, and 12 is bigger. :) + +// Round constants for Poseidon and Poseidon2 are the same (given a specific instance) +#[rustfmt::skip] +pub const ALL_ROUND_CONSTANTS: [u64; MAX_WIDTH * N_ROUNDS] = [ + // WARNING: The AVX2 Goldilocks specialization relies on all round constants being in + // 0..0xfffeeac900011537. If these constants are randomly regenerated, there is a ~.6% chance + // that this condition will no longer hold. + 0xe034a8785fd284a7, 0xe2463f1ea42e1b80, 0x048742e681ae290a, 0xe4af50ade990154c, + 0x8b13ffaaf4f78f8a, 0xe3fbead7dccd8d63, 0x631a47705eb92bf8, 0x88fbbb8698548659, + 0x74cd2003b0f349c9, 0xe16a3df6764a3f5d, 0x57ce63971a71aaa2, 0xdc1f7fd3e7823051, + 0xbb8423be34c18d7a, 0xf8bc5a2a0c1b3d6d, 0xf1a01bbd6f7123e5, 0xed960a080f5e348b, + 0x1b9c0c1e87e2390e, 0x18c83caf729a613e, 0x671ab9fe037a72c4, 0x508565f67d4c276a, + 0x4d2cd8827a482590, 0xa48e11e84dd3500b, 0x825a8c955fc2442b, 0xf573a6ee07cddc68, + 0x7dd3f19c73a39e0b, 0xcc0f13537a796fa6, 0x1d9006bfaedac57f, 0x4705f69b68b0b7de, + 0x5b62bfb718bcc57f, 0x879d821770563827, 0x3da5ccb7f8dff0e3, 0xb49d6a706923fc5b, + 0xb6a0babe883a969d, 0x2984f9b055401960, 0xcd3496f05511d79d, 0x4791da5d63854fc5, + 0xdb7344d0580a39d4, 0x5aedc4dad1de120a, 0x5e1bdc1fb8e1abf0, 0x3904c09a0e46747c, + 0xb54a0e23ab85ddcd, 0xc0c3cf05bccbdb3a, 0xb362076a73baf7e9, 0x212c953d81a5d5ba, + 0x212d4cc965d898bd, 0xdd44ddd0f41509b9, 0x8931329fa67823c0, 0xc65510f4d2a873be, + 0xe3ecbb6ba1e16211, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x70f5b3266792bbb6, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xe7560e690634757e, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xafd0202bc7eaf66e, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x349f4c5871f220fd, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x3697eb3e31529e0d, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x7735d5b0622d9900, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x5f5b58b9cf997668, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x645534b6548af9d9, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x4232d29d91a426a8, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xb987278aed485d35, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x6dabeef669bb406e, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x35ee78288b749d40, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x6dcd560f14af0fc3, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x71ed3dc007ea6383, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x8b6b51caab7f5b6f, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xcf2e8cc4181dbfa8, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xa01d3f1c306f825a, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xccee646a5d8ddb87, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x70df6f277cbaffeb, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x64ec0a6556b8f45c, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x6f68c9664fda6e37, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x387356e4516fab6f, 0x35310dce33903e67, 0x45f3e5251d30f912, 0x7c97f480ca428f45, + 0x74d5874c20b50de2, 0xff1d5b7cee3dc67f, 0xa04d5d5ac0ff3de9, 0x1cefb5eb7d24580e, + 0xf685e1bfcc0104ad, 0x6204dd95db22ead4, 0x8265c6c57c73c440, 0x4f708ab0b4e1e382, + 0xcfc60c7a52fbffa7, 0x9c0c1951d8910306, 0x4d06df27c89819f2, 0x621bdb0e75eca660, + 0x343adffd079cee57, 0xa760f0e5debde398, 0xe3110fefd97b188a, 0x0ed6584e6b150297, + 0x2b10e625d0d079c0, 0xefa493442057264f, 0xebcfaa7b3f26a2b6, 0xf36bcda28e343e2a, + 0xa1183cb63b67aa9e, 0x40f3e415d5e5b0ba, 0xc51fc2367eff7b15, 0xe07fe5f3aebc649f, + 0xc9cb2be56968e8aa, 0x648600db69078a0e, 0x4e9135ab1256edb9, 0x00382c73435556c2, + 0x1d78cafac9150ddf, 0xb8df60ab6215a233, 0xa7a65ba31f8fcd9a, 0x907d436dd964006b, + 0x3bdf7fd528633b97, 0x265adb359c0cc0f8, 0xf16cfc4034b39614, 0x71f0751b08fa0947, + 0x3165eda4b5403a37, 0xca30fc5680467e46, 0x4c743354d37777c5, 0x3d1f0a4e6bba4a09, + 0xc0c2e289afa75181, 0x1e4fa2ad948978b7, 0x2a226a127a0bb26a, 0xe61738a70357ce76, +]; + +// Applying cheap 4x4 MDS matrix to each 4-element part of the state +// The matrix in this case is: +// M_4 = +// [5 7 1 3] +// [4 6 1 1] +// [1 3 5 7] +// [1 1 4 6] +// The computation is shown in more detail in https://tosc.iacr.org/index.php/ToSC/article/view/888/839, Figure 13 (M_{4,4}^{8,4} with alpha = 2) +#[inline(always)] +fn matrix_mul_block(x: &mut [u64]) { + let mut t_2 = x[1]; + let mut t_3 = x[3]; + let t_0 = x[0] + t_2; + let t_1 = x[2] + t_3; + t_2 = (t_2 << 1) + t_1; + t_3 = (t_3 << 1) + t_0; + let t_4 = (t_1 << 2) + t_3; + let t_5 = (t_0 << 2) + t_2; + let t_6 = t_3 + t_5; + let t_7 = t_2 + t_4; + x[0] = t_6; + x[1] = t_5; + x[2] = t_7; + x[3] = t_4; +} + +#[inline(always)] +#[unroll_for_loops] +fn combine_m4_prods(x: &mut [u64], s: [u64; 4]) { + for i in 0..4 { + x[i] += s[i]; + } +} + +const T4: usize = WIDTH / 4; + +// Apply external matrix to a state vector with at most 32 bits elements. +// This is employed to compute product with external matrix employing only native u64 integer +// arithmetic for efficiency +#[inline(always)] +#[unroll_for_loops] +fn external_matrix_with_u64_arithmetic(x: &mut [u64; WIDTH]) { + for i in 0..T4 { + matrix_mul_block(&mut x[i * 4..(i + 1) * 4]); + } + + // Applying second cheap matrix + // This completes the multiplication by + // M_E = + // [2*M_4 M_4 M_4] + // [ M_4 2*M_4 M_4] + // [ M_4 M_4 2*M_4] + // using the results with M_4 obtained above + + // compute vector to be later used to combine M_4 multiplication results with current state x; + // this operation is performed without loops for efficiency + debug_assert_eq!(T4, 3); + let s0 = x[0] + x[4] + x[8]; + let s1 = x[1] + x[5] + x[9]; + let s2 = x[2] + x[6] + x[10]; + let s3 = x[3] + x[7] + x[11]; + let s = [s0, s1, s2, s3]; + + for i in 0..T4 { + combine_m4_prods(&mut x[i * 4..(i + 1) * 4], s); + } +} + +pub trait Poseidon2: PrimeField64 { + /// Total number of round constants required: width of the input + /// times number of rounds. + const N_ROUND_CONSTANTS: usize = WIDTH * N_ROUNDS; + + /// We only need INTERNAL_MATRIX_DIAG_M_1 here, specifying the diagonal - 1 of the internal matrix + const INTERNAL_MATRIX_DIAG_M_1: [u64; WIDTH]; + + /// Compute the product between the state vector and the matrix employed in full rounds of + /// the permutation + #[inline(always)] + #[unroll_for_loops] + fn external_matrix(state: &mut [Self; WIDTH]) { + let mut state_l = [0u64; WIDTH]; + let mut state_h = [0u64; WIDTH]; + for i in 0..WIDTH { + let state_u64 = state[i].to_noncanonical_u64(); + state_h[i] = state_u64 >> 32; + state_l[i] = (state_u64 as u32) as u64; + } + external_matrix_with_u64_arithmetic(&mut state_l); + external_matrix_with_u64_arithmetic(&mut state_h); + + for i in 0..WIDTH { + let (state_u64, carry) = state_l[i].overflowing_add(state_h[i] << 32); + state[i] = + Self::from_noncanonical_u96((state_u64, (state_h[i] >> 32) as u32 + carry as u32)); + } + } + + /// Same as `external_matrix` for field extensions of `Self`. + fn external_matrix_field, const D: usize>( + state: &mut [F; WIDTH], + ) { + // Applying cheap 4x4 MDS matrix to each 4-element part of the state + let t4 = WIDTH / 4; + for i in 0..t4 { + let start_index = i * 4; + let mut t_0 = state[start_index]; + t_0 += state[start_index + 1]; + let mut t_1 = state[start_index + 2]; + t_1 += state[start_index + 3]; + let mut t_2 = state[start_index + 1]; + t_2 = t_2 + t_2; + t_2 += t_1; + let mut t_3 = state[start_index + 3]; + t_3 = t_3 + t_3; + t_3 += t_0; + let mut t_4 = t_1; + t_4 = F::from_canonical_u64(4) * t_4; + t_4 += t_3; + let mut t_5 = t_0; + t_5 = F::from_canonical_u64(4) * t_5; + t_5 += t_2; + let mut t_6 = t_3; + t_6 += t_5; + let mut t_7 = t_2; + t_7 += t_4; + state[start_index] = t_6; + state[start_index + 1] = t_5; + state[start_index + 2] = t_7; + state[start_index + 3] = t_4; + } + + // Applying second cheap matrix + let mut stored = [F::ZERO; 4]; + for l in 0..4 { + stored[l] = state[l]; + for j in 1..t4 { + stored[l] += state[4 * j + l]; + } + } + for i in 0..WIDTH { + state[i] += stored[i % 4]; + } + } + + /// Recursive version of `external_matrix`. + fn external_matrix_circuit( + builder: &mut CircuitBuilder, + state: &mut [ExtensionTarget; WIDTH], + ) where + Self: RichField + Extendable, + { + // In contrast to the Poseidon circuit, we *may not need* PoseidonMdsGate, because the number of constraints will fit regardless + // Check! + let four = Self::from_canonical_u64(0x4); + // let four = builder.constant_extension(Self::Extension::from_canonical_u64(0x4)); + + // Applying cheap 4x4 MDS matrix to each 4-element part of the state + let t4 = WIDTH / 4; + for i in 0..t4 { + let start_index = i * 4; + let mut t_0 = state[start_index]; + t_0 = builder.add_extension(t_0, state[start_index + 1]); + let mut t_1 = state[start_index + 2]; + t_1 = builder.add_extension(t_1, state[start_index + 3]); + let mut t_2 = state[start_index + 1]; + t_2 = builder.add_extension(t_2, t_2); // Double + t_2 = builder.add_extension(t_2, t_1); + let mut t_3 = state[start_index + 3]; + t_3 = builder.add_extension(t_3, t_3); // Double + t_3 = builder.add_extension(t_3, t_0); + let mut t_4 = t_1; + t_4 = builder.mul_const_extension(four, t_4); // times 4 + t_4 = builder.add_extension(t_4, t_3); + let mut t_5 = t_0; + t_5 = builder.mul_const_extension(four, t_5); // times 4 + t_5 = builder.add_extension(t_5, t_2); + let mut t_6 = t_3; + t_6 = builder.add_extension(t_6, t_5); + let mut t_7 = t_2; + t_7 = builder.add_extension(t_7, t_4); + state[start_index] = t_6; + state[start_index + 1] = t_5; + state[start_index + 2] = t_7; + state[start_index + 3] = t_4; + } + + // Applying second cheap matrix + let mut stored = [builder.zero_extension(); 4]; + for l in 0..4 { + stored[l] = state[l]; + for j in 1..t4 { + stored[l] = builder.add_extension(stored[l], state[4 * j + l]); + } + } + for i in 0..WIDTH { + state[i] = builder.add_extension(state[i], stored[i % 4]); + } + } + + /// Compute the product between the state vector and the matrix employed in partial rounds of + /// the permutation + #[inline(always)] + #[unroll_for_loops] + fn internal_matrix(state: &mut [Self; WIDTH]) { + // This computes the mutliplication with the matrix + // M_I = + // [r_1 1 1 ... 1] + // [ 1 r_2 1 ... 1] + // ... + // [ 1 1 1 ... r_t] + // for pseudo-random values r_1, r_2, ..., r_t. Note that for efficiency in Self::INTERNAL_MATRIX_DIAG_M_1 only r_1 - 1, r_2 - 1, ..., r_t - 1 are stored + // Compute input sum + let f_sum = Self::from_noncanonical_u128( + state + .iter() + .fold(0u128, |sum, el| sum + el.to_noncanonical_u64() as u128), + ); + // Add sum + diag entry * element to each element + for i in 0..WIDTH { + state[i] *= Self::from_canonical_u64(Self::INTERNAL_MATRIX_DIAG_M_1[i]); + state[i] += f_sum; + } + } + + /// Same as `internal_matrix` for field extensions of `Self`. + fn internal_matrix_field, const D: usize>( + state: &mut [F; WIDTH], + ) { + // Compute input sum + let sum = state.iter().fold(F::ZERO, |sum, el| sum + *el); + // Add sum + diag entry * element to each element + for i in 0..state.len() { + state[i] *= F::from_canonical_u64(Self::INTERNAL_MATRIX_DIAG_M_1[i]); + state[i] += sum; + } + } + + /// Recursive version of `internal_matrix`. + fn internal_matrix_circuit( + builder: &mut CircuitBuilder, + state: &mut [ExtensionTarget; WIDTH], + ) where + Self: RichField + Extendable, + { + // Compute input sum + let mut sum = state[0]; + for i in 1..state.len() { + sum = builder.add_extension(sum, state[i]); + } + // Add sum + diag entry * element to each element + for i in 0..state.len() { + // Computes `C * x + y` + state[i] = builder.mul_const_add_extension( + Self::from_canonical_u64(::INTERNAL_MATRIX_DIAG_M_1[i]), + state[i], + sum, + ); + } + } + + /// Add round constant to `state` for round `round_ctr` + #[inline(always)] + #[unroll_for_loops] + fn constant_layer(state: &mut [Self; WIDTH], round_ctr: usize) { + for i in 0..12 { + if i < WIDTH { + let round_constant = ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr]; + unsafe { + state[i] = state[i].add_canonical_u64(round_constant); + } + } + } + } + + /// Same as `constant_layer` for field extensions of `Self`. + fn constant_layer_field, const D: usize>( + state: &mut [F; WIDTH], + round_ctr: usize, + ) { + for i in 0..WIDTH { + state[i] += F::from_canonical_u64(ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr]); + } + } + + /// Recursive version of `constant_layer`. + fn constant_layer_circuit( + builder: &mut CircuitBuilder, + state: &mut [ExtensionTarget; WIDTH], + round_ctr: usize, + ) where + Self: RichField + Extendable, + { + for i in 0..WIDTH { + let c = ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr]; + let c = Self::Extension::from_canonical_u64(c); + let c = builder.constant_extension(c); + state[i] = builder.add_extension(state[i], c); + } + } + + /// Apply the sbox to a single state element + #[inline(always)] + fn sbox_monomial, const D: usize>(x: F) -> F { + // x |--> x^7 + let x2 = x.square(); + let x4 = x2.square(); + let x3 = x * x2; + x3 * x4 + } + + /// Recursive version of `sbox_monomial`. + fn sbox_monomial_circuit( + builder: &mut CircuitBuilder, + x: ExtensionTarget, + ) -> ExtensionTarget + where + Self: RichField + Extendable, + { + // x |--> x^7 + builder.exp_u64_extension(x, 7) + } + + /// Apply the sbox-layer to the whole state of the permutation + #[inline(always)] + #[unroll_for_loops] + fn sbox_layer(state: &mut [Self; WIDTH]) { + for i in 0..12 { + if i < WIDTH { + state[i] = Self::sbox_monomial(state[i]); + } + } + } + + /// Same as `sbox_layer` for field extensions of `Self`. + fn sbox_layer_field, const D: usize>( + state: &mut [F; WIDTH], + ) { + for i in 0..WIDTH { + state[i] = Self::sbox_monomial(state[i]); + } + } + + /// Recursive version of `sbox_layer`. + fn sbox_layer_circuit( + builder: &mut CircuitBuilder, + state: &mut [ExtensionTarget; WIDTH], + ) where + Self: RichField + Extendable, + { + for i in 0..WIDTH { + state[i] = ::sbox_monomial_circuit(builder, state[i]); + } + } + + /// Apply half of the overall full rounds of the permutation. It can be employed to perform + /// both the first and the second half of the full rounds, depending on the value of `round_ctr` + #[inline] + fn full_rounds(state: &mut [Self; WIDTH], round_ctr: &mut usize) { + for _ in 0..HALF_N_FULL_ROUNDS { + Self::constant_layer(state, *round_ctr); + Self::sbox_layer(state); + Self::external_matrix(state); + *round_ctr += 1; + } + } + + /// Apply the partial rounds of the permutation + #[inline] + fn partial_rounds(state: &mut [Self; WIDTH], round_ctr: &mut usize) { + let mut constant_counter = HALF_N_FULL_ROUNDS * WIDTH; + for _ in 0..N_PARTIAL_ROUNDS { + unsafe { + state[0] = state[0].add_canonical_u64(ALL_ROUND_CONSTANTS[constant_counter]); + constant_counter += WIDTH; + } + state[0] = Self::sbox_monomial(state[0]); + Self::internal_matrix(state); + } + *round_ctr += N_PARTIAL_ROUNDS; + } + /// Apply the entire Poseidon2 permutation to `input` + /// + /// ```rust + /// use plonky2::field::goldilocks_field::GoldilocksField as F; + /// use plonky2::field::types::Sample; + /// use poseidon2_plonky2::poseidon2_hash::Poseidon2; + /// + /// let output = F::poseidon2(F::rand_array()); + /// ``` + #[inline] + fn poseidon2(input: [Self; WIDTH]) -> [Self; WIDTH] { + let mut state = input; + let mut round_ctr = 0; + + // First external matrix + Self::external_matrix(&mut state); + + Self::full_rounds(&mut state, &mut round_ctr); + Self::partial_rounds(&mut state, &mut round_ctr); + Self::full_rounds(&mut state, &mut round_ctr); + debug_assert_eq!(round_ctr, N_ROUNDS); + + state + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +/// Poseidon2 permutation +pub struct Poseidon2Permutation { + state: [T; WIDTH], +} + +impl AsRef<[T]> for Poseidon2Permutation { + fn as_ref(&self) -> &[T] { + &self.state + } +} + +trait Permuter: Sized { + fn permute(input: [Self; WIDTH]) -> [Self; WIDTH]; +} + +impl Permuter for F { + fn permute(input: [Self; WIDTH]) -> [Self; WIDTH] { + ::poseidon2(input) + } +} + +impl Permuter for Target { + fn permute(_input: [Self; WIDTH]) -> [Self; WIDTH] { + panic!("Call `permute_swapped()` instead of `permute()`"); + } +} + +impl PlonkyPermutation + for Poseidon2Permutation +{ + const RATE: usize = RATE; + + const WIDTH: usize = WIDTH; + + fn new>(elts: I) -> Self { + let mut perm = Self { + state: [T::default(); WIDTH], + }; + perm.set_from_iter(elts, 0); + perm + } + + fn set_elt(&mut self, elt: T, idx: usize) { + self.state[idx] = elt; + } + + fn set_from_slice(&mut self, elts: &[T], start_idx: usize) { + let begin = start_idx; + let end = start_idx + elts.len(); + self.state[begin..end].copy_from_slice(elts); + } + + fn set_from_iter>(&mut self, elts: I, start_idx: usize) { + for (s, e) in self.state[start_idx..].iter_mut().zip(elts) { + *s = e; + } + } + + fn permute(&mut self) { + self.state = T::permute(self.state); + } + + fn squeeze(&self) -> &[T] { + &self.state[..Self::RATE] + } +} + +/// Poseidon2 hash function. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Poseidon2Hash; +impl Hasher for Poseidon2Hash { + const HASH_SIZE: usize = 4 * 8; + type Hash = HashOut; + type Permutation = Poseidon2Permutation; + + fn hash_no_pad(input: &[F]) -> Self::Hash { + hash_n_to_hash_no_pad::(input) + } + + fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { + compress::(left, right) + } +} + +impl AlgebraicHasher for Poseidon2Hash { + fn permute_swapped( + inputs: Self::AlgebraicPermutation, + swap: BoolTarget, + builder: &mut CircuitBuilder, + ) -> Self::AlgebraicPermutation + where + F: RichField + Extendable, + { + let gate_type = Poseidon2Gate::::new(); + let gate = builder.add_gate(gate_type, vec![]); + + let swap_wire = Poseidon2Gate::::WIRE_SWAP; + let swap_wire = Target::wire(gate, swap_wire); + builder.connect(swap.target, swap_wire); + + // Route input wires. + for i in 0..WIDTH { + let in_wire = Poseidon2Gate::::wire_input(i); + let in_wire = Target::wire(gate, in_wire); + builder.connect(inputs.as_ref()[i], in_wire); + } + + // Collect output wires. + Self::AlgebraicPermutation::new( + (0..WIDTH).map(|i| Target::wire(gate, Poseidon2Gate::::wire_output(i))), + ) + } + + type AlgebraicPermutation = Poseidon2Permutation; +} + +#[cfg(test)] +pub(crate) mod test_helpers { + use anyhow::Result; + use log::{info, Level}; + use plonky2::field::extension::Extendable; + use plonky2::field::types::Field; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::{PartialWitness, WitnessWrite}; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; + use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; + use plonky2::plonk::proof::ProofWithPublicInputs; + use plonky2::plonk::prover::prove; + use plonky2::util::timing::TimingTree; + + use super::Poseidon2; + use crate::poseidon2_hash::WIDTH; + + pub(crate) fn check_test_vectors(test_vectors: Vec<([u64; WIDTH], [u64; WIDTH])>) + where + F: Poseidon2, + { + for (input, expected_output) in test_vectors.into_iter() { + let mut state = [F::ZERO; WIDTH]; + for i in 0..WIDTH { + state[i] = F::from_canonical_u64(input[i]); + } + let output = F::poseidon2(state); + for i in 0..WIDTH { + let ex_output = F::from_canonical_u64(expected_output[i]); // Adjust! + assert_eq!(output[i], ex_output); + } + } + } + + pub(crate) fn prove_circuit_with_poseidon_hash< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, + H: Hasher + AlgebraicHasher, + >( + config: CircuitConfig, + num_ops: usize, + _hasher: H, + print_timing: bool, + ) -> Result<(CircuitData, ProofWithPublicInputs)> { + let mut builder = CircuitBuilder::::new(config); + let init_t = builder.add_virtual_public_input(); + let mut res_t = builder.add_virtual_target(); + builder.connect(init_t, res_t); + let hash_targets = (0..WIDTH - 1) + .map(|_| builder.add_virtual_target()) + .collect::>(); + for _ in 0..num_ops { + res_t = builder.mul(res_t, res_t); + let mut to_be_hashed_elements = vec![res_t]; + to_be_hashed_elements.extend_from_slice(hash_targets.as_slice()); + res_t = builder.hash_or_noop::(to_be_hashed_elements).elements[0] + } + let out_t = builder.add_virtual_public_input(); + let is_eq_t = builder.is_equal(out_t, res_t); + builder.assert_one(is_eq_t.target); + + let data = builder.build::(); + + let mut pw = PartialWitness::::new(); + let input = F::rand(); + pw.set_target(init_t, input); + + let input_hash_elements = hash_targets + .iter() + .map(|&hash_t| { + let elem = F::rand(); + pw.set_target(hash_t, elem); + elem + }) + .collect::>(); + + let mut res = input; + for _ in 0..num_ops { + res = res.mul(res); + let mut to_be_hashed_elements = vec![res]; + to_be_hashed_elements.extend_from_slice(input_hash_elements.as_slice()); + res = H::hash_no_pad(to_be_hashed_elements.as_slice()).elements[0] + } + + pw.set_target(out_t, res); + + let proof = if print_timing { + let mut timing = TimingTree::new("prove", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + timing.print(); + let proof_bytes = serde_cbor::to_vec(&proof).unwrap(); + info!("proof size: {}", proof_bytes.len()); + proof + } else { + data.prove(pw)? + }; + + assert_eq!(proof.public_inputs[0], input); + assert_eq!(proof.public_inputs[1], res); + + Ok((data, proof)) + } + + pub(crate) fn recursive_proof< + F: RichField + Poseidon2 + Extendable, + C: GenericConfig, + InnerC: GenericConfig, + const D: usize, + >( + inner_proof: ProofWithPublicInputs, + inner_cd: &CircuitData, + config: &CircuitConfig, + ) -> Result<(CircuitData, ProofWithPublicInputs)> + where + InnerC::Hasher: AlgebraicHasher, + { + let mut builder = CircuitBuilder::::new(config.clone()); + let mut pw = PartialWitness::new(); + let pt = builder.add_virtual_proof_with_pis(&inner_cd.common); + pw.set_proof_with_pis_target(&pt, &inner_proof); + + let inner_data = + builder.add_virtual_verifier_data(inner_cd.common.config.fri_config.cap_height); + pw.set_cap_target( + &inner_data.constants_sigmas_cap, + &inner_cd.verifier_only.constants_sigmas_cap, + ); + pw.set_hash_target( + inner_data.circuit_digest, + inner_cd.verifier_only.circuit_digest, + ); + + for &pi_t in pt.public_inputs.iter() { + let t = builder.add_virtual_public_input(); + builder.connect(pi_t, t); + } + builder.verify_proof::(&pt, &inner_data, &inner_cd.common); + let data = builder.build::(); + + let proof = data.prove(pw)?; + + Ok((data, proof)) + } +} From a58a2bf854009ee3f4bd5231407769cb4d1cb9dc Mon Sep 17 00:00:00 2001 From: Steven Gu Date: Tue, 3 Sep 2024 10:39:56 +0800 Subject: [PATCH 175/175] Fix `Witness` inheritance to `WitnessWrite`. --- starky/src/recursive_verifier.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 83e39398b3..d6c2f9fec8 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -14,7 +14,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; -use plonky2::iop::witness::Witness; +use plonky2::iop::witness::WitnessWrite; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::util::reducing::ReducingFactorTarget; @@ -328,7 +328,7 @@ pub fn set_stark_proof_with_pis_target, W, const D ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, - W: Witness, + W: WitnessWrite, { let StarkProofWithPublicInputs { proof, @@ -357,7 +357,7 @@ pub fn set_stark_proof_target, W, const D: usize>( ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, - W: Witness, + W: WitnessWrite, { witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap); if let (Some(quotient_polys_cap_target), Some(quotient_polys_cap)) =